summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--.gitignore2
-rw-r--r--ChangeLog75
-rw-r--r--Make_global.am2
-rw-r--r--configure.ac96
-rw-r--r--include/libipset/Makefile.am3
-rw-r--r--include/libipset/args.h1
-rw-r--r--include/libipset/data.h6
-rw-r--r--include/libipset/linux_ip_set.h2
-rw-r--r--include/libipset/list_sort.h4
-rw-r--r--include/libipset/parse.h2
-rw-r--r--include/libipset/session.h3
-rw-r--r--include/libipset/xlate.h6
-rw-r--r--kernel/ChangeLog79
-rw-r--r--kernel/include/linux/netfilter/ipset/ip_set.h17
-rw-r--r--kernel/include/linux/netfilter/ipset/ip_set_compat.h.in133
-rw-r--r--kernel/include/uapi/linux/netfilter/ipset/ip_set.h6
-rw-r--r--kernel/net/netfilter/ipset/Kconfig2
-rw-r--r--kernel/net/netfilter/ipset/ip_set_bitmap_gen.h14
-rw-r--r--kernel/net/netfilter/ipset/ip_set_bitmap_ip.c4
-rw-r--r--kernel/net/netfilter/ipset/ip_set_core.c333
-rw-r--r--kernel/net/netfilter/ipset/ip_set_hash_gen.h124
-rw-r--r--kernel/net/netfilter/ipset/ip_set_hash_ip.c40
-rw-r--r--kernel/net/netfilter/ipset/ip_set_hash_ipmark.c17
-rw-r--r--kernel/net/netfilter/ipset/ip_set_hash_ipport.c34
-rw-r--r--kernel/net/netfilter/ipset/ip_set_hash_ipportip.c10
-rw-r--r--kernel/net/netfilter/ipset/ip_set_hash_ipportnet.c10
-rw-r--r--kernel/net/netfilter/ipset/ip_set_hash_net.c10
-rw-r--r--kernel/net/netfilter/ipset/ip_set_hash_netiface.c17
-rw-r--r--kernel/net/netfilter/ipset/ip_set_hash_netnet.c35
-rw-r--r--kernel/net/netfilter/ipset/ip_set_hash_netport.c10
-rw-r--r--kernel/net/netfilter/ipset/ip_set_hash_netportnet.c27
-rw-r--r--kernel/net/netfilter/ipset/ip_set_list_set.c13
-rw-r--r--kernel/net/sched/em_ipset.c7
-rw-r--r--lib/Makefile.am1
-rw-r--r--lib/args.c8
-rw-r--r--lib/data.c12
-rw-r--r--lib/debug.c1
-rw-r--r--lib/errcode.c4
-rw-r--r--lib/ipset.c601
-rw-r--r--lib/ipset_hash_ip.c86
-rw-r--r--lib/ipset_hash_ipport.c108
-rw-r--r--lib/ipset_hash_netnet.c101
-rw-r--r--lib/libipset.map9
-rw-r--r--lib/parse.c119
-rw-r--r--lib/print.c22
-rw-r--r--lib/session.c125
-rw-r--r--src/Makefile.am8
-rw-r--r--src/ipset-translate.891
-rw-r--r--src/ipset.841
-rw-r--r--src/ipset.c9
-rw-r--r--src/ui.c2
-rwxr-xr-xtests/cidr.sh32
-rw-r--r--tests/comment.t2
-rw-r--r--tests/hash:ip,port.t126
-rw-r--r--tests/hash:ip,port.t.list22
-rw-r--r--tests/hash:ip,port.t.list311
-rw-r--r--tests/hash:ip,port.t.list49
-rw-r--r--tests/hash:ip,port.t.list511
-rw-r--r--tests/hash:ip,port.t.list69
-rw-r--r--tests/hash:ip,port.t.list79
-rw-r--r--tests/hash:ip.t82
-rw-r--r--tests/hash:ip.t.list411
-rw-r--r--tests/hash:ip.t.list59
-rw-r--r--tests/hash:ip.t.list69
-rw-r--r--tests/hash:net,iface.t4
-rw-r--r--tests/hash:net,net.t106
-rw-r--r--tests/hash:net,net.t.list311
-rw-r--r--tests/hash:net,net.t.list49
-rw-r--r--tests/hash:net,net.t.list511
-rw-r--r--tests/hash:net,net.t.list69
-rw-r--r--tests/hash:net,net.t.list79
-rw-r--r--tests/hash:net,port,net.t6
-rwxr-xr-xtests/netnetgen.sh2
-rw-r--r--tests/restore.t24
-rwxr-xr-xtests/setlist_resize.sh6
l---------tests/xlate/ipset-translate1
-rwxr-xr-xtests/xlate/runtest.sh35
-rw-r--r--tests/xlate/xlate.t57
-rw-r--r--tests/xlate/xlate.t.nft58
79 files changed, 2775 insertions, 387 deletions
diff --git a/.gitignore b/.gitignore
index 0e8a087..3ce7819 100644
--- a/.gitignore
+++ b/.gitignore
@@ -16,6 +16,8 @@ Makefile.in
*.mod.o.cmd
*.mod.cmd
*.mod
+*.order.cmd
+*.symvers.cmd
.tmp_versions
Module.symvers
modules.order
diff --git a/ChangeLog b/ChangeLog
index 2bd363e..30012d2 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,78 @@
+7.21
+ - The patch "Fix hex literals in json output" broke save mode, restore it
+ - Fix -Werror=format-extra-args warning
+ - Workaround misleading -Wstringop-truncation warning
+
+7.20
+ - Ignore *.order.cmd and *.symvers.cmd files in kernel builds
+ - Bash completion utility updated
+ - Fix json output for -name option (Mark)
+ - Fix hex literals in json output
+ - tests: increase timeout to cope with slow virtual test machine
+
+7.19
+ - build: Fix the double-prefix in pkgconfig (Sam James)
+
+7.18
+ - Add json output to list command (Thomas Oberhammer)
+ - tests: hash:ip,port.t: Replace VRRP by GRE protocol (Phil Sutter)
+ - tests: hash:ip,port.t: 'vrrp' is printed as 'carp' (Phil Sutter)
+ - tests: cidr.sh: Add ipcalc fallback (Phil Sutter)
+ - tests: xlate: Make test input valid (Phil Sutter)
+ - tests: xlate: Test built binary by default (Phil Sutter)
+ - xlate: Drop dead code (Phil Sutter)
+ - xlate: Fix for fd leak in error path (Phil Sutter)
+ - configure.ac: fix bashisms (Sam James)
+ - lib/Makefile.am: fix pkgconfig dir (Sam James)
+
+7.17
+ - Tests: When verifying comments/timeouts, make sure entries don't expire
+ - Tests: Make sure the internal batches add the correct number of elements
+ - Tests: Verify that hash:net,port,net type can handle 0/0 properly
+ - Makefile: Create LZMA-compressed dist-files (Phil Sutter)
+
+7.16
+ - Add new ipset_parse_bitmask() function to the library interface
+ - test: Make sure no more than 64 clashing elements can be added
+ to hash:net,iface sets
+ - netfilter: ipset: add tests for the new bitmask feature (Vishwanath Pai)
+ - netfilter: ipset: Update the man page to include netmask/bitmask options
+ (Vishwanath Pai)
+ - netfilter: ipset: Add bitmask support to hash:netnet (Vishwanath Pai)
+ - netfilter: ipset: Add bitmask support to hash:ipport (Vishwanath Pai)
+ - netfilter: ipset: Add bitmask support to hash:ip (Vishwanath Pai)
+ - netfilter: ipset: Add support for new bitmask parameter (Vishwanath Pai)
+ - ipset-translate: allow invoking with a path name (Quentin Armitage)
+ - Fix IPv6 sets nftables translation (Pablo Neira Ayuso)
+ - Fix typo in ipset-translate man page (Bernhard M. Wiedemann)
+
+7.14
+ - Add missing function to libipset.map and bump library version
+ (reported by Jan Engelhardt)
+
+7.13
+ - When parsing protocols by number, do not check it in /etc/protocols.
+ - Add missing hunk to patch "Allow specifying protocols by number"
+
+7.12
+ - Allow specifying protocols by number (Haw Loeung)
+ - Fix example in ipset.8 manpage discovered by Pablo Neira Ayuso.
+ - tests: add tests ipset to nftables (Pablo Neira Ayuso)
+ - add ipset to nftables translation infrastructure (Pablo Neira Ayuso)
+ - lib: Detach restore routine from parser (Pablo Neira Ayuso)
+ - lib: split parser from command execution (Pablo Neira Ayuso)
+ - Fix patch "Parse port before trying by service name"
+
+7.11
+ - Parse port before trying by service name (Haw Loeung)
+ - Silence unused-but-set-variable warnings (reported by
+ Serhey Popovych)
+ - Handle -Werror=implicit-fallthrough= in debug mode compiling
+ - ipset: fix print format warning (Neutron Soutmun)
+ - Updated utilities
+ - Argument parsing buffer overflow in ipset_parse_argv fixed
+ (reported by Marshall Whittaker)
+ -
7.9
- Fix library versioning (Jan Engelhardt)
diff --git a/Make_global.am b/Make_global.am
index 3ad7f73..9ba6adf 100644
--- a/Make_global.am
+++ b/Make_global.am
@@ -69,7 +69,7 @@
# interface.
# curr:rev:age
-LIBVERSION = 15:0:2
+LIBVERSION = 17:0:4
AM_CPPFLAGS = $(kinclude_CFLAGS) $(all_includes) -I$(top_srcdir)/include
diff --git a/configure.ac b/configure.ac
index 4bc33e6..db9e3f8 100644
--- a/configure.ac
+++ b/configure.ac
@@ -1,18 +1,20 @@
dnl Boilerplate
-AC_INIT([ipset], [7.10], [kadlec@netfilter.org])
+AC_INIT([ipset], [7.21], [kadlec@netfilter.org])
AC_CONFIG_AUX_DIR([build-aux])
AC_CANONICAL_HOST
AC_CONFIG_MACRO_DIR([m4])
AC_CONFIG_HEADER([config.h])
-AM_INIT_AUTOMAKE([foreign subdir-objects tar-pax])
+AM_INIT_AUTOMAKE([foreign subdir-objects tar-pax no-dist-gzip dist-xz])
m4_ifdef([AM_SILENT_RULES], [AM_SILENT_RULES([yes])])
+AC_PROG_LN_S
AC_ENABLE_STATIC
LT_INIT([dlopen])
LT_CONFIG_LTDL_DIR([libltdl])
LTDL_INIT([nonrecursive])
PKG_PROG_PKG_CONFIG
+PKG_INSTALLDIR
dnl Shortcut: Linux supported alone
case "$host" in
@@ -26,7 +28,7 @@ AC_ARG_WITH([kmod],
[Build the kernel module (default: yes)]),
[BUILDKMOD="$withval";],
[BUILDKMOD="yes";])
-AM_CONDITIONAL(WITH_KMOD, test "$BUILDKMOD" == "yes")
+AM_CONDITIONAL(WITH_KMOD, test "$BUILDKMOD" = "yes")
dnl Additional arguments
dnl Kernel build directory or source tree
@@ -75,7 +77,7 @@ if test "x$enable_bashcompl" = "xyes"; then
AC_SUBST(bashcompdir)
fi
-if test "$BUILDKMOD" == "yes"
+if test "$BUILDKMOD" = "yes"
then
dnl Sigh: check kernel version dependencies
if test "$KBUILDDIR" != ""
@@ -203,7 +205,7 @@ AC_CHECK_TYPES([union nf_inet_addr],,,[#include <linux/types.h>
dnl Checks for functions
AC_CHECK_FUNCS(gethostbyname2)
-if test "$BUILDKMOD" == "yes"
+if test "$BUILDKMOD" = "yes"
then
dnl Check kernel incompatibilities... Ugly like hell
@@ -442,6 +444,10 @@ if test -f $ksourcedir/include/linux/mm.h && \
$GREP -q 'kvcalloc' $ksourcedir/include/linux/mm.h; then
AC_MSG_RESULT(yes)
AC_SUBST(HAVE_KVCALLOC, define)
+elif test -f $ksourcedir/include/linux/slab.h && \
+ $GREP -q 'kvcalloc' $ksourcedir/include/linux/slab.h; then
+ AC_MSG_RESULT(yes)
+ AC_SUBST(HAVE_KVCALLOC, define)
else
AC_MSG_RESULT(no)
AC_SUBST(HAVE_KVCALLOC, undef)
@@ -452,6 +458,10 @@ if test -f $ksourcedir/include/linux/mm.h && \
$GREP -q 'kvfree' $ksourcedir/include/linux/mm.h; then
AC_MSG_RESULT(yes)
AC_SUBST(HAVE_KVFREE, define)
+elif test -f $ksourcedir/include/linux/slab.h && \
+ $GREP -q 'kvfree' $ksourcedir/include/linux/slab.h; then
+ AC_MSG_RESULT(yes)
+ AC_SUBST(HAVE_KVFREE, define)
else
AC_MSG_RESULT(no)
AC_SUBST(HAVE_KVFREE, undef)
@@ -715,6 +725,16 @@ else
AC_SUBST(HAVE_TIMER_SETUP, undef)
fi
+AC_MSG_CHECKING([kernel source for timer_shutdown_sync() in timer.h])
+if test -f $ksourcedir/include/linux/timer.h && \
+ $GREP -q ' timer_shutdown_sync' $ksourcedir/include/linux/timer.h; then
+ AC_MSG_RESULT(yes)
+ AC_SUBST(HAVE_TIMER_SHUTDOWN_SYNC, define)
+else
+ AC_MSG_RESULT(no)
+ AC_SUBST(HAVE_TIMER_SHUTDOWN_SYNC, undef)
+fi
+
AC_MSG_CHECKING([kernel source for lockdep_nfnl_is_held() in nfnetlink.h])
if test -f $ksourcedir/include/linux/netfilter/nfnetlink.h && \
$GREP -q ' lockdep_nfnl_is_held' $ksourcedir/include/linux/netfilter/nfnetlink.h; then
@@ -745,6 +765,16 @@ else
AC_SUBST(HAVE_STRSCPY, undef)
fi
+AC_MSG_CHECKING([kernel source for strscpy_pad() in string.h])
+if test -f $ksourcedir/include/linux/timer.h && \
+ $GREP -q ' strscpy_pad' $ksourcedir/include/linux/string.h; then
+ AC_MSG_RESULT(yes)
+ AC_SUBST(HAVE_STRSCPY_PAD, define)
+else
+ AC_MSG_RESULT(no)
+ AC_SUBST(HAVE_STRSCPY, undef)
+fi
+
AC_MSG_CHECKING([kernel source for synchronize_rcu_bh() in rcutiny.h and rcupdate.h])
if test -f $ksourcedir/include/linux/rcupdate.h && \
$GREP -q 'static inline void synchronize_rcu_bh' \
@@ -786,11 +816,65 @@ else
AC_SUBST(HAVE_NLA_POLICY_EXACT_LEN, undef)
fi
-AC_MSG_CHECKING([kernel source for kvzalloc() in mm.h])
+AC_MSG_CHECKING([kernel source for nfnl_msg_put() in nfnetlink.h])
+if test -f $ksourcedir/include/linux/netfilter/nfnetlink.h && \
+ $GREP -q 'nfnl_msg_put' $ksourcedir/include/linux/netfilter/nfnetlink.h; then
+ AC_MSG_RESULT(yes)
+ AC_SUBST(HAVE_NFNL_MSG_PUT, define)
+else
+ AC_MSG_RESULT(no)
+ AC_SUBST(HAVE_NFNL_MSG_PUT, undef)
+fi
+
+AC_MSG_CHECKING([kernel source for struct nfnl_info in nfnl_callback function])
+if test -f $ksourcedir/include/linux/netfilter/nfnetlink.h && \
+ $AWK '/^struct nfnl_callback /,/^}/' $ksourcedir/include/linux/netfilter/nfnetlink.h | $GREP -q 'struct nfnl_info'; then
+ AC_MSG_RESULT(yes)
+ AC_SUBST(HAVE_NFNL_INFO_IN_NFNL_CALLBACK, define)
+else
+ AC_MSG_RESULT(no)
+ AC_SUBST(HAVE_NFNL_INFO_IN_NFNL_CALLBACK, undef)
+fi
+
+AC_MSG_CHECKING([kernel source for enum nfnl_callback_type])
+if test -f $ksourcedir/include/linux/netfilter/nfnetlink.h && \
+ $GREP -q 'enum nfnl_callback_type ' $ksourcedir/include/linux/netfilter/nfnetlink.h; then
+ AC_MSG_RESULT(yes)
+ AC_SUBST(HAVE_NFNL_CALLBACK_TYPE, define)
+else
+ AC_MSG_RESULT(no)
+ AC_SUBST(HAVE_NFNL_CALLBACK_TYPE, undef)
+fi
+
+AC_MSG_CHECKING([kernel source of handling -EAGAIN in nfnetlink_unicast])
+if test -f $ksourcedir/net/netfilter/nfnetlink.c && \
+ $AWK '/nfnetlink_unicast\(/,/^}/' $ksourcedir/net/netfilter/nfnetlink.c | $GREP -q 'err == -EAGAIN'; then
+ AC_MSG_RESULT(yes)
+ AC_SUBST(HAVE_EAGAIN_IN_NFNETLINK_UNICAST, define)
+else
+ AC_MSG_RESULT(no)
+ AC_SUBST(HAVE_EAGAIN_IN_NFNETLINK_UNICAST, undef)
+fi
+
+AC_MSG_CHECKING([kernel source for nlmsg_unicast which returns zero in case of success])
+if test -f $ksourcedir/include/net/netlink.h && \
+ $AWK '/static inline int nlmsg_unicast\(/,/^}/' $ksourcedir/include/net/netlink.h | $GREP -q 'err > 0'; then
+ AC_MSG_RESULT(yes)
+ AC_SUBST(HAVE_NLMSG_UNICAST, define)
+else
+ AC_MSG_RESULT(no)
+ AC_SUBST(HAVE_NLMSG_UNICAST, undef)
+fi
+
+AC_MSG_CHECKING([kernel source for kvzalloc() in mm.h and slab.h])
if test -f $ksourcedir/include/linux/mm.h && \
$GREP -q 'static inline void \*kvzalloc(' $ksourcedir/include/linux/mm.h; then
AC_MSG_RESULT(yes)
AC_SUBST(HAVE_KVZALLOC, define)
+elif test -f $ksourcedir/include/linux/slab.h && \
+ $GREP -q 'kvzalloc' $ksourcedir/include/linux/slab.h; then
+ AC_MSG_RESULT(yes)
+ AC_SUBST(HAVE_KVZALLOC, define)
else
AC_MSG_RESULT(no)
AC_SUBST(HAVE_KVZALLOC, undef)
diff --git a/include/libipset/Makefile.am b/include/libipset/Makefile.am
index c7f7b2b..2c04029 100644
--- a/include/libipset/Makefile.am
+++ b/include/libipset/Makefile.am
@@ -17,6 +17,7 @@ pkginclude_HEADERS = \
transport.h \
types.h \
ipset.h \
- utils.h
+ utils.h \
+ xlate.h
EXTRA_DIST = debug.h icmp.h icmpv6.h
diff --git a/include/libipset/args.h b/include/libipset/args.h
index ef861c1..a549e42 100644
--- a/include/libipset/args.h
+++ b/include/libipset/args.h
@@ -58,6 +58,7 @@ enum ipset_keywords {
IPSET_ARG_SKBQUEUE, /* skbqueue */
IPSET_ARG_BUCKETSIZE, /* bucketsize */
IPSET_ARG_INITVAL, /* initval */
+ IPSET_ARG_BITMASK, /* bitmask */
IPSET_ARG_MAX,
};
diff --git a/include/libipset/data.h b/include/libipset/data.h
index 0e33c67..afaf18c 100644
--- a/include/libipset/data.h
+++ b/include/libipset/data.h
@@ -37,6 +37,7 @@ enum ipset_opt {
IPSET_OPT_RESIZE,
IPSET_OPT_SIZE,
IPSET_OPT_FORCEADD,
+ IPSET_OPT_BITMASK,
/* Create-specific options, filled out by the kernel */
IPSET_OPT_ELEMENTS,
IPSET_OPT_REFERENCES,
@@ -70,7 +71,7 @@ enum ipset_opt {
IPSET_OPT_BUCKETSIZE,
IPSET_OPT_INITVAL,
/* Internal options */
- IPSET_OPT_FLAGS = 48, /* IPSET_FLAG_EXIST| */
+ IPSET_OPT_FLAGS = 49, /* IPSET_FLAG_EXIST| */
IPSET_OPT_CADT_FLAGS, /* IPSET_FLAG_BEFORE| */
IPSET_OPT_ELEM,
IPSET_OPT_TYPE,
@@ -105,7 +106,8 @@ enum ipset_opt {
| IPSET_FLAG(IPSET_OPT_COUNTERS)\
| IPSET_FLAG(IPSET_OPT_CREATE_COMMENT)\
| IPSET_FLAG(IPSET_OPT_FORCEADD)\
- | IPSET_FLAG(IPSET_OPT_SKBINFO))
+ | IPSET_FLAG(IPSET_OPT_SKBINFO)\
+ | IPSET_FLAG(IPSET_OPT_BITMASK))
#define IPSET_ADT_FLAGS \
(IPSET_FLAG(IPSET_OPT_IP) \
diff --git a/include/libipset/linux_ip_set.h b/include/libipset/linux_ip_set.h
index 1852636..4e32a50 100644
--- a/include/libipset/linux_ip_set.h
+++ b/include/libipset/linux_ip_set.h
@@ -89,6 +89,7 @@ enum {
IPSET_ATTR_CADT_LINENO = IPSET_ATTR_LINENO, /* 9 */
IPSET_ATTR_MARK, /* 10 */
IPSET_ATTR_MARKMASK, /* 11 */
+ IPSET_ATTR_BITMASK, /* 12 */
/* Reserve empty slots */
IPSET_ATTR_CADT_MAX = 16,
/* Create-only specific attributes */
@@ -157,6 +158,7 @@ enum ipset_errno {
IPSET_ERR_COMMENT,
IPSET_ERR_INVALID_MARKMASK,
IPSET_ERR_SKBINFO,
+ IPSET_ERR_BITMASK_NETMASK_EXCL,
/* Type specific error codes */
IPSET_ERR_TYPE_SPECIFIC = 4352,
diff --git a/include/libipset/list_sort.h b/include/libipset/list_sort.h
index 70bb02d..d9d7b36 100644
--- a/include/libipset/list_sort.h
+++ b/include/libipset/list_sort.h
@@ -61,7 +61,7 @@ static inline void list_del(struct list_head *entry)
// entry->prev = (void *) 0;
}
-static inline void __list_splice(const struct list_head *list,
+static inline void __list_splice(struct list_head *list,
struct list_head *prev,
struct list_head *next)
{
@@ -75,7 +75,7 @@ static inline void __list_splice(const struct list_head *list,
next->prev = last;
}
-static inline void list_splice(const struct list_head *list,
+static inline void list_splice(struct list_head *list,
struct list_head *head)
{
if (!list_empty(list))
diff --git a/include/libipset/parse.h b/include/libipset/parse.h
index 3fa9129..0123d4b 100644
--- a/include/libipset/parse.h
+++ b/include/libipset/parse.h
@@ -92,6 +92,8 @@ extern int ipset_parse_uint8(struct ipset_session *session,
enum ipset_opt opt, const char *str);
extern int ipset_parse_netmask(struct ipset_session *session,
enum ipset_opt opt, const char *str);
+extern int ipset_parse_bitmask(struct ipset_session *session,
+ enum ipset_opt opt, const char *str);
extern int ipset_parse_flag(struct ipset_session *session,
enum ipset_opt opt, const char *str);
extern int ipset_parse_typename(struct ipset_session *session,
diff --git a/include/libipset/session.h b/include/libipset/session.h
index 5f18a6e..365e17e 100644
--- a/include/libipset/session.h
+++ b/include/libipset/session.h
@@ -84,6 +84,8 @@ enum ipset_envopt {
IPSET_ENV_LIST_SETNAME = (1 << IPSET_ENV_BIT_LIST_SETNAME),
IPSET_ENV_BIT_LIST_HEADER = 5,
IPSET_ENV_LIST_HEADER = (1 << IPSET_ENV_BIT_LIST_HEADER),
+ IPSET_ENV_BIT_QUOTED = 6,
+ IPSET_ENV_QUOTED = (1 << IPSET_ENV_BIT_QUOTED),
};
extern bool ipset_envopt_test(struct ipset_session *session,
@@ -98,6 +100,7 @@ enum ipset_output_mode {
IPSET_LIST_PLAIN,
IPSET_LIST_SAVE,
IPSET_LIST_XML,
+ IPSET_LIST_JSON,
};
extern int ipset_session_output(struct ipset_session *session,
diff --git a/include/libipset/xlate.h b/include/libipset/xlate.h
new file mode 100644
index 0000000..6569768
--- /dev/null
+++ b/include/libipset/xlate.h
@@ -0,0 +1,6 @@
+#ifndef LIBIPSET_XLATE_H
+#define LIBIPSET_XLATE_H
+
+int ipset_xlate_argv(struct ipset *ipset, int argc, char *argv[]);
+
+#endif
diff --git a/kernel/ChangeLog b/kernel/ChangeLog
index ec2134f..1b05fbd 100644
--- a/kernel/ChangeLog
+++ b/kernel/ChangeLog
@@ -1,3 +1,82 @@
+7.21
+ - netfilter: ipset: Suppress false sparse warnings
+ - tests: Verify module unload when sets with timeout were just destroyed
+ - netfilter: ipset: remove set destroy at ip_set module removal
+ - netfilter: ipset: Cleanup the code of destroy operation and explain
+ the two stages in comments
+ - netfilter: ipset: Missing gc cancellations fixed
+
+7.20
+ - treewide: Convert del_timer*() to timer_shutdown*() (Steven Rostedt)
+ - Use timer_shutdown_sync() when available, instead of del_timer_sync()
+ - netfilter: ipset: fix race condition between swap/destroy and kernel
+ side add/del/test v4
+ - netfilter: ipset: fix race condition between swap/destroy and kernel
+ side add/del/test v3
+ - netfilter: ipset: fix race condition between swap/destroy and kernel
+ side add/del/test v2
+ - netfilter: ipset: fix race condition between swap/destroy and kernel
+ side add/del/test
+
+7.18
+ - netfilter: ipset: Fix race between IPSET_CMD_CREATE and IPSET_CMD_SWAP
+ (reported by Kyle Zeng)
+ - netfilter: ipset: add the missing IP_SET_HASH_WITH_NET0 macro for
+ ip_set_hash_netportnet.c (Kyle Zeng)
+ - compatibility: handle strscpy_pad()
+ - netfilter: ipset: refactor deprecated strncpy (Justin Stitt)
+ - netfilter: ipset: remove rcu_read_lock_bh pair from ip_set_test
+ (Florian Westphal)
+ - netfilter: ipset: Replace strlcpy with strscpy (Azeem Shaikh)
+ - netfilter: ipset: Add schedule point in call_ad(). (Kuniyuki Iwashima)
+ - net: Kconfig: fix spellos (Randy Dunlap)
+ - netfilter: ipset: Fix overflow before widen in the bitmap_ip_create()
+ function. (Gavrilov Ilia)
+
+7.17
+ - netfilter: ipset: Rework long task execution when adding/deleting entries
+ - netfilter: ipset: fix hash:net,port,net hang with /0 subnet
+
+7.16
+ - netfilter: ipset: restore allowing 64 clashing elements in hash:net,iface
+ - Fix all debug mode warnings
+ - netfilter: ipset: Add support for new bitmask parameter (Vishwanath Pai)
+ - netfilter: ipset: regression in ip_set_hash_ip.c (Vishwanath Pai)
+ - netfilter: move from strlcpy with unused retval to strscpy
+ (Wolfram Sang)
+ - compatibility: handle unsafe_memcpy()
+ - netlink: Bounds-check struct nlmsgerr creation (Kees Cook)
+ - compatibility: move to skb_protocol in the code from tc_skb_protocol
+ - Compatibility: check kvcalloc, kvfree, kvzalloc in slab.h too
+ - sched: consistently handle layer3 header accesses in the presence
+ of VLANs (Toke Høiland-Jørgensen)
+ - treewide: Replace GPLv2 boilerplate/reference with SPDX
+ - rule 500 (Thomas Gleixner)
+ - headers: Remove some left-over license text in
+ include/uapi/linux/netfilter/ (Christophe JAILLET)
+ - netfilter: ipset: enforce documented limit to prevent allocating
+ huge memory
+ - netfilter: ipset: Fix oversized kvmalloc() calls
+
+7.15
+ - netfilter: ipset: Fix maximal range check in hash_ipportnet4_uadt()
+ (Nathan Chancellor)
+
+7.14
+ - 64bit division isn't allowed on 32bit, replace it with shift
+
+7.13
+ - Limit the maximal range of consecutive elements to add/delete fix
+
+7.12
+ - Limit the maximal range of consecutive elements to add/delete
+ - Backport "netfilter: use nfnetlink_unicast()"
+ - Backport "netfilter: nfnetlink: consolidate callback type"
+ - Backport "netfilter: nfnetlink: add struct nfnl_info and
+ pass it to callbacks"
+ - Backport "netfilter: add helper function to set up the
+ nfnetlink header and use it"
+
7.10
- Fix patch "Handle false warning from -Wstringop-overflow"
- Backward compatibility: handle renaming nla_strlcpy to nla_strscpy
diff --git a/kernel/include/linux/netfilter/ipset/ip_set.h b/kernel/include/linux/netfilter/ipset/ip_set.h
index 554f90f..7691b7a 100644
--- a/kernel/include/linux/netfilter/ipset/ip_set.h
+++ b/kernel/include/linux/netfilter/ipset/ip_set.h
@@ -189,6 +189,8 @@ struct ip_set_type_variant {
/* Return true if "b" set is the same as "a"
* according to the create set parameters */
bool (*same_set)(const struct ip_set *a, const struct ip_set *b);
+ /* Cancel ongoing garbage collectors before destroying the set*/
+ void (*cancel_gc)(struct ip_set *set);
/* Region-locking is used */
bool region_lock;
};
@@ -199,6 +201,9 @@ struct ip_set_region {
u32 elements; /* Number of elements vs timeout */
};
+/* Max range where every element is added/deleted in one step */
+#define IPSET_MAX_RANGE (1<<14)
+
/* The max revision number supported by any set type + 1 */
#define IPSET_REVISION_MAX 9
@@ -242,6 +247,8 @@ extern void ip_set_type_unregister(struct ip_set_type *set_type);
/* A generic IP set */
struct ip_set {
+ /* For call_cru in destroy */
+ struct rcu_head rcu;
/* The name of the set */
char name[IPSET_MAXNAMELEN];
/* Lock protecting the set data */
@@ -525,6 +532,16 @@ ip_set_init_skbinfo(struct ip_set_skbinfo *skbinfo,
*skbinfo = ext->skbinfo;
}
+static inline void
+nf_inet_addr_mask_inplace(union nf_inet_addr *a1,
+ const union nf_inet_addr *mask)
+{
+ a1->all[0] &= mask->all[0];
+ a1->all[1] &= mask->all[1];
+ a1->all[2] &= mask->all[2];
+ a1->all[3] &= mask->all[3];
+}
+
#define IP_SET_INIT_KEXT(skb, opt, set) \
{ .bytes = (skb)->len, .packets = 1, .target = true,\
.timeout = ip_set_adt_opt_timeout(opt, set) }
diff --git a/kernel/include/linux/netfilter/ipset/ip_set_compat.h.in b/kernel/include/linux/netfilter/ipset/ip_set_compat.h.in
index 96a4cf4..5746f39 100644
--- a/kernel/include/linux/netfilter/ipset/ip_set_compat.h.in
+++ b/kernel/include/linux/netfilter/ipset/ip_set_compat.h.in
@@ -51,7 +51,9 @@
#@HAVE_PASSING_EXTENDED_ACK_TO_CALLBACKS@ HAVE_PASSING_EXTENDED_ACK_TO_CALLBACKS
#@HAVE_TYPEDEF_SCTP_SCTPHDR_T@ HAVE_TYPEDEF_SCTP_SCTPHDR_T
#@HAVE_TIMER_SETUP@ HAVE_TIMER_SETUP
+#@HAVE_TIMER_SHUTDOWN_SYNC@ HAVE_TIMER_SHUTDOWN_SYNC
#@HAVE_STRSCPY@ HAVE_STRSCPY
+#@HAVE_STRSCPY_PAD@ HAVE_STRSCPY_PAD
#@HAVE_SYNCHRONIZE_RCU_BH@ HAVE_SYNCHRONIZE_RCU_BH
#@HAVE_LOCKDEP_NFNL_IS_HELD@ HAVE_LOCKDEP_NFNL_IS_HELD
#@HAVE_COND_RESCHED_RCU@ HAVE_COND_RESCHED_RCU
@@ -62,6 +64,11 @@
#@HAVE_KVZALLOC@ HAVE_KVZALLOC
#@HAVE_GFP_KERNEL_ACCOUNT@ HAVE_GFP_KERNEL_ACCOUNT
#@HAVE_NLA_STRSCPY@ HAVE_NLA_STRSCPY
+#@HAVE_NFNL_MSG_PUT@ HAVE_NFNL_MSG_PUT
+#@HAVE_NFNL_INFO_IN_NFNL_CALLBACK@ HAVE_NFNL_INFO_IN_NFNL_CALLBACK
+#@HAVE_NFNL_CALLBACK_TYPE@ HAVE_NFNL_CALLBACK_TYPE
+#@HAVE_EAGAIN_IN_NFNETLINK_UNICAST@ HAVE_EAGAIN_IN_NFNETLINK_UNICAST
+#@HAVE_NLMSG_UNICAST@ HAVE_NLMSG_UNICAST
#ifdef HAVE_EXPORT_SYMBOL_GPL_IN_MODULE_H
#include <linux/module.h>
@@ -348,18 +355,44 @@ static inline int nla_put_in6_addr(struct sk_buff *skb, int attrtype,
}
#endif
-#ifdef HAVE_PASSING_EXTENDED_ACK_TO_CALLBACKS
-#define IPSET_CBFN(fn, net, nl, skb, nlh, cda, e) fn(net, nl, skb, nlh, cda, e)
-#define IPSET_CBFN_AD(fn, net, nl, skb, ad, nlh, cda, e) fn(net, nl, skb, ad, nlh, cda, e)
-#define IPSET_SOCK_NET(net, ctnl) net
+#ifdef HAVE_NFNL_INFO_IN_NFNL_CALLBACK
+#define IPSET_CBFN(fn, net, nl, skb, nlh, cda, e, i) fn(skb, i, cda)
+#define IPSET_CBFN_AD(fn, net, nl, skb, ad, nlh, cda, e, i) fn(net, nl, skb, ad, nlh, cda, i)
+#define IPSET_SOCK_NET(n, ctnl, i) (i)->net
+#define INFO_NLH(i, n) (i)->nlh
+#define INFO_NET(i, n) (i)->net
+#define INFO_SK(i, n) (i)->sk
+#define CALL_AD(net, ctnl, skb, set, tb, adt, flags, l) call_ad(net, ctnl, skb, set, tb, adt, flags, l)
+#elif defined(HAVE_PASSING_EXTENDED_ACK_TO_CALLBACKS)
+#define IPSET_CBFN(fn, net, nl, skb, nlh, cda, e, i) fn(net, nl, skb, nlh, cda, e)
+#define IPSET_CBFN_AD(fn, net, nl, skb, ad, nlh, cda, e, i) fn(net, nl, skb, ad, nlh, cda, e)
+#define IPSET_SOCK_NET(net, ctnl, i) net
+#define INFO_NLH(i, n) n
+#define INFO_NET(i, n) n
+#define INFO_SK(i, n) n
+#define CALL_AD(net, ctnl, skb, set, tb, adt, flags, l) call_ad(net, ctnl, skb, set, tb, adt, flags, l)
#elif defined(HAVE_NET_IN_NFNL_CALLBACK_FN)
-#define IPSET_CBFN(fn, net, nl, skb, nlh, cda, e) fn(net, nl, skb, nlh, cda)
-#define IPSET_CBFN_AD(fn, net, nl, skb, ad, nlh, cda, e) fn(net, nl, skb, ad, nlh, cda)
-#define IPSET_SOCK_NET(net, ctnl) net
+#define IPSET_CBFN(fn, net, nl, skb, nlh, cda, e, i) fn(net, nl, skb, nlh, cda)
+#define IPSET_CBFN_AD(fn, net, nl, skb, ad, nlh, cda, e, i) fn(net, nl, skb, ad, nlh, cda)
+#define IPSET_SOCK_NET(net, ctnl, i) net
+#define INFO_NLH(i, n) n
+#define INFO_NET(i, n) n
+#define INFO_SK(i, n) n
+#define CALL_AD(net, ctnl, skb, set, tb, adt, flags, l) call_ad(net, ctnl, skb, set, tb, adt, flags, l)
#else
-#define IPSET_CBFN(fn, net, nl, skb, nlh, cda, e) fn(nl, skb, nlh, cda)
-#define IPSET_CBFN_AD(fn, net, nl, skb, ad, nlh, cda, e) fn(nl, skb, ad, nlh, cda)
-#define IPSET_SOCK_NET(net, ctnl) sock_net(ctnl)
+#define IPSET_CBFN(fn, net, nl, skb, nlh, cda, e, i) fn(nl, skb, nlh, cda)
+#define IPSET_CBFN_AD(fn, net, nl, skb, ad, nlh, cda, e, i) fn(nl, skb, ad, nlh, cda)
+#define IPSET_SOCK_NET(net, ctnl, i) sock_net(ctnl)
+#define INFO_NLH(i, n) n
+#define INFO_NET(i, n) n
+#define INFO_SK(i, n) n
+#define CALL_AD(net, ctnl, skb, set, tb, adt, flags, l) call_ad(ctnl, skb, set, tb, adt, flags, l)
+#endif
+
+#ifdef HAVE_NFNL_CALLBACK_TYPE
+#define SET_NFNL_CALLBACK_TYPE(t) .type = t,
+#else
+#define SET_NFNL_CALLBACK_TYPE(t)
#endif
#ifndef HAVE_TC_SKB_PROTOCOL
@@ -375,11 +408,9 @@ static inline int nla_put_in6_addr(struct sk_buff *skb, int attrtype,
#define skb_vlan_tag_present vlan_tx_tag_present
#endif
-static inline __be16 tc_skb_protocol(const struct sk_buff *skb)
+#ifndef HAVE_SKB_PROTOCOL
+static inline __be16 skb_protocol(const struct sk_buff *skb, bool skip_vlan)
{
-#ifdef HAVE_SKB_PROTOCOL
- return skb_protocol(skb, true);
-#else
if (skb_vlan_tag_present(skb))
#ifdef HAVE_VLAN_PROTO_IN_SK_BUFF
return skb->vlan_proto;
@@ -387,9 +418,9 @@ static inline __be16 tc_skb_protocol(const struct sk_buff *skb)
return htons(ETH_P_8021Q);
#endif
return skb->protocol;
-#endif
}
#endif
+#endif
#ifdef HAVE_XT_NET
#define IPSET_DEV_NET(par) xt_net(par)
@@ -406,6 +437,36 @@ static inline u16 nfnl_msg_type(u8 subsys, u8 msg_type)
}
#endif
+#ifndef HAVE_NFNL_MSG_PUT
+#include <linux/netfilter/nfnetlink.h>
+static inline void nfnl_fill_hdr(struct nlmsghdr *nlh, u8 family, u8 version,
+ __be16 res_id)
+{
+ struct nfgenmsg *nfmsg;
+
+ nfmsg = nlmsg_data(nlh);
+ nfmsg->nfgen_family = family;
+ nfmsg->version = version;
+ nfmsg->res_id = res_id;
+}
+
+static inline struct nlmsghdr *nfnl_msg_put(struct sk_buff *skb, u32 portid,
+ u32 seq, int type, int flags,
+ u8 family, u8 version,
+ __be16 res_id)
+{
+ struct nlmsghdr *nlh;
+
+ nlh = nlmsg_put(skb, portid, seq, type, sizeof(struct nfgenmsg), flags);
+ if (!nlh)
+ return NULL;
+
+ nfnl_fill_hdr(nlh, family, version, res_id);
+
+ return nlh;
+}
+#endif
+
#ifdef HAVE_NETLINK_EXTENDED_ACK
#define NETLINK_ACK(in_skb, nlh, err, extack) netlink_ack(in_skb, nlh, err, extack)
#else
@@ -446,6 +507,10 @@ static inline u16 nfnl_msg_type(u8 subsys, u8 msg_type)
struct type *var = set->data
#endif
+#ifndef HAVE_TIMER_SHUTDOWN_SYNC
+#define timer_shutdown_sync(timer) del_timer_sync(timer)
+#endif
+
#ifndef HAVE_STRSCPY
static inline ssize_t strscpy(char * dest, const char * src, size_t count)
{
@@ -455,10 +520,42 @@ static inline ssize_t strscpy(char * dest, const char * src, size_t count)
}
#endif
+#ifndef HAVE_STRSCPY_PAD
+static inline ssize_t strscpy_pad(char *dest, const char *src, size_t count)
+{
+ ssize_t written;
+
+ written = strscpy(dest, src, count);
+ if (written < 0 || written == count - 1)
+ return written;
+
+ memset(dest + written + 1, 0, count - written - 1);
+
+ return written;
+}
+#endif
+
#ifndef HAVE_NLA_STRSCPY
#define nla_strscpy nla_strlcpy
#endif
+#if !defined(HAVE_EAGAIN_IN_NFNETLINK_UNICAST) || !defined(HAVE_NLMSG_UNICAST)
+#define NFNETLINK_UNICAST(cntl, skb, net, portid) ipset_nfnetlink_unicast(cntl, skb, portid)
+static inline int ipset_nfnetlink_unicast(struct sock *ctnl, struct sk_buff *skb, u32 portid)
+{
+ int err = netlink_unicast(ctnl, skb, portid, MSG_DONTWAIT);
+
+ if (err > 0)
+ err = 0;
+ if (err == -EAGAIN)
+ err = -ENOBUFS;
+
+ return err;
+}
+#else
+#define NFNETLINK_UNICAST(cntl, skb, net, portid) nfnetlink_unicast(skb, net, portid)
+#endif
+
#ifndef smp_mb__before_atomic
#define smp_mb__before_atomic() smp_mb()
#define smp_mb__after_atomic() smp_mb()
@@ -529,5 +626,11 @@ static inline void *kvzalloc(size_t size, gfp_t flags)
return members;
}
#endif
+
+#ifndef unsafe_memcpy
+#define unsafe_memcpy(dst, src, bytes, justification) \
+ memcpy(dst, src, bytes)
+#endif
+
#endif /* IP_SET_COMPAT_HEADERS */
#endif /* __IP_SET_COMPAT_H */
diff --git a/kernel/include/uapi/linux/netfilter/ipset/ip_set.h b/kernel/include/uapi/linux/netfilter/ipset/ip_set.h
index 8a495aa..b81f1ae 100644
--- a/kernel/include/uapi/linux/netfilter/ipset/ip_set.h
+++ b/kernel/include/uapi/linux/netfilter/ipset/ip_set.h
@@ -3,10 +3,6 @@
* Patrick Schaaf <bof@bof.de>
* Martin Josefsson <gandalf@wlug.westbo.se>
* Copyright (C) 2003-2011 Jozsef Kadlecsik <kadlec@netfilter.org>
- *
- * 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 _UAPI_IP_SET_H
#define _UAPI_IP_SET_H
@@ -89,6 +85,7 @@ enum {
IPSET_ATTR_CADT_LINENO = IPSET_ATTR_LINENO, /* 9 */
IPSET_ATTR_MARK, /* 10 */
IPSET_ATTR_MARKMASK, /* 11 */
+ IPSET_ATTR_BITMASK, /* 12 */
/* Reserve empty slots */
IPSET_ATTR_CADT_MAX = 16,
/* Create-only specific attributes */
@@ -157,6 +154,7 @@ enum ipset_errno {
IPSET_ERR_COMMENT,
IPSET_ERR_INVALID_MARKMASK,
IPSET_ERR_SKBINFO,
+ IPSET_ERR_BITMASK_NETMASK_EXCL,
/* Type specific error codes */
IPSET_ERR_TYPE_SPECIFIC = 4352,
diff --git a/kernel/net/netfilter/ipset/Kconfig b/kernel/net/netfilter/ipset/Kconfig
index 861659f..8772af5 100644
--- a/kernel/net/netfilter/ipset/Kconfig
+++ b/kernel/net/netfilter/ipset/Kconfig
@@ -29,7 +29,7 @@ config IP_SET_BITMAP_IP
depends on IP_SET
help
This option adds the bitmap:ip set type support, by which one
- can store IPv4 addresses (or network addresse) from a range.
+ can store IPv4 addresses (or network addresses) from a range.
To compile it as a module, choose M here. If unsure, say N.
diff --git a/kernel/net/netfilter/ipset/ip_set_bitmap_gen.h b/kernel/net/netfilter/ipset/ip_set_bitmap_gen.h
index 0479750..3245b6b 100644
--- a/kernel/net/netfilter/ipset/ip_set_bitmap_gen.h
+++ b/kernel/net/netfilter/ipset/ip_set_bitmap_gen.h
@@ -29,6 +29,7 @@
#define mtype_del IPSET_TOKEN(MTYPE, _del)
#define mtype_list IPSET_TOKEN(MTYPE, _list)
#define mtype_gc IPSET_TOKEN(MTYPE, _gc)
+#define mtype_cancel_gc IPSET_TOKEN(MTYPE, _cancel_gc)
#define mtype MTYPE
#define get_ext(set, map, id) ((map)->extensions + ((set)->dsize * (id)))
@@ -58,9 +59,6 @@ mtype_destroy(struct ip_set *set)
{
struct mtype *map = set->data;
- if (SET_WITH_TIMEOUT(set))
- del_timer_sync(&map->gc);
-
if (set->dsize && set->extensions & IPSET_EXT_DESTROY)
mtype_ext_cleanup(set);
ip_set_free(map->members);
@@ -290,6 +288,15 @@ mtype_gc(GC_ARG)
add_timer(&map->gc);
}
+static void
+mtype_cancel_gc(struct ip_set *set)
+{
+ struct mtype *map = set->data;
+
+ if (SET_WITH_TIMEOUT(set))
+ del_timer_sync(&map->gc);
+}
+
static const struct ip_set_type_variant mtype = {
.kadt = mtype_kadt,
.uadt = mtype_uadt,
@@ -303,6 +310,7 @@ static const struct ip_set_type_variant mtype = {
.head = mtype_head,
.list = mtype_list,
.same_set = mtype_same_set,
+ .cancel_gc = mtype_cancel_gc,
};
#endif /* __IP_SET_BITMAP_IP_GEN_H */
diff --git a/kernel/net/netfilter/ipset/ip_set_bitmap_ip.c b/kernel/net/netfilter/ipset/ip_set_bitmap_ip.c
index c488663..f37169c 100644
--- a/kernel/net/netfilter/ipset/ip_set_bitmap_ip.c
+++ b/kernel/net/netfilter/ipset/ip_set_bitmap_ip.c
@@ -312,8 +312,8 @@ bitmap_ip_create(struct net *net, struct ip_set *set, struct nlattr *tb[],
return -IPSET_ERR_BITMAP_RANGE;
pr_debug("mask_bits %u, netmask %u\n", mask_bits, netmask);
- hosts = 2 << (32 - netmask - 1);
- elements = 2 << (netmask - mask_bits - 1);
+ hosts = 2U << (32 - netmask - 1);
+ elements = 2UL << (netmask - mask_bits - 1);
}
if (elements > IPSET_BITMAP_MAX_RANGE + 1)
return -IPSET_ERR_BITMAP_RANGE_SIZE;
diff --git a/kernel/net/netfilter/ipset/ip_set_core.c b/kernel/net/netfilter/ipset/ip_set_core.c
index 63a7955..c31dbc3 100644
--- a/kernel/net/netfilter/ipset/ip_set_core.c
+++ b/kernel/net/netfilter/ipset/ip_set_core.c
@@ -30,7 +30,6 @@ static DEFINE_RWLOCK(ip_set_ref_lock); /* protects the set refs */
struct ip_set_net {
struct ip_set * __rcu *ip_set_list; /* all individual sets */
ip_set_id_t ip_set_max; /* max number of sets */
- bool is_deleted; /* deleted by ip_set_net_exit */
bool is_destroyed; /* all sets are destroyed */
};
@@ -62,6 +61,8 @@ MODULE_ALIAS_NFNL_SUBSYS(NFNL_SUBSYS_IPSET);
ip_set_dereference((inst)->ip_set_list)[id]
#define ip_set_ref_netlink(inst,id) \
rcu_dereference_raw((inst)->ip_set_list)[id]
+#define ip_set_dereference_nfnl(p) \
+ rcu_dereference_check(p, lockdep_nfnl_is_held(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
@@ -354,7 +355,7 @@ ip_set_init_comment(struct ip_set *set, struct ip_set_comment *comment,
c = kmalloc(sizeof(*c) + len + 1, GFP_ATOMIC);
if (unlikely(!c))
return;
- strlcpy(c->str, ext->comment, len + 1);
+ strscpy(c->str, ext->comment, len + 1);
set->ext_size += sizeof(*c) + strlen(c->str) + 1;
rcu_assign_pointer(comment->c, c);
}
@@ -684,6 +685,14 @@ __ip_set_put(struct ip_set *set)
* a separate reference counter
*/
static void
+__ip_set_get_netlink(struct ip_set *set)
+{
+ write_lock_bh(&ip_set_ref_lock);
+ set->ref_netlink++;
+ write_unlock_bh(&ip_set_ref_lock);
+}
+
+static void
__ip_set_put_netlink(struct ip_set *set)
{
write_lock_bh(&ip_set_ref_lock);
@@ -701,15 +710,10 @@ __ip_set_put_netlink(struct ip_set *set)
static struct ip_set *
ip_set_rcu_get(struct net *net, ip_set_id_t index)
{
- struct ip_set *set;
struct ip_set_net *inst = ip_set_pernet(net);
- rcu_read_lock();
- /* ip_set_list itself needs to be protected */
- set = rcu_dereference(inst->ip_set_list)[index];
- rcu_read_unlock();
-
- return set;
+ /* ip_set_list and the set pointer need to be protected */
+ return ip_set_dereference_nfnl(inst->ip_set_list)[index];
}
static inline void
@@ -740,9 +744,7 @@ ip_set_test(ip_set_id_t index, const struct sk_buff *skb,
!(opt->family == set->family || set->family == NFPROTO_UNSPEC))
return 0;
- rcu_read_lock_bh();
ret = set->variant->kadt(set, skb, par, IPSET_TEST, opt);
- rcu_read_unlock_bh();
if (ret == -EAGAIN) {
/* Type requests element to be completed */
@@ -875,7 +877,7 @@ ip_set_name_byindex(struct net *net, ip_set_id_t index, char *name)
BUG_ON(!set);
read_lock_bh(&ip_set_ref_lock);
- strncpy(name, set->name, IPSET_MAXNAMELEN);
+ strscpy_pad(name, set->name, IPSET_MAXNAMELEN);
read_unlock_bh(&ip_set_ref_lock);
}
EXPORT_SYMBOL_GPL(ip_set_name_byindex);
@@ -923,11 +925,9 @@ ip_set_nfnl_put(struct net *net, ip_set_id_t index)
struct ip_set_net *inst = ip_set_pernet(net);
nfnl_lock(NFNL_SUBSYS_IPSET);
- if (!inst->is_deleted) { /* already deleted from ip_set_net_exit() */
- set = ip_set(inst, index);
- if (set)
- __ip_set_put(set);
- }
+ set = ip_set(inst, index);
+ if (set)
+ __ip_set_put(set);
nfnl_unlock(NFNL_SUBSYS_IPSET);
}
EXPORT_SYMBOL_GPL(ip_set_nfnl_put);
@@ -964,20 +964,9 @@ static struct nlmsghdr *
start_msg(struct sk_buff *skb, u32 portid, u32 seq, unsigned int flags,
enum ipset_cmd cmd)
{
- struct nlmsghdr *nlh;
- struct nfgenmsg *nfmsg;
-
- nlh = nlmsg_put(skb, portid, seq, nfnl_msg_type(NFNL_SUBSYS_IPSET, cmd),
- sizeof(*nfmsg), flags);
- if (!nlh)
- return NULL;
-
- nfmsg = nlmsg_data(nlh);
- nfmsg->nfgen_family = NFPROTO_IPV4;
- nfmsg->version = NFNETLINK_V0;
- nfmsg->res_id = 0;
-
- return nlh;
+ return nfnl_msg_put(skb, portid, seq,
+ nfnl_msg_type(NFNL_SUBSYS_IPSET, cmd), flags,
+ NFPROTO_IPV4, NFNETLINK_V0, 0);
}
/* Create a set */
@@ -1047,7 +1036,8 @@ static int
IPSET_CBFN(ip_set_none, struct net *net, struct sock *ctnl,
struct sk_buff *skb, const struct nlmsghdr *nlh,
const struct nlattr * const attr[],
- struct netlink_ext_ack *extack)
+ struct netlink_ext_ack *extack,
+ const struct nfnl_info *info)
{
return -EOPNOTSUPP;
}
@@ -1056,16 +1046,17 @@ static int
IPSET_CBFN(ip_set_create, struct net *n, struct sock *ctnl,
struct sk_buff *skb, const struct nlmsghdr *nlh,
const struct nlattr * const attr[],
- struct netlink_ext_ack *extack)
+ struct netlink_ext_ack *extack,
+ const struct nfnl_info *info)
{
- struct net *net = IPSET_SOCK_NET(n, ctnl);
+ struct net *net = IPSET_SOCK_NET(n, ctnl, info);
struct ip_set_net *inst = ip_set_pernet(net);
struct ip_set *set, *clash = NULL;
ip_set_id_t index = IPSET_INVALID_ID;
struct nlattr *tb[IPSET_ATTR_CREATE_MAX + 1] = {};
const char *name, *typename;
u8 family, revision;
- u32 flags = flag_exist(nlh);
+ u32 flags = flag_exist(INFO_NLH(info, nlh));
int ret = 0;
if (unlikely(protocol_min_failed(attr) ||
@@ -1091,7 +1082,7 @@ IPSET_CBFN(ip_set_create, struct net *n, struct sock *ctnl,
if (!set)
return -ENOMEM;
spin_lock_init(&set->lock);
- strlcpy(set->name, name, IPSET_MAXNAMELEN);
+ strscpy(set->name, name, IPSET_MAXNAMELEN);
set->family = family;
set->revision = revision;
@@ -1116,7 +1107,7 @@ IPSET_CBFN(ip_set_create, struct net *n, struct sock *ctnl,
/* Set create flags depending on the type revision */
set->flags |= set->type->create_flags[revision];
- ret = set->type->create(net, set, tb, flags);
+ ret = set->type->create(INFO_NET(info, net), set, tb, flags);
if (ret != 0)
goto put_out;
@@ -1170,6 +1161,7 @@ IPSET_CBFN(ip_set_create, struct net *n, struct sock *ctnl,
return ret;
cleanup:
+ set->variant->cancel_gc(set);
set->variant->destroy(set);
put_out:
module_put(set->type->me);
@@ -1187,24 +1179,58 @@ ip_set_setname_policy[IPSET_ATTR_CMD_MAX + 1] = {
.len = IPSET_MAXNAMELEN - 1 },
};
+/* Destroying a set is split into two stages when a DESTROY command issued:
+ * - Cancel garbage collectors and decrement the module reference counter:
+ * - Cancelling may wait and we are allowed to do it at this stage.
+ * - Module remove is protected by rcu_barrier() which waits for
+ * the second stage to be finished.
+ * - In order to prevent the race between kernel side add/del/test element
+ * operations and destroy, the destroying of the set data areas are
+ * performed via a call_rcu() call.
+ */
+
+/* Call set variant specific destroy function and reclaim the set data. */
static void
-ip_set_destroy_set(struct ip_set *set)
+ip_set_destroy_set_variant(struct ip_set *set)
{
- pr_debug("set: %s\n", set->name);
-
/* Must call it without holding any lock */
set->variant->destroy(set);
- module_put(set->type->me);
kfree(set);
}
+static void
+ip_set_destroy_set_variant_rcu(struct rcu_head *head)
+{
+ struct ip_set *set = container_of(head, struct ip_set, rcu);
+
+ ip_set_destroy_set_variant(set);
+}
+
+/* Cancel the garbage collectors and decrement module references */
+static void
+ip_set_destroy_cancel_gc(struct ip_set *set)
+{
+ set->variant->cancel_gc(set);
+ module_put(set->type->me);
+}
+
+/* Use when we may wait for the complete destroy to be finished.
+ */
+static void
+ip_set_destroy_set(struct ip_set *set)
+{
+ ip_set_destroy_cancel_gc(set);
+ ip_set_destroy_set_variant(set);
+}
+
static int
IPSET_CBFN(ip_set_destroy, struct net *net, struct sock *ctnl,
struct sk_buff *skb, const struct nlmsghdr *nlh,
const struct nlattr * const attr[],
- struct netlink_ext_ack *extack)
+ struct netlink_ext_ack *extack,
+ const struct nfnl_info *info)
{
- struct ip_set_net *inst = ip_set_pernet(IPSET_SOCK_NET(net, ctnl));
+ struct ip_set_net *inst = ip_set_pernet(IPSET_SOCK_NET(net, ctnl, info));
struct ip_set *s;
ip_set_id_t i;
int ret = 0;
@@ -1212,9 +1238,6 @@ IPSET_CBFN(ip_set_destroy, struct net *net, struct sock *ctnl,
if (unlikely(protocol_min_failed(attr)))
return -IPSET_ERR_PROTOCOL;
- /* Must wait for flush to be really finished in list:set */
- rcu_barrier();
-
/* Commands are serialized and references are
* protected by the ip_set_ref_lock.
* External systems (i.e. xt_set) must call
@@ -1225,8 +1248,10 @@ IPSET_CBFN(ip_set_destroy, struct net *net, struct sock *ctnl,
* counter, so if it's already zero, we can proceed
* without holding the lock.
*/
- read_lock_bh(&ip_set_ref_lock);
if (!attr[IPSET_ATTR_SETNAME]) {
+ /* Must wait for flush to be really finished in list:set */
+ rcu_barrier();
+ read_lock_bh(&ip_set_ref_lock);
for (i = 0; i < inst->ip_set_max; i++) {
s = ip_set(inst, i);
if (s && (s->ref || s->ref_netlink)) {
@@ -1246,7 +1271,10 @@ IPSET_CBFN(ip_set_destroy, struct net *net, struct sock *ctnl,
/* Modified by ip_set_destroy() only, which is serialized */
inst->is_destroyed = false;
} else {
- u32 flags = flag_exist(nlh);
+ u32 flags = flag_exist(INFO_NLH(info, nlh));
+ u16 features = 0;
+
+ read_lock_bh(&ip_set_ref_lock);
s = find_set_and_id(inst, nla_data(attr[IPSET_ATTR_SETNAME]),
&i);
if (!s) {
@@ -1257,10 +1285,16 @@ IPSET_CBFN(ip_set_destroy, struct net *net, struct sock *ctnl,
ret = -IPSET_ERR_BUSY;
goto out;
}
+ features = s->type->features;
ip_set(inst, i) = NULL;
read_unlock_bh(&ip_set_ref_lock);
-
- ip_set_destroy_set(s);
+ if (features & IPSET_TYPE_NAME) {
+ /* Must wait for flush to be really finished */
+ rcu_barrier();
+ }
+ /* Must cancel garbage collectors */
+ ip_set_destroy_cancel_gc(s);
+ call_rcu(&s->rcu, ip_set_destroy_set_variant_rcu);
}
return 0;
out:
@@ -1284,9 +1318,10 @@ static int
IPSET_CBFN(ip_set_flush, struct net *net, struct sock *ctnl,
struct sk_buff *skb, const struct nlmsghdr *nlh,
const struct nlattr * const attr[],
- struct netlink_ext_ack *extack)
+ struct netlink_ext_ack *extack,
+ const struct nfnl_info *info)
{
- struct ip_set_net *inst = ip_set_pernet(IPSET_SOCK_NET(net, ctnl));
+ struct ip_set_net *inst = ip_set_pernet(IPSET_SOCK_NET(net, ctnl, info));
struct ip_set *s;
ip_set_id_t i;
@@ -1325,9 +1360,10 @@ static int
IPSET_CBFN(ip_set_rename, struct net *net, struct sock *ctnl,
struct sk_buff *skb, const struct nlmsghdr *nlh,
const struct nlattr * const attr[],
- struct netlink_ext_ack *extack)
+ struct netlink_ext_ack *extack,
+ const struct nfnl_info *info)
{
- struct ip_set_net *inst = ip_set_pernet(IPSET_SOCK_NET(net, ctnl));
+ struct ip_set_net *inst = ip_set_pernet(IPSET_SOCK_NET(net, ctnl, info));
struct ip_set *set, *s;
const char *name2;
ip_set_id_t i;
@@ -1356,11 +1392,11 @@ IPSET_CBFN(ip_set_rename, struct net *net, struct sock *ctnl,
goto out;
}
}
- ret = strscpy(set->name, name2, IPSET_MAXNAMELEN);
+ strscpy_pad(set->name, name2, IPSET_MAXNAMELEN);
out:
write_unlock_bh(&ip_set_ref_lock);
- return ret < 0 ? ret : 0;
+ return ret;
}
/* Swap two sets so that name/index points to the other.
@@ -1376,9 +1412,10 @@ static int
IPSET_CBFN(ip_set_swap, struct net *net, struct sock *ctnl,
struct sk_buff *skb, const struct nlmsghdr *nlh,
const struct nlattr * const attr[],
- struct netlink_ext_ack *extack)
+ struct netlink_ext_ack *extack,
+ const struct nfnl_info *info)
{
- struct ip_set_net *inst = ip_set_pernet(IPSET_SOCK_NET(net, ctnl));
+ struct ip_set_net *inst = ip_set_pernet(IPSET_SOCK_NET(net, ctnl, info));
struct ip_set *from, *to;
ip_set_id_t from_id, to_id;
char from_name[IPSET_MAXNAMELEN];
@@ -1413,9 +1450,9 @@ IPSET_CBFN(ip_set_swap, struct net *net, struct sock *ctnl,
return -EBUSY;
}
- strncpy(from_name, from->name, IPSET_MAXNAMELEN);
- strncpy(from->name, to->name, IPSET_MAXNAMELEN);
- strncpy(to->name, from_name, IPSET_MAXNAMELEN);
+ strscpy_pad(from_name, from->name, IPSET_MAXNAMELEN);
+ strscpy_pad(from->name, to->name, IPSET_MAXNAMELEN);
+ strscpy_pad(to->name, from_name, IPSET_MAXNAMELEN);
swap(from->ref, to->ref);
ip_set(inst, from_id) = to;
@@ -1706,7 +1743,8 @@ static int
IPSET_CBFN(ip_set_dump, struct net *net, struct sock *ctnl,
struct sk_buff *skb, const struct nlmsghdr *nlh,
const struct nlattr * const attr[],
- struct netlink_ext_ack *extack)
+ struct netlink_ext_ack *extack,
+ const struct nfnl_info *info)
{
if (unlikely(protocol_min_failed(attr)))
return -IPSET_ERR_PROTOCOL;
@@ -1728,7 +1766,7 @@ IPSET_CBFN(ip_set_dump, struct net *net, struct sock *ctnl,
.dump = ip_set_dump_do,
.done = ip_set_dump_done,
};
- return netlink_dump_start(ctnl, skb, nlh, &c);
+ return netlink_dump_start(INFO_SK(info, ctnl), skb, INFO_NLH(info, nlh), &c);
}
#endif
}
@@ -1745,8 +1783,8 @@ static const struct nla_policy ip_set_adt_policy[IPSET_ATTR_CMD_MAX + 1] = {
};
static int
-call_ad(struct sock *ctnl, struct sk_buff *skb, struct ip_set *set,
- struct nlattr *tb[], enum ipset_adt adt,
+CALL_AD(struct net *net, struct sock *ctnl, struct sk_buff *skb,
+ struct ip_set *set, struct nlattr *tb[], enum ipset_adt adt,
u32 flags, bool use_lineno)
{
int ret;
@@ -1754,13 +1792,22 @@ call_ad(struct sock *ctnl, struct sk_buff *skb, struct ip_set *set,
bool eexist = flags & IPSET_FLAG_EXIST, retried = false;
do {
+ if (retried) {
+ __ip_set_get_netlink(set);
+ nfnl_unlock(NFNL_SUBSYS_IPSET);
+ cond_resched();
+ nfnl_lock(NFNL_SUBSYS_IPSET);
+ __ip_set_put_netlink(set);
+ }
+
ip_set_lock(set);
ret = set->variant->uadt(set, tb, adt, &lineno, flags, retried);
ip_set_unlock(set);
retried = true;
- } while (ret == -EAGAIN &&
- set->variant->resize &&
- (ret = set->variant->resize(set, retried)) == 0);
+ } while (ret == -ERANGE ||
+ (ret == -EAGAIN &&
+ set->variant->resize &&
+ (ret = set->variant->resize(set, retried)) == 0));
if (!ret || (ret == -IPSET_ERR_EXIST && eexist))
return 0;
@@ -1779,11 +1826,12 @@ call_ad(struct sock *ctnl, struct sk_buff *skb, struct ip_set *set,
skb2 = nlmsg_new(payload, GFP_KERNEL);
if (!skb2)
return -ENOMEM;
- rep = __nlmsg_put(skb2, NETLINK_PORTID(skb),
- nlh->nlmsg_seq, NLMSG_ERROR, payload, 0);
+ rep = nlmsg_put(skb2, NETLINK_PORTID(skb),
+ nlh->nlmsg_seq, NLMSG_ERROR, payload, 0);
errmsg = nlmsg_data(rep);
errmsg->error = ret;
- memcpy(&errmsg->msg, nlh, nlh->nlmsg_len);
+ unsafe_memcpy(&errmsg->msg, nlh, nlh->nlmsg_len,
+ /* Bounds checked by the skb layer. */);
cmdattr = (void *)&errmsg->msg + min_len;
ret = NLA_PARSE(cda, IPSET_ATTR_CMD_MAX, cmdattr,
@@ -1798,8 +1846,7 @@ call_ad(struct sock *ctnl, struct sk_buff *skb, struct ip_set *set,
*errline = lineno;
- netlink_unicast(ctnl, skb2, NETLINK_PORTID(skb),
- MSG_DONTWAIT);
+ NFNETLINK_UNICAST(ctnl, skb2, net, NETLINK_PORTID(skb));
/* Signal netlink not to send its ACK/errmsg. */
return -EINTR;
}
@@ -1807,19 +1854,18 @@ call_ad(struct sock *ctnl, struct sk_buff *skb, struct ip_set *set,
return ret;
}
-static int
-IPSET_CBFN_AD(ip_set_ad, struct net *net, struct sock *ctnl,
- struct sk_buff *skb,
- enum ipset_adt adt,
- const struct nlmsghdr *nlh,
- const struct nlattr * const attr[],
- struct netlink_ext_ack *extack)
-{
- struct ip_set_net *inst = ip_set_pernet(IPSET_SOCK_NET(net, ctnl));
+static int IPSET_CBFN_AD(ip_set_ad, struct net *net, struct sock *ctnl,
+ struct sk_buff *skb,
+ enum ipset_adt adt,
+ const struct nlmsghdr *nlh,
+ const struct nlattr * const attr[],
+ struct netlink_ext_ack *extack, const struct nfnl_info *info)
+{
+ struct ip_set_net *inst = ip_set_pernet(IPSET_SOCK_NET(net, ctnl, info));
struct ip_set *set;
struct nlattr *tb[IPSET_ATTR_ADT_MAX + 1] = {};
const struct nlattr *nla;
- u32 flags = flag_exist(nlh);
+ u32 flags = flag_exist(INFO_NLH(info, nlh));
bool use_lineno;
int ret = 0;
@@ -1844,7 +1890,7 @@ IPSET_CBFN_AD(ip_set_ad, struct net *net, struct sock *ctnl,
attr[IPSET_ATTR_DATA],
set->type->adt_policy, NULL))
return -IPSET_ERR_PROTOCOL;
- ret = call_ad(ctnl, skb, set, tb, adt, flags,
+ ret = CALL_AD(net, ctnl, skb, set, tb, adt, flags,
use_lineno);
} else {
int nla_rem;
@@ -1855,7 +1901,7 @@ IPSET_CBFN_AD(ip_set_ad, struct net *net, struct sock *ctnl,
NLA_PARSE_NESTED(tb, IPSET_ATTR_ADT_MAX, nla,
set->type->adt_policy, NULL))
return -IPSET_ERR_PROTOCOL;
- ret = call_ad(ctnl, skb, set, tb, adt,
+ ret = CALL_AD(net, ctnl, skb, set, tb, adt,
flags, use_lineno);
if (ret < 0)
return ret;
@@ -1868,20 +1914,22 @@ static int
IPSET_CBFN(ip_set_uadd, struct net *net, struct sock *ctnl,
struct sk_buff *skb, const struct nlmsghdr *nlh,
const struct nlattr * const attr[],
- struct netlink_ext_ack *extack)
+ struct netlink_ext_ack *extack,
+ const struct nfnl_info *info)
{
- return IPSET_CBFN_AD(ip_set_ad, net, ctnl, skb,
- IPSET_ADD, nlh, attr, extack);
+ return IPSET_CBFN_AD(ip_set_ad, INFO_NET(info, net), INFO_SK(info, ctnl), skb,
+ IPSET_ADD, INFO_NLH(info, nlh), attr, extack, info);
}
static int
IPSET_CBFN(ip_set_udel, struct net *net, struct sock *ctnl,
struct sk_buff *skb, const struct nlmsghdr *nlh,
const struct nlattr * const attr[],
- struct netlink_ext_ack *extack)
+ struct netlink_ext_ack *extack,
+ const struct nfnl_info *info)
{
- return IPSET_CBFN_AD(ip_set_ad, net, ctnl, skb,
- IPSET_DEL, nlh, attr, extack);
+ return IPSET_CBFN_AD(ip_set_ad, INFO_NET(info, net), INFO_SK(info, ctnl), skb,
+ IPSET_DEL, INFO_NLH(info, nlh), attr, extack, info);
}
static int
@@ -1889,9 +1937,10 @@ IPSET_CBFN(ip_set_utest, struct net *net, struct sock *ctnl,
struct sk_buff *skb,
const struct nlmsghdr *nlh,
const struct nlattr * const attr[],
- struct netlink_ext_ack *extack)
+ struct netlink_ext_ack *extack,
+ const struct nfnl_info *info)
{
- struct ip_set_net *inst = ip_set_pernet(IPSET_SOCK_NET(net, ctnl));
+ struct ip_set_net *inst = ip_set_pernet(IPSET_SOCK_NET(net, ctnl, info));
struct ip_set *set;
struct nlattr *tb[IPSET_ATTR_ADT_MAX + 1] = {};
int ret = 0;
@@ -1927,13 +1976,13 @@ static int
IPSET_CBFN(ip_set_header, struct net *net, struct sock *ctnl,
struct sk_buff *skb, const struct nlmsghdr *nlh,
const struct nlattr * const attr[],
- struct netlink_ext_ack *extack)
+ struct netlink_ext_ack *extack,
+ const struct nfnl_info *info)
{
- struct ip_set_net *inst = ip_set_pernet(IPSET_SOCK_NET(net, ctnl));
+ struct ip_set_net *inst = ip_set_pernet(IPSET_SOCK_NET(net, ctnl, info));
const struct ip_set *set;
struct sk_buff *skb2;
struct nlmsghdr *nlh2;
- int ret = 0;
if (unlikely(protocol_min_failed(attr) ||
!attr[IPSET_ATTR_SETNAME]))
@@ -1947,7 +1996,7 @@ IPSET_CBFN(ip_set_header, struct net *net, struct sock *ctnl,
if (!skb2)
return -ENOMEM;
- nlh2 = start_msg(skb2, NETLINK_PORTID(skb), nlh->nlmsg_seq, 0,
+ nlh2 = start_msg(skb2, NETLINK_PORTID(skb), INFO_NLH(info, nlh)->nlmsg_seq, 0,
IPSET_CMD_HEADER);
if (!nlh2)
goto nlmsg_failure;
@@ -1959,11 +2008,7 @@ IPSET_CBFN(ip_set_header, struct net *net, struct sock *ctnl,
goto nla_put_failure;
nlmsg_end(skb2, nlh2);
- ret = netlink_unicast(ctnl, skb2, NETLINK_PORTID(skb), MSG_DONTWAIT);
- if (ret < 0)
- return ret;
-
- return 0;
+ return NFNETLINK_UNICAST(INFO_SK(info, ctnl), skb2, INFO_NET(info, net), NETLINK_PORTID(skb));
nla_put_failure:
nlmsg_cancel(skb2, nlh2);
@@ -1985,7 +2030,8 @@ static int
IPSET_CBFN(ip_set_type, struct net *net, struct sock *ctnl,
struct sk_buff *skb, const struct nlmsghdr *nlh,
const struct nlattr * const attr[],
- struct netlink_ext_ack *extack)
+ struct netlink_ext_ack *extack,
+ const struct nfnl_info *info)
{
struct sk_buff *skb2;
struct nlmsghdr *nlh2;
@@ -2008,7 +2054,7 @@ IPSET_CBFN(ip_set_type, struct net *net, struct sock *ctnl,
if (!skb2)
return -ENOMEM;
- nlh2 = start_msg(skb2, NETLINK_PORTID(skb), nlh->nlmsg_seq, 0,
+ nlh2 = start_msg(skb2, NETLINK_PORTID(skb), INFO_NLH(info, nlh)->nlmsg_seq, 0,
IPSET_CMD_TYPE);
if (!nlh2)
goto nlmsg_failure;
@@ -2021,11 +2067,7 @@ IPSET_CBFN(ip_set_type, struct net *net, struct sock *ctnl,
nlmsg_end(skb2, nlh2);
pr_debug("Send TYPE, nlmsg_len: %u\n", nlh2->nlmsg_len);
- ret = netlink_unicast(ctnl, skb2, NETLINK_PORTID(skb), MSG_DONTWAIT);
- if (ret < 0)
- return ret;
-
- return 0;
+ return NFNETLINK_UNICAST(INFO_SK(info, ctnl), skb2, INFO_NET(info, net), NETLINK_PORTID(skb));
nla_put_failure:
nlmsg_cancel(skb2, nlh2);
@@ -2045,11 +2087,11 @@ static int
IPSET_CBFN(ip_set_protocol, struct net *net, struct sock *ctnl,
struct sk_buff *skb, const struct nlmsghdr *nlh,
const struct nlattr * const attr[],
- struct netlink_ext_ack *extack)
+ struct netlink_ext_ack *extack,
+ const struct nfnl_info *info)
{
struct sk_buff *skb2;
struct nlmsghdr *nlh2;
- int ret = 0;
if (unlikely(!attr[IPSET_ATTR_PROTOCOL]))
return -IPSET_ERR_PROTOCOL;
@@ -2058,7 +2100,7 @@ IPSET_CBFN(ip_set_protocol, struct net *net, struct sock *ctnl,
if (!skb2)
return -ENOMEM;
- nlh2 = start_msg(skb2, NETLINK_PORTID(skb), nlh->nlmsg_seq, 0,
+ nlh2 = start_msg(skb2, NETLINK_PORTID(skb), INFO_NLH(info, nlh)->nlmsg_seq, 0,
IPSET_CMD_PROTOCOL);
if (!nlh2)
goto nlmsg_failure;
@@ -2068,11 +2110,7 @@ IPSET_CBFN(ip_set_protocol, struct net *net, struct sock *ctnl,
goto nla_put_failure;
nlmsg_end(skb2, nlh2);
- ret = netlink_unicast(ctnl, skb2, NETLINK_PORTID(skb), MSG_DONTWAIT);
- if (ret < 0)
- return ret;
-
- return 0;
+ return NFNETLINK_UNICAST(INFO_SK(info, ctnl), skb2, INFO_NET(info, net), NETLINK_PORTID(skb));
nla_put_failure:
nlmsg_cancel(skb2, nlh2);
@@ -2087,14 +2125,14 @@ static int
IPSET_CBFN(ip_set_byname, struct net *net, struct sock *ctnl,
struct sk_buff *skb, const struct nlmsghdr *nlh,
const struct nlattr * const attr[],
- struct netlink_ext_ack *extack)
+ struct netlink_ext_ack *extack,
+ const struct nfnl_info *info)
{
- struct ip_set_net *inst = ip_set_pernet(IPSET_SOCK_NET(net, ctnl));
+ struct ip_set_net *inst = ip_set_pernet(IPSET_SOCK_NET(net, ctnl, info));
struct sk_buff *skb2;
struct nlmsghdr *nlh2;
ip_set_id_t id = IPSET_INVALID_ID;
const struct ip_set *set;
- int ret = 0;
if (unlikely(protocol_failed(attr) ||
!attr[IPSET_ATTR_SETNAME]))
@@ -2108,7 +2146,7 @@ IPSET_CBFN(ip_set_byname, struct net *net, struct sock *ctnl,
if (!skb2)
return -ENOMEM;
- nlh2 = start_msg(skb2, NETLINK_PORTID(skb), nlh->nlmsg_seq, 0,
+ nlh2 = start_msg(skb2, NETLINK_PORTID(skb), INFO_NLH(info, nlh)->nlmsg_seq, 0,
IPSET_CMD_GET_BYNAME);
if (!nlh2)
goto nlmsg_failure;
@@ -2118,11 +2156,7 @@ IPSET_CBFN(ip_set_byname, struct net *net, struct sock *ctnl,
goto nla_put_failure;
nlmsg_end(skb2, nlh2);
- ret = netlink_unicast(ctnl, skb2, NETLINK_PORTID(skb), MSG_DONTWAIT);
- if (ret < 0)
- return ret;
-
- return 0;
+ return NFNETLINK_UNICAST(INFO_SK(info, ctnl), skb2, INFO_NET(info, net), NETLINK_PORTID(skb));
nla_put_failure:
nlmsg_cancel(skb2, nlh2);
@@ -2140,14 +2174,14 @@ static int
IPSET_CBFN(ip_set_byindex, struct net *net, struct sock *ctnl,
struct sk_buff *skb, const struct nlmsghdr *nlh,
const struct nlattr * const attr[],
- struct netlink_ext_ack *extack)
+ struct netlink_ext_ack *extack,
+ const struct nfnl_info *info)
{
- struct ip_set_net *inst = ip_set_pernet(IPSET_SOCK_NET(net, ctnl));
+ struct ip_set_net *inst = ip_set_pernet(IPSET_SOCK_NET(net, ctnl, info));
struct sk_buff *skb2;
struct nlmsghdr *nlh2;
ip_set_id_t id = IPSET_INVALID_ID;
const struct ip_set *set;
- int ret = 0;
if (unlikely(protocol_failed(attr) ||
!attr[IPSET_ATTR_INDEX]))
@@ -2164,7 +2198,7 @@ IPSET_CBFN(ip_set_byindex, struct net *net, struct sock *ctnl,
if (!skb2)
return -ENOMEM;
- nlh2 = start_msg(skb2, NETLINK_PORTID(skb), nlh->nlmsg_seq, 0,
+ nlh2 = start_msg(skb2, NETLINK_PORTID(skb), INFO_NLH(info, nlh)->nlmsg_seq, 0,
IPSET_CMD_GET_BYINDEX);
if (!nlh2)
goto nlmsg_failure;
@@ -2173,11 +2207,7 @@ IPSET_CBFN(ip_set_byindex, struct net *net, struct sock *ctnl,
goto nla_put_failure;
nlmsg_end(skb2, nlh2);
- ret = netlink_unicast(ctnl, skb2, NETLINK_PORTID(skb), MSG_DONTWAIT);
- if (ret < 0)
- return ret;
-
- return 0;
+ return NFNETLINK_UNICAST(INFO_SK(info, ctnl), skb2, INFO_NET(info, net), NETLINK_PORTID(skb));
nla_put_failure:
nlmsg_cancel(skb2, nlh2);
@@ -2189,80 +2219,96 @@ nlmsg_failure:
static const struct nfnl_callback ip_set_netlink_subsys_cb[IPSET_MSG_MAX] = {
[IPSET_CMD_NONE] = {
.call = ip_set_none,
+ SET_NFNL_CALLBACK_TYPE(NFNL_CB_MUTEX)
.attr_count = IPSET_ATTR_CMD_MAX,
},
[IPSET_CMD_CREATE] = {
.call = ip_set_create,
+ SET_NFNL_CALLBACK_TYPE(NFNL_CB_MUTEX)
.attr_count = IPSET_ATTR_CMD_MAX,
.policy = ip_set_create_policy,
},
[IPSET_CMD_DESTROY] = {
.call = ip_set_destroy,
+ SET_NFNL_CALLBACK_TYPE(NFNL_CB_MUTEX)
.attr_count = IPSET_ATTR_CMD_MAX,
.policy = ip_set_setname_policy,
},
[IPSET_CMD_FLUSH] = {
.call = ip_set_flush,
+ SET_NFNL_CALLBACK_TYPE(NFNL_CB_MUTEX)
.attr_count = IPSET_ATTR_CMD_MAX,
.policy = ip_set_setname_policy,
},
[IPSET_CMD_RENAME] = {
.call = ip_set_rename,
+ SET_NFNL_CALLBACK_TYPE(NFNL_CB_MUTEX)
.attr_count = IPSET_ATTR_CMD_MAX,
.policy = ip_set_setname2_policy,
},
[IPSET_CMD_SWAP] = {
.call = ip_set_swap,
+ SET_NFNL_CALLBACK_TYPE(NFNL_CB_MUTEX)
.attr_count = IPSET_ATTR_CMD_MAX,
.policy = ip_set_setname2_policy,
},
[IPSET_CMD_LIST] = {
.call = ip_set_dump,
+ SET_NFNL_CALLBACK_TYPE(NFNL_CB_MUTEX)
.attr_count = IPSET_ATTR_CMD_MAX,
.policy = ip_set_dump_policy,
},
[IPSET_CMD_SAVE] = {
.call = ip_set_dump,
+ SET_NFNL_CALLBACK_TYPE(NFNL_CB_MUTEX)
.attr_count = IPSET_ATTR_CMD_MAX,
.policy = ip_set_setname_policy,
},
[IPSET_CMD_ADD] = {
.call = ip_set_uadd,
+ SET_NFNL_CALLBACK_TYPE(NFNL_CB_MUTEX)
.attr_count = IPSET_ATTR_CMD_MAX,
.policy = ip_set_adt_policy,
},
[IPSET_CMD_DEL] = {
.call = ip_set_udel,
+ SET_NFNL_CALLBACK_TYPE(NFNL_CB_MUTEX)
.attr_count = IPSET_ATTR_CMD_MAX,
.policy = ip_set_adt_policy,
},
[IPSET_CMD_TEST] = {
.call = ip_set_utest,
+ SET_NFNL_CALLBACK_TYPE(NFNL_CB_MUTEX)
.attr_count = IPSET_ATTR_CMD_MAX,
.policy = ip_set_adt_policy,
},
[IPSET_CMD_HEADER] = {
.call = ip_set_header,
+ SET_NFNL_CALLBACK_TYPE(NFNL_CB_MUTEX)
.attr_count = IPSET_ATTR_CMD_MAX,
.policy = ip_set_setname_policy,
},
[IPSET_CMD_TYPE] = {
.call = ip_set_type,
+ SET_NFNL_CALLBACK_TYPE(NFNL_CB_MUTEX)
.attr_count = IPSET_ATTR_CMD_MAX,
.policy = ip_set_type_policy,
},
[IPSET_CMD_PROTOCOL] = {
.call = ip_set_protocol,
+ SET_NFNL_CALLBACK_TYPE(NFNL_CB_MUTEX)
.attr_count = IPSET_ATTR_CMD_MAX,
.policy = ip_set_protocol_policy,
},
[IPSET_CMD_GET_BYNAME] = {
.call = ip_set_byname,
+ SET_NFNL_CALLBACK_TYPE(NFNL_CB_MUTEX)
.attr_count = IPSET_ATTR_CMD_MAX,
.policy = ip_set_setname_policy,
},
[IPSET_CMD_GET_BYINDEX] = {
.call = ip_set_byindex,
+ SET_NFNL_CALLBACK_TYPE(NFNL_CB_MUTEX)
.attr_count = IPSET_ATTR_CMD_MAX,
.policy = ip_set_index_policy,
},
@@ -2435,7 +2481,6 @@ ip_set_net_init(struct net *net)
#else
goto err_alloc;
#endif
- inst->is_deleted = false;
inst->is_destroyed = false;
rcu_assign_pointer(inst->ip_set_list, list);
return 0;
@@ -2452,20 +2497,6 @@ ip_set_net_exit(struct net *net)
{
struct ip_set_net *inst = ip_set_pernet(net);
- struct ip_set *set = NULL;
- ip_set_id_t i;
-
- inst->is_deleted = true; /* flag for ip_set_nfnl_put */
-
- nfnl_lock(NFNL_SUBSYS_IPSET);
- for (i = 0; i < inst->ip_set_max; i++) {
- set = ip_set(inst, i);
- if (set) {
- ip_set(inst, i) = NULL;
- ip_set_destroy_set(set);
- }
- }
- nfnl_unlock(NFNL_SUBSYS_IPSET);
kvfree(rcu_dereference_protected(inst->ip_set_list, 1));
#ifndef HAVE_NET_OPS_ID
kvfree(inst);
@@ -2530,8 +2561,8 @@ ip_set_fini(void)
{
nf_unregister_sockopt(&so_set);
nfnetlink_subsys_unregister(&ip_set_netlink_subsys);
-
UNREGISTER_PERNET_SUBSYS(&ip_set_net_ops);
+
pr_debug("these are the famous last words\n");
}
diff --git a/kernel/net/netfilter/ipset/ip_set_hash_gen.h b/kernel/net/netfilter/ipset/ip_set_hash_gen.h
index adf35fd..76c3894 100644
--- a/kernel/net/netfilter/ipset/ip_set_hash_gen.h
+++ b/kernel/net/netfilter/ipset/ip_set_hash_gen.h
@@ -43,31 +43,8 @@
#define AHASH_MAX_SIZE (6 * AHASH_INIT_SIZE)
/* Max muber of elements in the array block when tuned */
#define AHASH_MAX_TUNED 64
-
#define AHASH_MAX(h) ((h)->bucketsize)
-/* Max number of elements can be tuned */
-#ifdef IP_SET_HASH_WITH_MULTI
-static u8
-tune_bucketsize(u8 curr, u32 multi)
-{
- u32 n;
-
- if (multi < curr)
- return curr;
-
- n = curr + AHASH_INIT_SIZE;
- /* Currently, at listing one hash bucket must fit into a message.
- * Therefore we have a hard limit here.
- */
- return n > curr && n <= AHASH_MAX_TUNED ? n : curr;
-}
-#define TUNE_BUCKETSIZE(h, multi) \
- ((h)->bucketsize = tune_bucketsize((h)->bucketsize, multi))
-#else
-#define TUNE_BUCKETSIZE(h, multi)
-#endif
-
/* A hash bucket */
struct hbucket {
struct rcu_head rcu; /* for call_rcu_bh */
@@ -131,11 +108,11 @@ htable_size(u8 hbits)
{
size_t hsize;
- /* We must fit both into u32 in jhash and size_t */
+ /* We must fit both into u32 in jhash and INT_MAX in kvmalloc_node() */
if (hbits > 31)
return 0;
hsize = jhash_size(hbits);
- if ((((size_t)-1) - sizeof(struct htable)) / sizeof(struct hbucket *)
+ if ((INT_MAX - sizeof(struct htable)) / sizeof(struct hbucket *)
< hsize)
return 0;
@@ -183,6 +160,17 @@ htable_size(u8 hbits)
(SET_WITH_TIMEOUT(set) && \
ip_set_timeout_expired(ext_timeout(d, set)))
+#if defined(IP_SET_HASH_WITH_NETMASK) || defined(IP_SET_HASH_WITH_BITMASK)
+static const union nf_inet_addr onesmask = {
+ .all[0] = 0xffffffff,
+ .all[1] = 0xffffffff,
+ .all[2] = 0xffffffff,
+ .all[3] = 0xffffffff
+};
+
+static const union nf_inet_addr zeromask = {};
+#endif
+
#endif /* _IP_SET_HASH_GEN_H */
#ifndef MTYPE
@@ -234,6 +222,7 @@ htable_size(u8 hbits)
#undef mtype_gc_do
#undef mtype_gc
#undef mtype_gc_init
+#undef mtype_cancel_gc
#undef mtype_variant
#undef mtype_data_match
@@ -278,6 +267,7 @@ htable_size(u8 hbits)
#define mtype_gc_do IPSET_TOKEN(MTYPE, _gc_do)
#define mtype_gc IPSET_TOKEN(MTYPE, _gc)
#define mtype_gc_init IPSET_TOKEN(MTYPE, _gc_init)
+#define mtype_cancel_gc IPSET_TOKEN(MTYPE, _cancel_gc)
#define mtype_variant IPSET_TOKEN(MTYPE, _variant)
#define mtype_data_match IPSET_TOKEN(MTYPE, _data_match)
@@ -307,8 +297,9 @@ struct htype {
u32 markmask; /* markmask value for mark mask to store */
#endif
u8 bucketsize; /* max elements in an array block */
-#ifdef IP_SET_HASH_WITH_NETMASK
+#if defined(IP_SET_HASH_WITH_NETMASK) || defined(IP_SET_HASH_WITH_BITMASK)
u8 netmask; /* netmask value for subnets to store */
+ union nf_inet_addr bitmask; /* stores bitmask */
#endif
struct list_head ad; /* Resize add|del backlist */
struct mtype_elem next; /* temporary storage for uadd */
@@ -441,7 +432,7 @@ mtype_ahash_destroy(struct ip_set *set, struct htable *t, bool ext_destroy)
u32 i;
for (i = 0; i < jhash_size(t->htable_bits); i++) {
- n = __ipset_dereference(hbucket(t, i));
+ n = (__force struct hbucket *)hbucket(t, i);
if (!n)
continue;
if (set->extensions & IPSET_EXT_DESTROY && ext_destroy)
@@ -461,10 +452,7 @@ mtype_destroy(struct ip_set *set)
struct htype *h = set->data;
struct list_head *l, *lt;
- if (SET_WITH_TIMEOUT(set))
- cancel_delayed_work_sync(&h->gc.dwork);
-
- mtype_ahash_destroy(set, ipset_dereference_nfnl(h->table), true);
+ mtype_ahash_destroy(set, (__force struct htable *)h->table, true);
list_for_each_safe(l, lt, &h->ad) {
list_del(l);
kfree(l);
@@ -483,8 +471,8 @@ mtype_same_set(const struct ip_set *a, const struct ip_set *b)
/* Resizing changes htable_bits, so we ignore it */
return x->maxelem == y->maxelem &&
a->timeout == b->timeout &&
-#ifdef IP_SET_HASH_WITH_NETMASK
- x->netmask == y->netmask &&
+#if defined(IP_SET_HASH_WITH_NETMASK) || defined(IP_SET_HASH_WITH_BITMASK)
+ nf_inet_addr_cmp(&x->bitmask, &y->bitmask) &&
#endif
#ifdef IP_SET_HASH_WITH_MARKMASK
x->markmask == y->markmask &&
@@ -609,6 +597,15 @@ mtype_gc_init(struct htable_gc *gc)
queue_delayed_work(system_power_efficient_wq, &gc->dwork, HZ);
}
+static void
+mtype_cancel_gc(struct ip_set *set)
+{
+ struct htype *h = set->data;
+
+ if (SET_WITH_TIMEOUT(set))
+ cancel_delayed_work_sync(&h->gc.dwork);
+}
+
static int
mtype_add(struct ip_set *set, void *value, const struct ip_set_ext *ext,
struct ip_set_ext *mext, u32 flags);
@@ -937,7 +934,12 @@ mtype_add(struct ip_set *set, void *value, const struct ip_set_ext *ext,
goto set_full;
/* Create a new slot */
if (n->pos >= n->size) {
- TUNE_BUCKETSIZE(h, multi);
+#ifdef IP_SET_HASH_WITH_MULTI
+ if (h->bucketsize >= AHASH_MAX_TUNED)
+ goto set_full;
+ else if (h->bucketsize <= multi)
+ h->bucketsize += AHASH_INIT_SIZE;
+#endif
if (n->size >= AHASH_MAX(h)) {
/* Trigger rehashing */
mtype_data_next(&h->next, d);
@@ -1283,9 +1285,21 @@ mtype_head(struct ip_set *set, struct sk_buff *skb)
htonl(jhash_size(htable_bits))) ||
nla_put_net32(skb, IPSET_ATTR_MAXELEM, htonl(h->maxelem)))
goto nla_put_failure;
+#ifdef IP_SET_HASH_WITH_BITMASK
+ /* if netmask is set to anything other than HOST_MASK we know that the user supplied netmask
+ * and not bitmask. These two are mutually exclusive. */
+ if (h->netmask == HOST_MASK && !nf_inet_addr_cmp(&onesmask, &h->bitmask)) {
+ if (set->family == NFPROTO_IPV4) {
+ if (nla_put_ipaddr4(skb, IPSET_ATTR_BITMASK, h->bitmask.ip))
+ goto nla_put_failure;
+ } else if (set->family == NFPROTO_IPV6) {
+ if (nla_put_ipaddr6(skb, IPSET_ATTR_BITMASK, &h->bitmask.in6))
+ goto nla_put_failure;
+ }
+ }
+#endif
#ifdef IP_SET_HASH_WITH_NETMASK
- if (h->netmask != HOST_MASK &&
- nla_put_u8(skb, IPSET_ATTR_NETMASK, h->netmask))
+ if (h->netmask != HOST_MASK && nla_put_u8(skb, IPSET_ATTR_NETMASK, h->netmask))
goto nla_put_failure;
#endif
#ifdef IP_SET_HASH_WITH_MARKMASK
@@ -1435,6 +1449,7 @@ static const struct ip_set_type_variant mtype_variant = {
.uref = mtype_uref,
.resize = mtype_resize,
.same_set = mtype_same_set,
+ .cancel_gc = mtype_cancel_gc,
.region_lock = true,
};
@@ -1448,8 +1463,10 @@ IPSET_TOKEN(HTYPE, _create)(struct net *net, struct ip_set *set,
u32 markmask;
#endif
u8 hbits;
-#ifdef IP_SET_HASH_WITH_NETMASK
- u8 netmask;
+#if defined(IP_SET_HASH_WITH_NETMASK) || defined(IP_SET_HASH_WITH_BITMASK)
+ int ret __attribute__((unused)) = 0;
+ u8 netmask = set->family == NFPROTO_IPV4 ? 32 : 128;
+ union nf_inet_addr bitmask = onesmask;
#endif
size_t hsize;
struct htype *h;
@@ -1487,7 +1504,6 @@ IPSET_TOKEN(HTYPE, _create)(struct net *net, struct ip_set *set,
#endif
#ifdef IP_SET_HASH_WITH_NETMASK
- netmask = set->family == NFPROTO_IPV4 ? 32 : 128;
if (tb[IPSET_ATTR_NETMASK]) {
netmask = nla_get_u8(tb[IPSET_ATTR_NETMASK]);
@@ -1495,6 +1511,33 @@ IPSET_TOKEN(HTYPE, _create)(struct net *net, struct ip_set *set,
(set->family == NFPROTO_IPV6 && netmask > 128) ||
netmask == 0)
return -IPSET_ERR_INVALID_NETMASK;
+
+ /* we convert netmask to bitmask and store it */
+ if (set->family == NFPROTO_IPV4)
+ bitmask.ip = ip_set_netmask(netmask);
+ else
+ ip6_netmask(&bitmask, netmask);
+ }
+#endif
+
+#ifdef IP_SET_HASH_WITH_BITMASK
+ if (tb[IPSET_ATTR_BITMASK]) {
+ /* bitmask and netmask do the same thing, allow only one of these options */
+ if (tb[IPSET_ATTR_NETMASK])
+ return -IPSET_ERR_BITMASK_NETMASK_EXCL;
+
+ if (set->family == NFPROTO_IPV4) {
+ ret = ip_set_get_ipaddr4(tb[IPSET_ATTR_BITMASK], &bitmask.ip);
+ if (ret || !bitmask.ip)
+ return -IPSET_ERR_INVALID_NETMASK;
+ } else if (set->family == NFPROTO_IPV6) {
+ ret = ip_set_get_ipaddr6(tb[IPSET_ATTR_BITMASK], &bitmask);
+ if (ret || ipv6_addr_any(&bitmask.in6))
+ return -IPSET_ERR_INVALID_NETMASK;
+ }
+
+ if (nf_inet_addr_cmp(&bitmask, &zeromask))
+ return -IPSET_ERR_INVALID_NETMASK;
}
#endif
@@ -1537,7 +1580,8 @@ IPSET_TOKEN(HTYPE, _create)(struct net *net, struct ip_set *set,
for (i = 0; i < ahash_numof_locks(hbits); i++)
spin_lock_init(&t->hregion[i].lock);
h->maxelem = maxelem;
-#ifdef IP_SET_HASH_WITH_NETMASK
+#if defined(IP_SET_HASH_WITH_NETMASK) || defined(IP_SET_HASH_WITH_BITMASK)
+ h->bitmask = bitmask;
h->netmask = netmask;
#endif
#ifdef IP_SET_HASH_WITH_MARKMASK
diff --git a/kernel/net/netfilter/ipset/ip_set_hash_ip.c b/kernel/net/netfilter/ipset/ip_set_hash_ip.c
index 94e17a5..5a2cb71 100644
--- a/kernel/net/netfilter/ipset/ip_set_hash_ip.c
+++ b/kernel/net/netfilter/ipset/ip_set_hash_ip.c
@@ -25,7 +25,8 @@
/* 2 Comments support */
/* 3 Forceadd support */
/* 4 skbinfo support */
-#define IPSET_TYPE_REV_MAX 5 /* bucketsize, initval support */
+/* 5 bucketsize, initval support */
+#define IPSET_TYPE_REV_MAX 6 /* bitmask support */
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Jozsef Kadlecsik <kadlec@netfilter.org>");
@@ -35,6 +36,7 @@ MODULE_ALIAS("ip_set_hash:ip");
/* Type specific function prefix */
#define HTYPE hash_ip
#define IP_SET_HASH_WITH_NETMASK
+#define IP_SET_HASH_WITH_BITMASK
/* IPv4 variant */
@@ -87,7 +89,7 @@ hash_ip4_kadt(struct ip_set *set, const struct sk_buff *skb,
__be32 ip;
ip4addrptr(skb, opt->flags & IPSET_DIM_ONE_SRC, &ip);
- ip &= ip_set_netmask(h->netmask);
+ ip &= h->bitmask.ip;
if (ip == 0)
return -EINVAL;
@@ -99,11 +101,11 @@ static int
hash_ip4_uadt(struct ip_set *set, struct nlattr *tb[],
enum ipset_adt adt, u32 *lineno, u32 flags, bool retried)
{
- const struct hash_ip4 *h = set->data;
+ struct hash_ip4 *h = set->data;
ipset_adtfn adtfn = set->variant->adt[adt];
struct hash_ip4_elem e = { 0 };
struct ip_set_ext ext = IP_SET_INIT_UEXT(set);
- u32 ip = 0, ip_to = 0, hosts;
+ u32 ip = 0, ip_to = 0, hosts, i = 0;
int ret = 0;
if (tb[IPSET_ATTR_LINENO])
@@ -120,7 +122,7 @@ hash_ip4_uadt(struct ip_set *set, struct nlattr *tb[],
if (ret)
return ret;
- ip &= ip_set_hostmask(h->netmask);
+ ip &= ntohl(h->bitmask.ip);
e.ip = htonl(ip);
if (e.ip == 0)
return -IPSET_ERR_HASH_ELEM;
@@ -133,8 +135,11 @@ hash_ip4_uadt(struct ip_set *set, struct nlattr *tb[],
ret = ip_set_get_hostipaddr4(tb[IPSET_ATTR_IP_TO], &ip_to);
if (ret)
return ret;
- if (ip > ip_to)
+ if (ip > ip_to) {
+ if (ip_to == 0)
+ return -IPSET_ERR_HASH_ELEM;
swap(ip, ip_to);
+ }
} else if (tb[IPSET_ATTR_CIDR]) {
u8 cidr = nla_get_u8(tb[IPSET_ATTR_CIDR]);
@@ -145,18 +150,20 @@ hash_ip4_uadt(struct ip_set *set, struct nlattr *tb[],
hosts = h->netmask == 32 ? 1 : 2 << (32 - h->netmask - 1);
- if (retried) {
+ if (retried)
ip = ntohl(h->next.ip);
+ for (; ip <= ip_to; i++) {
e.ip = htonl(ip);
- }
- for (; ip <= ip_to;) {
+ if (i > IPSET_MAX_RANGE) {
+ hash_ip4_data_next(&h->next, &e);
+ return -ERANGE;
+ }
ret = adtfn(set, &e, &ext, &ext, flags);
if (ret && !ip_set_eexist(ret, flags))
return ret;
ip += hosts;
- e.ip = htonl(ip);
- if (e.ip == 0)
+ if (ip == 0)
return 0;
ret = 0;
@@ -181,12 +188,6 @@ hash_ip6_data_equal(const struct hash_ip6_elem *ip1,
return ipv6_addr_equal(&ip1->ip.in6, &ip2->ip.in6);
}
-static void
-hash_ip6_netmask(union nf_inet_addr *ip, u8 prefix)
-{
- ip6_netmask(ip, prefix);
-}
-
static bool
hash_ip6_data_list(struct sk_buff *skb, const struct hash_ip6_elem *e)
{
@@ -223,7 +224,7 @@ hash_ip6_kadt(struct ip_set *set, const struct sk_buff *skb,
struct ip_set_ext ext = IP_SET_INIT_KEXT(skb, opt, set);
ip6addrptr(skb, opt->flags & IPSET_DIM_ONE_SRC, &e.ip.in6);
- hash_ip6_netmask(&e.ip, h->netmask);
+ nf_inet_addr_mask_inplace(&e.ip, &h->bitmask);
if (ipv6_addr_any(&e.ip.in6))
return -EINVAL;
@@ -262,7 +263,7 @@ hash_ip6_uadt(struct ip_set *set, struct nlattr *tb[],
if (ret)
return ret;
- hash_ip6_netmask(&e.ip, h->netmask);
+ nf_inet_addr_mask_inplace(&e.ip, &h->bitmask);
if (ipv6_addr_any(&e.ip.in6))
return -IPSET_ERR_HASH_ELEM;
@@ -289,6 +290,7 @@ static struct ip_set_type hash_ip_type __read_mostly = {
[IPSET_ATTR_RESIZE] = { .type = NLA_U8 },
[IPSET_ATTR_TIMEOUT] = { .type = NLA_U32 },
[IPSET_ATTR_NETMASK] = { .type = NLA_U8 },
+ [IPSET_ATTR_BITMASK] = { .type = NLA_NESTED },
[IPSET_ATTR_CADT_FLAGS] = { .type = NLA_U32 },
},
.adt_policy = {
diff --git a/kernel/net/netfilter/ipset/ip_set_hash_ipmark.c b/kernel/net/netfilter/ipset/ip_set_hash_ipmark.c
index 1757bf8..ad2a2dc 100644
--- a/kernel/net/netfilter/ipset/ip_set_hash_ipmark.c
+++ b/kernel/net/netfilter/ipset/ip_set_hash_ipmark.c
@@ -99,11 +99,11 @@ static int
hash_ipmark4_uadt(struct ip_set *set, struct nlattr *tb[],
enum ipset_adt adt, u32 *lineno, u32 flags, bool retried)
{
- const struct hash_ipmark4 *h = set->data;
+ struct hash_ipmark4 *h = set->data;
ipset_adtfn adtfn = set->variant->adt[adt];
struct hash_ipmark4_elem e = { };
struct ip_set_ext ext = IP_SET_INIT_UEXT(set);
- u32 ip, ip_to = 0;
+ u32 ip, ip_to = 0, i = 0;
int ret;
if (tb[IPSET_ATTR_LINENO])
@@ -123,6 +123,8 @@ hash_ipmark4_uadt(struct ip_set *set, struct nlattr *tb[],
e.mark = ntohl(nla_get_be32(tb[IPSET_ATTR_MARK]));
e.mark &= h->markmask;
+ if (e.mark == 0 && e.ip == 0)
+ return -IPSET_ERR_HASH_ELEM;
if (adt == IPSET_TEST ||
!(tb[IPSET_ATTR_IP_TO] || tb[IPSET_ATTR_CIDR])) {
@@ -135,8 +137,11 @@ hash_ipmark4_uadt(struct ip_set *set, struct nlattr *tb[],
ret = ip_set_get_hostipaddr4(tb[IPSET_ATTR_IP_TO], &ip_to);
if (ret)
return ret;
- if (ip > ip_to)
+ if (ip > ip_to) {
+ if (e.mark == 0 && ip_to == 0)
+ return -IPSET_ERR_HASH_ELEM;
swap(ip, ip_to);
+ }
} else if (tb[IPSET_ATTR_CIDR]) {
u8 cidr = nla_get_u8(tb[IPSET_ATTR_CIDR]);
@@ -147,8 +152,12 @@ hash_ipmark4_uadt(struct ip_set *set, struct nlattr *tb[],
if (retried)
ip = ntohl(h->next.ip);
- for (; ip <= ip_to; ip++) {
+ for (; ip <= ip_to; ip++, i++) {
e.ip = htonl(ip);
+ if (i > IPSET_MAX_RANGE) {
+ hash_ipmark4_data_next(&h->next, &e);
+ return -ERANGE;
+ }
ret = adtfn(set, &e, &ext, &ext, flags);
if (ret && !ip_set_eexist(ret, flags))
diff --git a/kernel/net/netfilter/ipset/ip_set_hash_ipport.c b/kernel/net/netfilter/ipset/ip_set_hash_ipport.c
index b22f280..46f8bbf 100644
--- a/kernel/net/netfilter/ipset/ip_set_hash_ipport.c
+++ b/kernel/net/netfilter/ipset/ip_set_hash_ipport.c
@@ -27,7 +27,8 @@
/* 3 Comments support added */
/* 4 Forceadd support added */
/* 5 skbinfo support added */
-#define IPSET_TYPE_REV_MAX 6 /* bucketsize, initval support added */
+/* 6 bucketsize, initval support added */
+#define IPSET_TYPE_REV_MAX 7 /* bitmask support added */
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Jozsef Kadlecsik <kadlec@netfilter.org>");
@@ -36,6 +37,8 @@ MODULE_ALIAS("ip_set_hash:ip,port");
/* Type specific function prefix */
#define HTYPE hash_ipport
+#define IP_SET_HASH_WITH_NETMASK
+#define IP_SET_HASH_WITH_BITMASK
/* IPv4 variant */
@@ -93,12 +96,16 @@ hash_ipport4_kadt(struct ip_set *set, const struct sk_buff *skb,
ipset_adtfn adtfn = set->variant->adt[adt];
struct hash_ipport4_elem e = { .ip = 0 };
struct ip_set_ext ext = IP_SET_INIT_KEXT(skb, opt, set);
+ const struct MTYPE *h = set->data;
if (!ip_set_get_ip4_port(skb, opt->flags & IPSET_DIM_TWO_SRC,
&e.port, &e.proto))
return -EINVAL;
ip4addrptr(skb, opt->flags & IPSET_DIM_ONE_SRC, &e.ip);
+ e.ip &= h->bitmask.ip;
+ if (e.ip == 0)
+ return -EINVAL;
return adtfn(set, &e, &ext, &opt->ext, opt->cmdflags);
}
@@ -106,11 +113,11 @@ static int
hash_ipport4_uadt(struct ip_set *set, struct nlattr *tb[],
enum ipset_adt adt, u32 *lineno, u32 flags, bool retried)
{
- const struct hash_ipport4 *h = set->data;
+ struct hash_ipport4 *h = set->data;
ipset_adtfn adtfn = set->variant->adt[adt];
struct hash_ipport4_elem e = { .ip = 0 };
struct ip_set_ext ext = IP_SET_INIT_UEXT(set);
- u32 ip, ip_to = 0, p = 0, port, port_to;
+ u32 ip, ip_to = 0, p = 0, port, port_to, i = 0;
bool with_ports = false;
int ret;
@@ -130,6 +137,10 @@ hash_ipport4_uadt(struct ip_set *set, struct nlattr *tb[],
if (ret)
return ret;
+ e.ip &= h->bitmask.ip;
+ if (e.ip == 0)
+ return -EINVAL;
+
e.port = nla_get_be16(tb[IPSET_ATTR_PORT]);
if (tb[IPSET_ATTR_PROTO]) {
@@ -179,9 +190,13 @@ hash_ipport4_uadt(struct ip_set *set, struct nlattr *tb[],
for (; ip <= ip_to; ip++) {
p = retried && ip == ntohl(h->next.ip) ? ntohs(h->next.port)
: port;
- for (; p <= port_to; p++) {
+ for (; p <= port_to; p++, i++) {
e.ip = htonl(ip);
e.port = htons(p);
+ if (i > IPSET_MAX_RANGE) {
+ hash_ipport4_data_next(&h->next, &e);
+ return -ERANGE;
+ }
ret = adtfn(set, &e, &ext, &ext, flags);
if (ret && !ip_set_eexist(ret, flags))
@@ -251,12 +266,17 @@ hash_ipport6_kadt(struct ip_set *set, const struct sk_buff *skb,
ipset_adtfn adtfn = set->variant->adt[adt];
struct hash_ipport6_elem e = { .ip = { .all = { 0 } } };
struct ip_set_ext ext = IP_SET_INIT_KEXT(skb, opt, set);
+ const struct MTYPE *h = set->data;
if (!ip_set_get_ip6_port(skb, opt->flags & IPSET_DIM_TWO_SRC,
&e.port, &e.proto))
return -EINVAL;
ip6addrptr(skb, opt->flags & IPSET_DIM_ONE_SRC, &e.ip.in6);
+ nf_inet_addr_mask_inplace(&e.ip, &h->bitmask);
+ if (ipv6_addr_any(&e.ip.in6))
+ return -EINVAL;
+
return adtfn(set, &e, &ext, &opt->ext, opt->cmdflags);
}
@@ -296,6 +316,10 @@ hash_ipport6_uadt(struct ip_set *set, struct nlattr *tb[],
if (ret)
return ret;
+ nf_inet_addr_mask_inplace(&e.ip, &h->bitmask);
+ if (ipv6_addr_any(&e.ip.in6))
+ return -EINVAL;
+
e.port = nla_get_be16(tb[IPSET_ATTR_PORT]);
if (tb[IPSET_ATTR_PROTO]) {
@@ -354,6 +378,8 @@ static struct ip_set_type hash_ipport_type __read_mostly = {
[IPSET_ATTR_PROTO] = { .type = NLA_U8 },
[IPSET_ATTR_TIMEOUT] = { .type = NLA_U32 },
[IPSET_ATTR_CADT_FLAGS] = { .type = NLA_U32 },
+ [IPSET_ATTR_NETMASK] = { .type = NLA_U8 },
+ [IPSET_ATTR_BITMASK] = { .type = NLA_NESTED },
},
.adt_policy = {
[IPSET_ATTR_IP] = { .type = NLA_NESTED },
diff --git a/kernel/net/netfilter/ipset/ip_set_hash_ipportip.c b/kernel/net/netfilter/ipset/ip_set_hash_ipportip.c
index d370151..ca5fe9c 100644
--- a/kernel/net/netfilter/ipset/ip_set_hash_ipportip.c
+++ b/kernel/net/netfilter/ipset/ip_set_hash_ipportip.c
@@ -109,11 +109,11 @@ static int
hash_ipportip4_uadt(struct ip_set *set, struct nlattr *tb[],
enum ipset_adt adt, u32 *lineno, u32 flags, bool retried)
{
- const struct hash_ipportip4 *h = set->data;
+ struct hash_ipportip4 *h = set->data;
ipset_adtfn adtfn = set->variant->adt[adt];
struct hash_ipportip4_elem e = { .ip = 0 };
struct ip_set_ext ext = IP_SET_INIT_UEXT(set);
- u32 ip, ip_to = 0, p = 0, port, port_to;
+ u32 ip, ip_to = 0, p = 0, port, port_to, i = 0;
bool with_ports = false;
int ret;
@@ -186,9 +186,13 @@ hash_ipportip4_uadt(struct ip_set *set, struct nlattr *tb[],
for (; ip <= ip_to; ip++) {
p = retried && ip == ntohl(h->next.ip) ? ntohs(h->next.port)
: port;
- for (; p <= port_to; p++) {
+ for (; p <= port_to; p++, i++) {
e.ip = htonl(ip);
e.port = htons(p);
+ if (i > IPSET_MAX_RANGE) {
+ hash_ipportip4_data_next(&h->next, &e);
+ return -ERANGE;
+ }
ret = adtfn(set, &e, &ext, &ext, flags);
if (ret && !ip_set_eexist(ret, flags))
diff --git a/kernel/net/netfilter/ipset/ip_set_hash_ipportnet.c b/kernel/net/netfilter/ipset/ip_set_hash_ipportnet.c
index 64f82f2..8f44644 100644
--- a/kernel/net/netfilter/ipset/ip_set_hash_ipportnet.c
+++ b/kernel/net/netfilter/ipset/ip_set_hash_ipportnet.c
@@ -161,12 +161,12 @@ static int
hash_ipportnet4_uadt(struct ip_set *set, struct nlattr *tb[],
enum ipset_adt adt, u32 *lineno, u32 flags, bool retried)
{
- const struct hash_ipportnet4 *h = set->data;
+ struct hash_ipportnet4 *h = set->data;
ipset_adtfn adtfn = set->variant->adt[adt];
struct hash_ipportnet4_elem e = { .cidr = HOST_MASK - 1 };
struct ip_set_ext ext = IP_SET_INIT_UEXT(set);
u32 ip = 0, ip_to = 0, p = 0, port, port_to;
- u32 ip2_from = 0, ip2_to = 0, ip2;
+ u32 ip2_from = 0, ip2_to = 0, ip2, i = 0;
bool with_ports = false;
u8 cidr;
int ret;
@@ -280,9 +280,15 @@ hash_ipportnet4_uadt(struct ip_set *set, struct nlattr *tb[],
for (; p <= port_to; p++) {
e.port = htons(p);
do {
+ i++;
e.ip2 = htonl(ip2);
ip2 = ip_set_range_to_cidr(ip2, ip2_to, &cidr);
e.cidr = cidr - 1;
+ if (i > IPSET_MAX_RANGE) {
+ hash_ipportnet4_data_next(&h->next,
+ &e);
+ return -ERANGE;
+ }
ret = adtfn(set, &e, &ext, &ext, flags);
if (ret && !ip_set_eexist(ret, flags))
diff --git a/kernel/net/netfilter/ipset/ip_set_hash_net.c b/kernel/net/netfilter/ipset/ip_set_hash_net.c
index 54318dd..8baceea 100644
--- a/kernel/net/netfilter/ipset/ip_set_hash_net.c
+++ b/kernel/net/netfilter/ipset/ip_set_hash_net.c
@@ -137,11 +137,11 @@ static int
hash_net4_uadt(struct ip_set *set, struct nlattr *tb[],
enum ipset_adt adt, u32 *lineno, u32 flags, bool retried)
{
- const struct hash_net4 *h = set->data;
+ struct hash_net4 *h = set->data;
ipset_adtfn adtfn = set->variant->adt[adt];
struct hash_net4_elem e = { .cidr = HOST_MASK };
struct ip_set_ext ext = IP_SET_INIT_UEXT(set);
- u32 ip = 0, ip_to = 0;
+ u32 ip = 0, ip_to = 0, i = 0;
int ret;
if (tb[IPSET_ATTR_LINENO])
@@ -189,10 +189,16 @@ hash_net4_uadt(struct ip_set *set, struct nlattr *tb[],
if (ip + UINT_MAX == ip_to)
return -IPSET_ERR_HASH_RANGE;
}
+
if (retried)
ip = ntohl(h->next.ip);
do {
+ i++;
e.ip = htonl(ip);
+ if (i > IPSET_MAX_RANGE) {
+ hash_net4_data_next(&h->next, &e);
+ return -ERANGE;
+ }
ip = ip_set_range_to_cidr(ip, ip_to, &e.cidr);
ret = adtfn(set, &e, &ext, &ext, flags);
if (ret && !ip_set_eexist(ret, flags))
diff --git a/kernel/net/netfilter/ipset/ip_set_hash_netiface.c b/kernel/net/netfilter/ipset/ip_set_hash_netiface.c
index 58332ea..5baa852 100644
--- a/kernel/net/netfilter/ipset/ip_set_hash_netiface.c
+++ b/kernel/net/netfilter/ipset/ip_set_hash_netiface.c
@@ -41,7 +41,7 @@ MODULE_ALIAS("ip_set_hash:net,iface");
#define IP_SET_HASH_WITH_MULTI
#define IP_SET_HASH_WITH_NET0
-#define STRLCPY(a, b) strlcpy(a, b, IFNAMSIZ)
+#define STRSCPY(a, b) strscpy(a, b, IFNAMSIZ)
/* IPv4 variant */
@@ -183,11 +183,11 @@ hash_netiface4_kadt(struct ip_set *set, const struct sk_buff *skb,
if (!eiface)
return -EINVAL;
- STRLCPY(e.iface, eiface);
+ STRSCPY(e.iface, eiface);
e.physdev = 1;
#endif
} else {
- STRLCPY(e.iface, SRCDIR ? IFACE(in) : IFACE(out));
+ STRSCPY(e.iface, SRCDIR ? IFACE(in) : IFACE(out));
}
if (strlen(e.iface) == 0)
@@ -203,7 +203,7 @@ hash_netiface4_uadt(struct ip_set *set, struct nlattr *tb[],
ipset_adtfn adtfn = set->variant->adt[adt];
struct hash_netiface4_elem e = { .cidr = HOST_MASK, .elem = 1 };
struct ip_set_ext ext = IP_SET_INIT_UEXT(set);
- u32 ip = 0, ip_to = 0;
+ u32 ip = 0, ip_to = 0, i = 0;
int ret;
if (tb[IPSET_ATTR_LINENO])
@@ -261,7 +261,12 @@ hash_netiface4_uadt(struct ip_set *set, struct nlattr *tb[],
if (retried)
ip = ntohl(h->next.ip);
do {
+ i++;
e.ip = htonl(ip);
+ if (i > IPSET_MAX_RANGE) {
+ hash_netiface4_data_next(&h->next, &e);
+ return -ERANGE;
+ }
ip = ip_set_range_to_cidr(ip, ip_to, &e.cidr);
ret = adtfn(set, &e, &ext, &ext, flags);
@@ -396,11 +401,11 @@ hash_netiface6_kadt(struct ip_set *set, const struct sk_buff *skb,
if (!eiface)
return -EINVAL;
- STRLCPY(e.iface, eiface);
+ STRSCPY(e.iface, eiface);
e.physdev = 1;
#endif
} else {
- STRLCPY(e.iface, SRCDIR ? IFACE(in) : IFACE(out));
+ STRSCPY(e.iface, SRCDIR ? IFACE(in) : IFACE(out));
}
if (strlen(e.iface) == 0)
diff --git a/kernel/net/netfilter/ipset/ip_set_hash_netnet.c b/kernel/net/netfilter/ipset/ip_set_hash_netnet.c
index 6532f05..8fbe649 100644
--- a/kernel/net/netfilter/ipset/ip_set_hash_netnet.c
+++ b/kernel/net/netfilter/ipset/ip_set_hash_netnet.c
@@ -23,7 +23,8 @@
#define IPSET_TYPE_REV_MIN 0
/* 1 Forceadd support added */
/* 2 skbinfo support added */
-#define IPSET_TYPE_REV_MAX 3 /* bucketsize, initval support added */
+/* 3 bucketsize, initval support added */
+#define IPSET_TYPE_REV_MAX 4 /* bitmask support added */
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Oliver Smith <oliver@8.c.9.b.0.7.4.0.1.0.0.2.ip6.arpa>");
@@ -33,6 +34,8 @@ MODULE_ALIAS("ip_set_hash:net,net");
/* Type specific function prefix */
#define HTYPE hash_netnet
#define IP_SET_HASH_WITH_NETS
+#define IP_SET_HASH_WITH_NETMASK
+#define IP_SET_HASH_WITH_BITMASK
#define IPSET_NET_COUNT 2
/* IPv4 variants */
@@ -153,8 +156,8 @@ hash_netnet4_kadt(struct ip_set *set, const struct sk_buff *skb,
ip4addrptr(skb, opt->flags & IPSET_DIM_ONE_SRC, &e.ip[0]);
ip4addrptr(skb, opt->flags & IPSET_DIM_TWO_SRC, &e.ip[1]);
- e.ip[0] &= ip_set_netmask(e.cidr[0]);
- e.ip[1] &= ip_set_netmask(e.cidr[1]);
+ e.ip[0] &= (ip_set_netmask(e.cidr[0]) & h->bitmask.ip);
+ e.ip[1] &= (ip_set_netmask(e.cidr[1]) & h->bitmask.ip);
return adtfn(set, &e, &ext, &opt->ext, opt->cmdflags);
}
@@ -163,12 +166,12 @@ static int
hash_netnet4_uadt(struct ip_set *set, struct nlattr *tb[],
enum ipset_adt adt, u32 *lineno, u32 flags, bool retried)
{
- const struct hash_netnet4 *h = set->data;
+ struct hash_netnet4 *h = set->data;
ipset_adtfn adtfn = set->variant->adt[adt];
struct hash_netnet4_elem e = { };
struct ip_set_ext ext = IP_SET_INIT_UEXT(set);
u32 ip = 0, ip_to = 0;
- u32 ip2 = 0, ip2_from = 0, ip2_to = 0;
+ u32 ip2 = 0, ip2_from = 0, ip2_to = 0, i = 0;
int ret;
if (tb[IPSET_ATTR_LINENO])
@@ -212,8 +215,8 @@ hash_netnet4_uadt(struct ip_set *set, struct nlattr *tb[],
if (adt == IPSET_TEST || !(tb[IPSET_ATTR_IP_TO] ||
tb[IPSET_ATTR_IP2_TO])) {
- e.ip[0] = htonl(ip & ip_set_hostmask(e.cidr[0]));
- e.ip[1] = htonl(ip2_from & ip_set_hostmask(e.cidr[1]));
+ e.ip[0] = htonl(ip & ntohl(h->bitmask.ip) & ip_set_hostmask(e.cidr[0]));
+ e.ip[1] = htonl(ip2_from & ntohl(h->bitmask.ip) & ip_set_hostmask(e.cidr[1]));
ret = adtfn(set, &e, &ext, &ext, flags);
return ip_set_enomatch(ret, flags, adt, set) ? -ret :
ip_set_eexist(ret, flags) ? 0 : ret;
@@ -256,7 +259,12 @@ hash_netnet4_uadt(struct ip_set *set, struct nlattr *tb[],
e.ip[0] = htonl(ip);
ip = ip_set_range_to_cidr(ip, ip_to, &e.cidr[0]);
do {
+ i++;
e.ip[1] = htonl(ip2);
+ if (i > IPSET_MAX_RANGE) {
+ hash_netnet4_data_next(&h->next, &e);
+ return -ERANGE;
+ }
ip2 = ip_set_range_to_cidr(ip2, ip2_to, &e.cidr[1]);
ret = adtfn(set, &e, &ext, &ext, flags);
if (ret && !ip_set_eexist(ret, flags))
@@ -390,6 +398,11 @@ hash_netnet6_kadt(struct ip_set *set, const struct sk_buff *skb,
ip6_netmask(&e.ip[0], e.cidr[0]);
ip6_netmask(&e.ip[1], e.cidr[1]);
+ nf_inet_addr_mask_inplace(&e.ip[0], &h->bitmask);
+ nf_inet_addr_mask_inplace(&e.ip[1], &h->bitmask);
+ if (e.cidr[0] == HOST_MASK && ipv6_addr_any(&e.ip[0].in6))
+ return -EINVAL;
+
return adtfn(set, &e, &ext, &opt->ext, opt->cmdflags);
}
@@ -400,6 +413,7 @@ hash_netnet6_uadt(struct ip_set *set, struct nlattr *tb[],
ipset_adtfn adtfn = set->variant->adt[adt];
struct hash_netnet6_elem e = { };
struct ip_set_ext ext = IP_SET_INIT_UEXT(set);
+ const struct hash_netnet6 *h = set->data;
int ret;
if (tb[IPSET_ATTR_LINENO])
@@ -439,6 +453,11 @@ hash_netnet6_uadt(struct ip_set *set, struct nlattr *tb[],
ip6_netmask(&e.ip[0], e.cidr[0]);
ip6_netmask(&e.ip[1], e.cidr[1]);
+ nf_inet_addr_mask_inplace(&e.ip[0], &h->bitmask);
+ nf_inet_addr_mask_inplace(&e.ip[1], &h->bitmask);
+ if (e.cidr[0] == HOST_MASK && ipv6_addr_any(&e.ip[0].in6))
+ return -IPSET_ERR_HASH_ELEM;
+
if (tb[IPSET_ATTR_CADT_FLAGS]) {
u32 cadt_flags = ip_set_get_h32(tb[IPSET_ATTR_CADT_FLAGS]);
@@ -470,6 +489,8 @@ static struct ip_set_type hash_netnet_type __read_mostly = {
[IPSET_ATTR_RESIZE] = { .type = NLA_U8 },
[IPSET_ATTR_TIMEOUT] = { .type = NLA_U32 },
[IPSET_ATTR_CADT_FLAGS] = { .type = NLA_U32 },
+ [IPSET_ATTR_NETMASK] = { .type = NLA_U8 },
+ [IPSET_ATTR_BITMASK] = { .type = NLA_NESTED },
},
.adt_policy = {
[IPSET_ATTR_IP] = { .type = NLA_NESTED },
diff --git a/kernel/net/netfilter/ipset/ip_set_hash_netport.c b/kernel/net/netfilter/ipset/ip_set_hash_netport.c
index 2b0a1eb..8a2a7c5 100644
--- a/kernel/net/netfilter/ipset/ip_set_hash_netport.c
+++ b/kernel/net/netfilter/ipset/ip_set_hash_netport.c
@@ -155,11 +155,11 @@ static int
hash_netport4_uadt(struct ip_set *set, struct nlattr *tb[],
enum ipset_adt adt, u32 *lineno, u32 flags, bool retried)
{
- const struct hash_netport4 *h = set->data;
+ struct hash_netport4 *h = set->data;
ipset_adtfn adtfn = set->variant->adt[adt];
struct hash_netport4_elem e = { .cidr = HOST_MASK - 1 };
struct ip_set_ext ext = IP_SET_INIT_UEXT(set);
- u32 port, port_to, p = 0, ip = 0, ip_to = 0;
+ u32 port, port_to, p = 0, ip = 0, ip_to = 0, i = 0;
bool with_ports = false;
u8 cidr;
int ret;
@@ -247,8 +247,12 @@ hash_netport4_uadt(struct ip_set *set, struct nlattr *tb[],
e.ip = htonl(ip);
ip = ip_set_range_to_cidr(ip, ip_to, &cidr);
e.cidr = cidr - 1;
- for (; p <= port_to; p++) {
+ for (; p <= port_to; p++, i++) {
e.port = htons(p);
+ if (i > IPSET_MAX_RANGE) {
+ hash_netport4_data_next(&h->next, &e);
+ return -ERANGE;
+ }
ret = adtfn(set, &e, &ext, &ext, flags);
if (ret && !ip_set_eexist(ret, flags))
return ret;
diff --git a/kernel/net/netfilter/ipset/ip_set_hash_netportnet.c b/kernel/net/netfilter/ipset/ip_set_hash_netportnet.c
index 070736d..d5774a1 100644
--- a/kernel/net/netfilter/ipset/ip_set_hash_netportnet.c
+++ b/kernel/net/netfilter/ipset/ip_set_hash_netportnet.c
@@ -37,6 +37,7 @@ MODULE_ALIAS("ip_set_hash:net,port,net");
#define IP_SET_HASH_WITH_PROTO
#define IP_SET_HASH_WITH_NETS
#define IPSET_NET_COUNT 2
+#define IP_SET_HASH_WITH_NET0
/* IPv4 variant */
@@ -174,16 +175,26 @@ hash_netportnet4_kadt(struct ip_set *set, const struct sk_buff *skb,
return adtfn(set, &e, &ext, &opt->ext, opt->cmdflags);
}
+static u32
+hash_netportnet4_range_to_cidr(u32 from, u32 to, u8 *cidr)
+{
+ if (from == 0 && to == UINT_MAX) {
+ *cidr = 0;
+ return to;
+ }
+ return ip_set_range_to_cidr(from, to, cidr);
+}
+
static int
hash_netportnet4_uadt(struct ip_set *set, struct nlattr *tb[],
enum ipset_adt adt, u32 *lineno, u32 flags, bool retried)
{
- const struct hash_netportnet4 *h = set->data;
+ struct hash_netportnet4 *h = set->data;
ipset_adtfn adtfn = set->variant->adt[adt];
struct hash_netportnet4_elem e = { };
struct ip_set_ext ext = IP_SET_INIT_UEXT(set);
u32 ip = 0, ip_to = 0, p = 0, port, port_to;
- u32 ip2_from = 0, ip2_to = 0, ip2;
+ u32 ip2_from = 0, ip2_to = 0, ip2, i = 0;
bool with_ports = false;
int ret;
@@ -297,13 +308,19 @@ hash_netportnet4_uadt(struct ip_set *set, struct nlattr *tb[],
do {
e.ip[0] = htonl(ip);
- ip = ip_set_range_to_cidr(ip, ip_to, &e.cidr[0]);
+ ip = hash_netportnet4_range_to_cidr(ip, ip_to, &e.cidr[0]);
for (; p <= port_to; p++) {
e.port = htons(p);
do {
+ i++;
e.ip[1] = htonl(ip2);
- ip2 = ip_set_range_to_cidr(ip2, ip2_to,
- &e.cidr[1]);
+ if (i > IPSET_MAX_RANGE) {
+ hash_netportnet4_data_next(&h->next,
+ &e);
+ return -ERANGE;
+ }
+ ip2 = hash_netportnet4_range_to_cidr(ip2,
+ ip2_to, &e.cidr[1]);
ret = adtfn(set, &e, &ext, &ext, flags);
if (ret && !ip_set_eexist(ret, flags))
return ret;
diff --git a/kernel/net/netfilter/ipset/ip_set_list_set.c b/kernel/net/netfilter/ipset/ip_set_list_set.c
index 8c7fef8..cc2e5b9 100644
--- a/kernel/net/netfilter/ipset/ip_set_list_set.c
+++ b/kernel/net/netfilter/ipset/ip_set_list_set.c
@@ -429,9 +429,6 @@ list_set_destroy(struct ip_set *set)
struct list_set *map = set->data;
struct set_elem *e, *n;
- if (SET_WITH_TIMEOUT(set))
- del_timer_sync(&map->gc);
-
list_for_each_entry_safe(e, n, &map->members, list) {
list_del(&e->list);
ip_set_put_byindex(map->net, e->id);
@@ -548,6 +545,15 @@ list_set_same_set(const struct ip_set *a, const struct ip_set *b)
a->extensions == b->extensions;
}
+static void
+list_set_cancel_gc(struct ip_set *set)
+{
+ struct list_set *map = set->data;
+
+ if (SET_WITH_TIMEOUT(set))
+ timer_shutdown_sync(&map->gc);
+}
+
static const struct ip_set_type_variant set_variant = {
.kadt = list_set_kadt,
.uadt = list_set_uadt,
@@ -561,6 +567,7 @@ static const struct ip_set_type_variant set_variant = {
.head = list_set_head,
.list = list_set_list,
.same_set = list_set_same_set,
+ .cancel_gc = list_set_cancel_gc,
};
static void
diff --git a/kernel/net/sched/em_ipset.c b/kernel/net/sched/em_ipset.c
index 96fd4a3..5428f8d 100644
--- a/kernel/net/sched/em_ipset.c
+++ b/kernel/net/sched/em_ipset.c
@@ -1,11 +1,8 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* net/sched/em_ipset.c ipset ematch
*
* Copyright (c) 2012 Florian Westphal <fw@strlen.de>
- *
- * 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 <linux/gfp.h>
@@ -87,7 +84,7 @@ static int em_ipset_match(struct sk_buff *skb, struct tcf_ematch *em,
#else
#define ACPAR_FAMILY(f) acpar.family = f
#endif
- switch (tc_skb_protocol(skb)) {
+ switch (skb_protocol(skb, true)) {
case htons(ETH_P_IP):
ACPAR_FAMILY(NFPROTO_IPV4);
if (!pskb_network_may_pull(skb, sizeof(struct iphdr)))
diff --git a/lib/Makefile.am b/lib/Makefile.am
index 3a82417..a9edf95 100644
--- a/lib/Makefile.am
+++ b/lib/Makefile.am
@@ -46,7 +46,6 @@ EXTRA_libipset_la_SOURCES = \
EXTRA_DIST = $(IPSET_SETTYPE_LIST) libipset.map
-pkgconfigdir = $(libdir)/pkgconfig
pkgconfig_DATA = libipset.pc
dist_man_MANS = libipset.3
diff --git a/lib/args.c b/lib/args.c
index bab3b13..e47105c 100644
--- a/lib/args.c
+++ b/lib/args.c
@@ -300,6 +300,14 @@ static const struct ipset_arg ipset_args[] = {
.print = ipset_print_hexnumber,
.help = "[initval VALUE]",
},
+ [IPSET_ARG_BITMASK] = {
+ .name = { "bitmask", NULL },
+ .has_arg = IPSET_MANDATORY_ARG,
+ .opt = IPSET_OPT_BITMASK,
+ .parse = ipset_parse_bitmask,
+ .print = ipset_print_ip,
+ .help = "[bitmask bitmask]",
+ },
};
const struct ipset_arg *
diff --git a/lib/data.c b/lib/data.c
index 7720178..c05b201 100644
--- a/lib/data.c
+++ b/lib/data.c
@@ -53,6 +53,7 @@ struct ipset_data {
uint8_t bucketsize;
uint8_t resize;
uint8_t netmask;
+ union nf_inet_addr bitmask;
uint32_t hashsize;
uint32_t maxelem;
uint32_t markmask;
@@ -110,7 +111,7 @@ ipset_strlcpy(char *dst, const char *src, size_t len)
assert(dst);
assert(src);
- strncpy(dst, src, len);
+ memcpy(dst, src, len);
dst[len - 1] = '\0';
}
@@ -301,6 +302,12 @@ ipset_data_set(struct ipset_data *data, enum ipset_opt opt, const void *value)
case IPSET_OPT_NETMASK:
data->create.netmask = *(const uint8_t *) value;
break;
+ case IPSET_OPT_BITMASK:
+ if (!(data->family == NFPROTO_IPV4 ||
+ data->family == NFPROTO_IPV6))
+ return -1;
+ copy_addr(data->family, &data->create.bitmask, value);
+ break;
case IPSET_OPT_BUCKETSIZE:
data->create.bucketsize = *(const uint8_t *) value;
break;
@@ -508,6 +515,8 @@ ipset_data_get(const struct ipset_data *data, enum ipset_opt opt)
return &data->create.markmask;
case IPSET_OPT_NETMASK:
return &data->create.netmask;
+ case IPSET_OPT_BITMASK:
+ return &data->create.bitmask;
case IPSET_OPT_BUCKETSIZE:
return &data->create.bucketsize;
case IPSET_OPT_RESIZE:
@@ -594,6 +603,7 @@ ipset_data_sizeof(enum ipset_opt opt, uint8_t family)
case IPSET_OPT_IP_TO:
case IPSET_OPT_IP2:
case IPSET_OPT_IP2_TO:
+ case IPSET_OPT_BITMASK:
return family == NFPROTO_IPV4 ? sizeof(uint32_t)
: sizeof(struct in6_addr);
case IPSET_OPT_MARK:
diff --git a/lib/debug.c b/lib/debug.c
index bf57a41..dbc5cfb 100644
--- a/lib/debug.c
+++ b/lib/debug.c
@@ -40,6 +40,7 @@ static const struct ipset_attrname createattr2name[] = {
[IPSET_ATTR_MAXELEM] = { .name = "MAXELEM" },
[IPSET_ATTR_MARKMASK] = { .name = "MARKMASK" },
[IPSET_ATTR_NETMASK] = { .name = "NETMASK" },
+ [IPSET_ATTR_BITMASK] = { .name = "BITMASK" },
[IPSET_ATTR_BUCKETSIZE] = { .name = "BUCKETSIZE" },
[IPSET_ATTR_RESIZE] = { .name = "RESIZE" },
[IPSET_ATTR_SIZE] = { .name = "SIZE" },
diff --git a/lib/errcode.c b/lib/errcode.c
index b38f95e..49c97a1 100644
--- a/lib/errcode.c
+++ b/lib/errcode.c
@@ -25,6 +25,8 @@ static const struct ipset_errcode_table core_errcode_table[] = {
"The set with the given name does not exist" },
{ EMSGSIZE, 0,
"Kernel error received: message could not be created" },
+ { ERANGE, 0,
+ "The specified range is too large, split it up into smaller ranges" },
{ IPSET_ERR_PROTOCOL, 0,
"Kernel error received: ipset protocol error" },
@@ -42,6 +44,8 @@ static const struct ipset_errcode_table core_errcode_table[] = {
"The value of the markmask parameter is invalid" },
{ IPSET_ERR_INVALID_FAMILY, 0,
"Protocol family not supported by the set type" },
+ { IPSET_ERR_BITMASK_NETMASK_EXCL, 0,
+ "netmask and bitmask options are mutually exclusive, provide only one" },
/* DESTROY specific error codes */
{ IPSET_ERR_BUSY, IPSET_CMD_DESTROY,
diff --git a/lib/ipset.c b/lib/ipset.c
index 6729919..c910d88 100644
--- a/lib/ipset.c
+++ b/lib/ipset.c
@@ -13,6 +13,7 @@
#include <stdio.h> /* printf */
#include <stdlib.h> /* exit */
#include <string.h> /* str* */
+#include <inttypes.h> /* PRIu64 */
#include <config.h>
@@ -28,6 +29,8 @@
#include <libipset/utils.h> /* STREQ */
#include <libipset/ipset.h> /* prototypes */
#include <libipset/ip_set_compiler.h> /* compiler attributes */
+#include <libipset/list_sort.h> /* lists */
+#include <libipset/xlate.h> /* ipset_xlate_argv */
static char program_name[] = PACKAGE;
static char program_version[] = PACKAGE_VERSION;
@@ -50,6 +53,17 @@ struct ipset {
char *newargv[MAX_ARGS];
int newargc;
const char *filename; /* Input/output filename */
+ bool xlate;
+ struct list_head xlate_sets;
+};
+
+struct ipset_xlate_set {
+ struct list_head list;
+ char name[IPSET_MAXNAMELEN];
+ uint8_t netmask;
+ uint8_t family;
+ bool interval;
+ const struct ipset_type *type;
};
/* Commands and environment options */
@@ -221,7 +235,7 @@ const struct ipset_envopts ipset_envopts[] = {
{ .name = { "-o", "-output" },
.has_arg = IPSET_MANDATORY_ARG, .flag = IPSET_OPT_MAX,
.parse = ipset_parse_output,
- .help = "plain|save|xml\n"
+ .help = "plain|save|xml|json\n"
" Specify output mode for listing sets.\n"
" Default value for \"list\" command is mode \"plain\"\n"
" and for \"save\" command is mode \"save\".",
@@ -415,6 +429,8 @@ ipset_parse_output(struct ipset *ipset,
return ipset_session_output(session, IPSET_LIST_PLAIN);
else if (STREQ(str, "xml"))
return ipset_session_output(session, IPSET_LIST_XML);
+ else if (STREQ(str, "json"))
+ return ipset_session_output(session, IPSET_LIST_JSON);
else if (STREQ(str, "save"))
return ipset_session_output(session, IPSET_LIST_SAVE);
@@ -923,20 +939,21 @@ static const char *cmd_prefix[] = {
[IPSET_TEST] = "test SETNAME",
};
-/* Workhorses */
+static struct ipset_xlate_set *
+ipset_xlate_set_get(struct ipset *ipset, const char *name)
+{
+ struct ipset_xlate_set *set;
-/**
- * ipset_parse_argv - parse and argv array and execute the command
- * @ipset: ipset structure
- * @argc: length of the array
- * @argv: array of strings
- *
- * Parse an array of strings and execute the ipset command.
- *
- * Returns 0 on success or a negative error code.
- */
-int
-ipset_parse_argv(struct ipset *ipset, int oargc, char *oargv[])
+ list_for_each_entry(set, &ipset->xlate_sets, list) {
+ if (!strcmp(set->name, name))
+ return set;
+ }
+
+ return NULL;
+}
+
+static int
+ipset_parser(struct ipset *ipset, int oargc, char *oargv[])
{
int ret = 0;
enum ipset_cmd cmd = IPSET_CMD_NONE;
@@ -944,7 +961,7 @@ ipset_parse_argv(struct ipset *ipset, int oargc, char *oargv[])
char *arg0 = NULL, *arg1 = NULL;
const struct ipset_envopts *opt;
const struct ipset_commands *command;
- const struct ipset_type *type;
+ const struct ipset_type *type = NULL;
struct ipset_session *session = ipset->session;
void *p = ipset_session_printf_private(session);
int argc = oargc;
@@ -1113,6 +1130,7 @@ ipset_parse_argv(struct ipset *ipset, int oargc, char *oargv[])
if (arg0) {
const struct ipset_arg *arg;
int k;
+ enum ipset_adt c;
/* Type-specific help, without kernel checking */
type = type_find(arg0);
@@ -1122,11 +1140,11 @@ ipset_parse_argv(struct ipset *ipset, int oargc, char *oargv[])
"Unknown settype: `%s'", arg0);
printf("\n%s type specific options:\n\n", type->name);
for (i = 0; cmd_help_order[i] != IPSET_CADT_MAX; i++) {
- cmd = cmd_help_order[i];
+ c = cmd_help_order[i];
printf("%s %s %s\n",
- cmd_prefix[cmd], type->name, type->cmd[cmd].help);
- for (k = 0; type->cmd[cmd].args[k] != IPSET_ARG_NONE; k++) {
- arg = ipset_keyword(type->cmd[cmd].args[k]);
+ cmd_prefix[c], type->name, type->cmd[c].help);
+ for (k = 0; type->cmd[c].args[k] != IPSET_ARG_NONE; k++) {
+ arg = ipset_keyword(type->cmd[c].args[k]);
if (!arg->help || arg->help[0] == '\0')
continue;
printf(" %s\n", arg->help);
@@ -1243,7 +1261,7 @@ ipset_parse_argv(struct ipset *ipset, int oargc, char *oargv[])
return ipset->custom_error(ipset,
p, IPSET_PARAMETER_PROBLEM,
"Unknown argument %s", argv[1]);
- return restore(ipset);
+ return IPSET_CMD_RESTORE;
case IPSET_CMD_ADD:
case IPSET_CMD_DEL:
case IPSET_CMD_TEST:
@@ -1253,7 +1271,20 @@ ipset_parse_argv(struct ipset *ipset, int oargc, char *oargv[])
if (ret < 0)
return ipset->standard_error(ipset, p);
- type = ipset_type_get(session, cmd);
+ if (!ipset->xlate) {
+ type = ipset_type_get(session, cmd);
+ } else {
+ const struct ipset_xlate_set *xlate_set;
+
+ xlate_set = ipset_xlate_set_get(ipset, arg0);
+ if (xlate_set) {
+ ipset_session_data_set(session, IPSET_OPT_TYPE,
+ xlate_set->type);
+ ipset_session_data_set(session, IPSET_OPT_FAMILY,
+ &xlate_set->family);
+ type = xlate_set->type;
+ }
+ }
if (type == NULL)
return ipset->standard_error(ipset, p);
@@ -1280,6 +1311,37 @@ ipset_parse_argv(struct ipset *ipset, int oargc, char *oargv[])
if (argc > 1)
return ipset->custom_error(ipset, p, IPSET_PARAMETER_PROBLEM,
"Unknown argument %s", argv[1]);
+
+ return cmd;
+}
+
+/* Workhorses */
+
+/**
+ * ipset_parse_argv - parse and argv array and execute the command
+ * @ipset: ipset structure
+ * @argc: length of the array
+ * @argv: array of strings
+ *
+ * Parse an array of strings and execute the ipset command.
+ *
+ * Returns 0 on success or a negative error code.
+ */
+int
+ipset_parse_argv(struct ipset *ipset, int oargc, char *oargv[])
+{
+ struct ipset_session *session = ipset->session;
+ void *p = ipset_session_printf_private(session);
+ enum ipset_cmd cmd;
+ int ret;
+
+ cmd = ipset_parser(ipset, oargc, oargv);
+ if (cmd < 0)
+ return cmd;
+
+ if (cmd == IPSET_CMD_RESTORE)
+ return restore(ipset);
+
ret = ipset_cmd(session, cmd, ipset->restore_line);
D("ret %d", ret);
/* In the case of warning, the return code is success */
@@ -1455,6 +1517,9 @@ ipset_init(void)
return NULL;
}
ipset_custom_printf(ipset, NULL, NULL, NULL, NULL);
+
+ INIT_LIST_HEAD(&ipset->xlate_sets);
+
return ipset;
}
@@ -1469,6 +1534,8 @@ ipset_init(void)
int
ipset_fini(struct ipset *ipset)
{
+ struct ipset_xlate_set *xlate_set, *next;
+
assert(ipset);
if (ipset->session)
@@ -1477,6 +1544,498 @@ ipset_fini(struct ipset *ipset)
if (ipset->newargv[0])
free(ipset->newargv[0]);
+ list_for_each_entry_safe(xlate_set, next, &ipset->xlate_sets, list)
+ free(xlate_set);
+
free(ipset);
return 0;
}
+
+/* Ignore the set family, use inet. */
+static const char *ipset_xlate_family(uint8_t family UNUSED)
+{
+ return "inet";
+}
+
+enum ipset_xlate_set_type {
+ IPSET_XLATE_TYPE_UNKNOWN = 0,
+ IPSET_XLATE_TYPE_HASH_MAC,
+ IPSET_XLATE_TYPE_HASH_IP,
+ IPSET_XLATE_TYPE_HASH_IP_MAC,
+ IPSET_XLATE_TYPE_HASH_NET_IFACE,
+ IPSET_XLATE_TYPE_HASH_NET_PORT,
+ IPSET_XLATE_TYPE_HASH_NET_PORT_NET,
+ IPSET_XLATE_TYPE_HASH_NET_NET,
+ IPSET_XLATE_TYPE_HASH_NET,
+ IPSET_XLATE_TYPE_HASH_IP_PORT_NET,
+ IPSET_XLATE_TYPE_HASH_IP_PORT_IP,
+ IPSET_XLATE_TYPE_HASH_IP_MARK,
+ IPSET_XLATE_TYPE_HASH_IP_PORT,
+ IPSET_XLATE_TYPE_BITMAP_PORT,
+ IPSET_XLATE_TYPE_BITMAP_IP_MAC,
+ IPSET_XLATE_TYPE_BITMAP_IP,
+};
+
+static enum ipset_xlate_set_type ipset_xlate_set_type(const char *typename)
+{
+ if (!strcmp(typename, "hash:mac"))
+ return IPSET_XLATE_TYPE_HASH_MAC;
+ else if (!strcmp(typename, "hash:ip"))
+ return IPSET_XLATE_TYPE_HASH_IP;
+ else if (!strcmp(typename, "hash:ip,mac"))
+ return IPSET_XLATE_TYPE_HASH_IP_MAC;
+ else if (!strcmp(typename, "hash:net,iface"))
+ return IPSET_XLATE_TYPE_HASH_NET_IFACE;
+ else if (!strcmp(typename, "hash:net,port"))
+ return IPSET_XLATE_TYPE_HASH_NET_PORT;
+ else if (!strcmp(typename, "hash:net,port,net"))
+ return IPSET_XLATE_TYPE_HASH_NET_PORT_NET;
+ else if (!strcmp(typename, "hash:net,net"))
+ return IPSET_XLATE_TYPE_HASH_NET_NET;
+ else if (!strcmp(typename, "hash:net"))
+ return IPSET_XLATE_TYPE_HASH_NET;
+ else if (!strcmp(typename, "hash:ip,port,net"))
+ return IPSET_XLATE_TYPE_HASH_IP_PORT_NET;
+ else if (!strcmp(typename, "hash:ip,port,ip"))
+ return IPSET_XLATE_TYPE_HASH_IP_PORT_IP;
+ else if (!strcmp(typename, "hash:ip,mark"))
+ return IPSET_XLATE_TYPE_HASH_IP_MARK;
+ else if (!strcmp(typename, "hash:ip,port"))
+ return IPSET_XLATE_TYPE_HASH_IP_PORT;
+ else if (!strcmp(typename, "hash:ip"))
+ return IPSET_XLATE_TYPE_HASH_IP;
+ else if (!strcmp(typename, "bitmap:port"))
+ return IPSET_XLATE_TYPE_BITMAP_PORT;
+ else if (!strcmp(typename, "bitmap:ip,mac"))
+ return IPSET_XLATE_TYPE_BITMAP_IP_MAC;
+ else if (!strcmp(typename, "bitmap:ip"))
+ return IPSET_XLATE_TYPE_BITMAP_IP;
+
+ return IPSET_XLATE_TYPE_UNKNOWN;
+}
+
+#define NFT_SET_INTERVAL (1 << 0)
+
+static const char *
+ipset_xlate_type_to_nftables(int family, enum ipset_xlate_set_type type,
+ uint32_t *flags)
+{
+ switch (type) {
+ case IPSET_XLATE_TYPE_HASH_MAC:
+ return "ether_addr";
+ case IPSET_XLATE_TYPE_HASH_IP:
+ if (family == AF_INET)
+ return "ipv4_addr";
+ else if (family == AF_INET6)
+ return "ipv6_addr";
+ break;
+ case IPSET_XLATE_TYPE_HASH_IP_MAC:
+ if (family == AF_INET)
+ return "ipv4_addr . ether_addr";
+ else if (family == AF_INET6)
+ return "ipv6_addr . ether_addr";
+ break;
+ case IPSET_XLATE_TYPE_HASH_NET_IFACE:
+ *flags |= NFT_SET_INTERVAL;
+ if (family == AF_INET)
+ return "ipv4_addr . ifname";
+ else if (family == AF_INET6)
+ return "ipv6_addr . ifname";
+ break;
+ case IPSET_XLATE_TYPE_HASH_NET_PORT:
+ *flags |= NFT_SET_INTERVAL;
+ if (family == AF_INET)
+ return "ipv4_addr . inet_proto . inet_service";
+ else if (family == AF_INET6)
+ return "ipv6_addr . inet_proto . inet_service";
+ break;
+ case IPSET_XLATE_TYPE_HASH_NET_PORT_NET:
+ *flags |= NFT_SET_INTERVAL;
+ if (family == AF_INET)
+ return "ipv4_addr . inet_proto . inet_service . ipv4_addr";
+ else if (family == AF_INET6)
+ return "ipv6_addr . inet_proto . inet_service . ipv6_addr";
+ break;
+ case IPSET_XLATE_TYPE_HASH_NET_NET:
+ *flags |= NFT_SET_INTERVAL;
+ if (family == AF_INET)
+ return "ipv4_addr . ipv4_addr";
+ else if (family == AF_INET6)
+ return "ipv6_addr . ipv6_addr";
+ break;
+ case IPSET_XLATE_TYPE_HASH_NET:
+ *flags |= NFT_SET_INTERVAL;
+ if (family == AF_INET)
+ return "ipv4_addr";
+ else if (family == AF_INET6)
+ return "ipv6_addr";
+ break;
+ case IPSET_XLATE_TYPE_HASH_IP_PORT_NET:
+ *flags |= NFT_SET_INTERVAL;
+ if (family == AF_INET)
+ return "ipv4_addr . inet_proto . inet_service . ipv4_addr";
+ else if (family == AF_INET6)
+ return "ipv6_addr . inet_proto . inet_service . ipv6_addr";
+ break;
+ case IPSET_XLATE_TYPE_HASH_IP_PORT_IP:
+ if (family == AF_INET)
+ return "ipv4_addr . inet_proto . inet_service . ipv4_addr";
+ else if (family == AF_INET6)
+ return "ipv6_addr . inet_proto . inet_service . ipv6_addr";
+ break;
+ case IPSET_XLATE_TYPE_HASH_IP_MARK:
+ if (family == AF_INET)
+ return "ipv4_addr . mark";
+ else if (family == AF_INET6)
+ return "ipv6_addr . mark";
+ break;
+ case IPSET_XLATE_TYPE_HASH_IP_PORT:
+ if (family == AF_INET)
+ return "ipv4_addr . inet_proto . inet_service";
+ else if (family == AF_INET6)
+ return "ipv6_addr . inet_proto . inet_service";
+ break;
+ case IPSET_XLATE_TYPE_BITMAP_PORT:
+ return "inet_service";
+ case IPSET_XLATE_TYPE_BITMAP_IP_MAC:
+ if (family == AF_INET)
+ return "ipv4_addr . ether_addr";
+ else if (family == AF_INET6)
+ return "ipv6_addr . ether_addr";
+ break;
+ case IPSET_XLATE_TYPE_BITMAP_IP:
+ if (family == AF_INET)
+ return "ipv4_addr";
+ else if (family == AF_INET6)
+ return "ipv6_addr";
+ break;
+ case IPSET_XLATE_TYPE_UNKNOWN:
+ break;
+ default:
+ break;
+ }
+ /* This should not ever happen. */
+ return "unknown";
+}
+
+static int ipset_xlate(struct ipset *ipset, enum ipset_cmd cmd,
+ const char *table)
+{
+ const char *set, *typename, *nft_type;
+ const struct ipset_type *ipset_type;
+ struct ipset_xlate_set *xlate_set;
+ enum ipset_xlate_set_type type;
+ struct ipset_session *session;
+ const uint32_t *cadt_flags;
+ const uint32_t *timeout;
+ const uint32_t *maxelem;
+ struct ipset_data *data;
+ const uint8_t *netmask;
+ const char *comment;
+ uint32_t flags = 0;
+ uint8_t family;
+ char buf[64];
+ bool concat;
+ char *term;
+
+ session = ipset_session(ipset);
+ data = ipset_session_data(session);
+
+ set = ipset_data_get(data, IPSET_SETNAME);
+ family = ipset_data_family(data);
+
+ switch (cmd) {
+ case IPSET_CMD_CREATE:
+ /* Not supported. */
+ if (ipset_data_test(data, IPSET_OPT_MARKMASK)) {
+ printf("# %s", ipset->cmdline);
+ break;
+ }
+ cadt_flags = ipset_data_get(data, IPSET_OPT_CADT_FLAGS);
+
+ /* Ignore:
+ * - IPSET_FLAG_WITH_COMMENT
+ * - IPSET_FLAG_WITH_FORCEADD
+ */
+ if (cadt_flags &&
+ (*cadt_flags & (IPSET_FLAG_BEFORE |
+ IPSET_FLAG_PHYSDEV |
+ IPSET_FLAG_NOMATCH |
+ IPSET_FLAG_WITH_SKBINFO |
+ IPSET_FLAG_IFACE_WILDCARD))) {
+ printf("# %s", ipset->cmdline);
+ break;
+ }
+
+ typename = ipset_data_get(data, IPSET_OPT_TYPENAME);
+ type = ipset_xlate_set_type(typename);
+ nft_type = ipset_xlate_type_to_nftables(family, type, &flags);
+
+ printf("add set %s %s %s { type %s; ",
+ ipset_xlate_family(family), table, set, nft_type);
+ if (cadt_flags) {
+ if (*cadt_flags & IPSET_FLAG_WITH_COUNTERS)
+ printf("counter; ");
+ }
+ timeout = ipset_data_get(data, IPSET_OPT_TIMEOUT);
+ if (timeout)
+ printf("timeout %us; ", *timeout);
+ maxelem = ipset_data_get(data, IPSET_OPT_MAXELEM);
+ if (maxelem)
+ printf("size %u; ", *maxelem);
+
+ netmask = ipset_data_get(data, IPSET_OPT_NETMASK);
+ if (netmask &&
+ ((family == AF_INET && *netmask < 32) ||
+ (family == AF_INET6 && *netmask < 128)))
+ flags |= NFT_SET_INTERVAL;
+
+ if (flags & NFT_SET_INTERVAL)
+ printf("flags interval; ");
+
+ /* These create-specific options are safe to be ignored:
+ * - IPSET_OPT_GC
+ * - IPSET_OPT_HASHSIZE
+ * - IPSET_OPT_PROBES
+ * - IPSET_OPT_RESIZE
+ * - IPSET_OPT_SIZE
+ * - IPSET_OPT_FORCEADD
+ *
+ * Ranges and CIDR are safe to be ignored too:
+ * - IPSET_OPT_IP_FROM
+ * - IPSET_OPT_IP_TO
+ * - IPSET_OPT_PORT_FROM
+ * - IPSET_OPT_PORT_TO
+ */
+
+ printf("}\n");
+
+ xlate_set = calloc(1, sizeof(*xlate_set));
+ if (!xlate_set)
+ return -1;
+
+ snprintf(xlate_set->name, sizeof(xlate_set->name), "%s", set);
+ ipset_type = ipset_types();
+ while (ipset_type) {
+ if (!strcmp(ipset_type->name, typename))
+ break;
+ ipset_type = ipset_type->next;
+ }
+
+ xlate_set->family = family;
+ xlate_set->type = ipset_type;
+ if (netmask) {
+ xlate_set->netmask = *netmask;
+ xlate_set->interval = true;
+ }
+ list_add_tail(&xlate_set->list, &ipset->xlate_sets);
+ break;
+ case IPSET_CMD_DESTROY:
+ printf("del set %s %s %s\n",
+ ipset_xlate_family(family), table, set);
+ break;
+ case IPSET_CMD_FLUSH:
+ if (!set) {
+ printf("# %s", ipset->cmdline);
+ } else {
+ printf("flush set %s %s %s\n",
+ ipset_xlate_family(family), table, set);
+ }
+ break;
+ case IPSET_CMD_RENAME:
+ printf("# %s", ipset->cmdline);
+ return -1;
+ case IPSET_CMD_SWAP:
+ printf("# %s", ipset->cmdline);
+ return -1;
+ case IPSET_CMD_LIST:
+ if (!set) {
+ printf("list sets %s %s\n",
+ ipset_xlate_family(family), table);
+ } else {
+ printf("list set %s %s %s\n",
+ ipset_xlate_family(family), table, set);
+ }
+ break;
+ case IPSET_CMD_SAVE:
+ printf("# %s", ipset->cmdline);
+ return -1;
+ case IPSET_CMD_ADD:
+ case IPSET_CMD_DEL:
+ case IPSET_CMD_TEST:
+ /* Not supported. */
+ if (ipset_data_test(data, IPSET_OPT_NOMATCH) ||
+ ipset_data_test(data, IPSET_OPT_SKBINFO) ||
+ ipset_data_test(data, IPSET_OPT_SKBMARK) ||
+ ipset_data_test(data, IPSET_OPT_SKBPRIO) ||
+ ipset_data_test(data, IPSET_OPT_SKBQUEUE) ||
+ ipset_data_test(data, IPSET_OPT_IFACE_WILDCARD)) {
+ printf("# %s", ipset->cmdline);
+ break;
+ }
+ printf("%s element %s %s %s { ",
+ cmd == IPSET_CMD_ADD ? "add" :
+ cmd == IPSET_CMD_DEL ? "delete" : "get",
+ ipset_xlate_family(family), table, set);
+
+ xlate_set = (struct ipset_xlate_set *)
+ ipset_xlate_set_get(ipset, set);
+ if (xlate_set && xlate_set->interval)
+ netmask = &xlate_set->netmask;
+ else
+ netmask = NULL;
+
+ concat = false;
+ if (ipset_data_test(data, IPSET_OPT_IP)) {
+ ipset_print_data(buf, sizeof(buf), data, IPSET_OPT_IP, 0);
+ printf("%s", buf);
+ if (netmask)
+ printf("/%u ", *netmask);
+ else
+ printf(" ");
+
+ concat = true;
+ }
+ if (ipset_data_test(data, IPSET_OPT_MARK)) {
+ ipset_print_mark(buf, sizeof(buf), data, IPSET_OPT_MARK, 0);
+ printf("%s%s ", concat ? ". " : "", buf);
+ }
+ if (ipset_data_test(data, IPSET_OPT_IFACE)) {
+ ipset_print_data(buf, sizeof(buf), data, IPSET_OPT_IFACE, 0);
+ printf("%s%s ", concat ? ". " : "", buf);
+ }
+ if (ipset_data_test(data, IPSET_OPT_ETHER)) {
+ ipset_print_ether(buf, sizeof(buf), data, IPSET_OPT_ETHER, 0);
+ size_t i;
+
+ for (i = 0; i < strlen(buf); i++)
+ buf[i] = tolower(buf[i]);
+
+ printf("%s%s ", concat ? ". " : "", buf);
+ concat = true;
+ }
+ if (ipset_data_test(data, IPSET_OPT_PORT)) {
+ ipset_print_proto_port(buf, sizeof(buf), data, IPSET_OPT_PORT, 0);
+ term = strchr(buf, ':');
+ if (term) {
+ *term = '\0';
+ printf("%s%s ", concat ? ". " : "", buf);
+ }
+ ipset_print_data(buf, sizeof(buf), data, IPSET_OPT_PORT, 0);
+ printf("%s%s ", concat ? ". " : "", buf);
+ }
+ if (ipset_data_test(data, IPSET_OPT_IP2)) {
+ ipset_print_ip(buf, sizeof(buf), data, IPSET_OPT_IP2, 0);
+ printf("%s%s", concat ? ". " : "", buf);
+ if (netmask)
+ printf("/%u ", *netmask);
+ else
+ printf(" ");
+ }
+ if (ipset_data_test(data, IPSET_OPT_PACKETS) &&
+ ipset_data_test(data, IPSET_OPT_BYTES)) {
+ const uint64_t *pkts, *bytes;
+
+ pkts = ipset_data_get(data, IPSET_OPT_PACKETS);
+ bytes = ipset_data_get(data, IPSET_OPT_BYTES);
+
+ printf("counter packets %" PRIu64 " bytes %" PRIu64 " ",
+ *pkts, *bytes);
+ }
+ timeout = ipset_data_get(data, IPSET_OPT_TIMEOUT);
+ if (timeout)
+ printf("timeout %us ", *timeout);
+
+ comment = ipset_data_get(data, IPSET_OPT_ADT_COMMENT);
+ if (comment)
+ printf("comment \"%s\" ", comment);
+
+ printf("}\n");
+ break;
+ case IPSET_CMD_GET_BYNAME:
+ printf("# %s", ipset->cmdline);
+ return -1;
+ case IPSET_CMD_GET_BYINDEX:
+ printf("# %s", ipset->cmdline);
+ return -1;
+ default:
+ break;
+ }
+
+ return 0;
+}
+
+static int ipset_xlate_restore(struct ipset *ipset)
+{
+ struct ipset_session *session = ipset_session(ipset);
+ struct ipset_data *data = ipset_session_data(session);
+ void *p = ipset_session_printf_private(session);
+ enum ipset_cmd cmd;
+ FILE *f = stdin;
+ int ret = 0;
+ char *c;
+
+ if (ipset->filename) {
+ f = fopen(ipset->filename, "r");
+ if (!f) {
+ fprintf(stderr, "cannot open file `%s'\n", ipset->filename);
+ return -1;
+ }
+ }
+
+ /* TODO: Allow to specify the table name other than 'global'. */
+ printf("add table inet global\n");
+
+ while (fgets(ipset->cmdline, sizeof(ipset->cmdline), f)) {
+ ipset->restore_line++;
+ c = ipset->cmdline;
+ while (isspace(c[0]))
+ c++;
+ if (c[0] == '\0' || c[0] == '#')
+ continue;
+ else if (STREQ(c, "COMMIT\n") || STREQ(c, "COMMIT\r\n"))
+ continue;
+
+ ret = build_argv(ipset, c);
+ if (ret < 0)
+ break;
+
+ cmd = ipset_parser(ipset, ipset->newargc, ipset->newargv);
+ if (cmd < 0)
+ ipset->standard_error(ipset, p);
+
+ /* TODO: Allow to specify the table name other than 'global'. */
+ ret = ipset_xlate(ipset, cmd, "global");
+ if (ret < 0)
+ break;
+
+ ipset_data_reset(data);
+ }
+
+ if (ipset->filename)
+ fclose(f);
+
+ return ret;
+}
+
+int ipset_xlate_argv(struct ipset *ipset, int argc, char *argv[])
+{
+ enum ipset_cmd cmd;
+ int ret;
+
+ ipset->xlate = true;
+
+ cmd = ipset_parser(ipset, argc, argv);
+ if (cmd < 0)
+ return cmd;
+
+ if (cmd == IPSET_CMD_RESTORE) {
+ ret = ipset_xlate_restore(ipset);
+ } else {
+ fprintf(stderr, "This command is not supported, "
+ "use `ipset-translate restore < file'\n");
+ ret = -1;
+ }
+
+ return ret;
+}
diff --git a/lib/ipset_hash_ip.c b/lib/ipset_hash_ip.c
index ea85700..4f96ebb 100644
--- a/lib/ipset_hash_ip.c
+++ b/lib/ipset_hash_ip.c
@@ -477,6 +477,91 @@ static struct ipset_type ipset_hash_ip5 = {
.description = "bucketsize, initval support",
};
+/* bitmask support */
+static struct ipset_type ipset_hash_ip6 = {
+ .name = "hash:ip",
+ .alias = { "iphash", NULL },
+ .revision = 6,
+ .family = NFPROTO_IPSET_IPV46,
+ .dimension = IPSET_DIM_ONE,
+ .elem = {
+ [IPSET_DIM_ONE - 1] = {
+ .parse = ipset_parse_ip4_single6,
+ .print = ipset_print_ip,
+ .opt = IPSET_OPT_IP
+ },
+ },
+ .cmd = {
+ [IPSET_CREATE] = {
+ .args = {
+ IPSET_ARG_FAMILY,
+ /* Aliases */
+ IPSET_ARG_INET,
+ IPSET_ARG_INET6,
+ IPSET_ARG_HASHSIZE,
+ IPSET_ARG_MAXELEM,
+ IPSET_ARG_NETMASK,
+ IPSET_ARG_BITMASK,
+ IPSET_ARG_TIMEOUT,
+ IPSET_ARG_COUNTERS,
+ IPSET_ARG_COMMENT,
+ IPSET_ARG_FORCEADD,
+ IPSET_ARG_SKBINFO,
+ IPSET_ARG_BUCKETSIZE,
+ IPSET_ARG_INITVAL,
+ /* Ignored options: backward compatibilty */
+ IPSET_ARG_PROBES,
+ IPSET_ARG_RESIZE,
+ IPSET_ARG_GC,
+ IPSET_ARG_NONE,
+ },
+ .need = 0,
+ .full = 0,
+ .help = "",
+ },
+ [IPSET_ADD] = {
+ .args = {
+ IPSET_ARG_TIMEOUT,
+ IPSET_ARG_PACKETS,
+ IPSET_ARG_BYTES,
+ IPSET_ARG_ADT_COMMENT,
+ IPSET_ARG_SKBMARK,
+ IPSET_ARG_SKBPRIO,
+ IPSET_ARG_SKBQUEUE,
+ IPSET_ARG_NONE,
+ },
+ .need = IPSET_FLAG(IPSET_OPT_IP),
+ .full = IPSET_FLAG(IPSET_OPT_IP)
+ | IPSET_FLAG(IPSET_OPT_IP_TO),
+ .help = "IP",
+ },
+ [IPSET_DEL] = {
+ .args = {
+ IPSET_ARG_NONE,
+ },
+ .need = IPSET_FLAG(IPSET_OPT_IP),
+ .full = IPSET_FLAG(IPSET_OPT_IP)
+ | IPSET_FLAG(IPSET_OPT_IP_TO),
+ .help = "IP",
+ },
+ [IPSET_TEST] = {
+ .args = {
+ IPSET_ARG_NONE,
+ },
+ .need = IPSET_FLAG(IPSET_OPT_IP),
+ .full = IPSET_FLAG(IPSET_OPT_IP)
+ | IPSET_FLAG(IPSET_OPT_IP_TO),
+ .help = "IP",
+ },
+ },
+ .usage = "where depending on the INET family\n"
+ " IP is a valid IPv4 or IPv6 address (or hostname),\n"
+ " CIDR is a valid IPv4 or IPv6 CIDR prefix.\n"
+ " Adding/deleting multiple elements in IP/CIDR or FROM-TO form\n"
+ " is supported for IPv4.",
+ .description = "bitmask support",
+};
+
void _init(void);
void _init(void)
{
@@ -486,4 +571,5 @@ void _init(void)
ipset_type_add(&ipset_hash_ip3);
ipset_type_add(&ipset_hash_ip4);
ipset_type_add(&ipset_hash_ip5);
+ ipset_type_add(&ipset_hash_ip6);
}
diff --git a/lib/ipset_hash_ipport.c b/lib/ipset_hash_ipport.c
index 288be10..2fa8abd 100644
--- a/lib/ipset_hash_ipport.c
+++ b/lib/ipset_hash_ipport.c
@@ -604,6 +604,113 @@ static struct ipset_type ipset_hash_ipport6 = {
.description = "bucketsize, initval support",
};
+/* bitmask support */
+static struct ipset_type ipset_hash_ipport7 = {
+ .name = "hash:ip,port",
+ .alias = { "ipporthash", NULL },
+ .revision = 7,
+ .family = NFPROTO_IPSET_IPV46,
+ .dimension = IPSET_DIM_TWO,
+ .elem = {
+ [IPSET_DIM_ONE - 1] = {
+ .parse = ipset_parse_ip4_single6,
+ .print = ipset_print_ip,
+ .opt = IPSET_OPT_IP
+ },
+ [IPSET_DIM_TWO - 1] = {
+ .parse = ipset_parse_proto_port,
+ .print = ipset_print_proto_port,
+ .opt = IPSET_OPT_PORT
+ },
+ },
+ .cmd = {
+ [IPSET_CREATE] = {
+ .args = {
+ IPSET_ARG_FAMILY,
+ /* Aliases */
+ IPSET_ARG_INET,
+ IPSET_ARG_INET6,
+ IPSET_ARG_HASHSIZE,
+ IPSET_ARG_MAXELEM,
+ IPSET_ARG_TIMEOUT,
+ IPSET_ARG_COUNTERS,
+ IPSET_ARG_COMMENT,
+ IPSET_ARG_FORCEADD,
+ IPSET_ARG_SKBINFO,
+ IPSET_ARG_BUCKETSIZE,
+ IPSET_ARG_INITVAL,
+ IPSET_ARG_NETMASK,
+ IPSET_ARG_BITMASK,
+ /* Ignored options: backward compatibilty */
+ IPSET_ARG_PROBES,
+ IPSET_ARG_RESIZE,
+ IPSET_ARG_IGNORED_FROM,
+ IPSET_ARG_IGNORED_TO,
+ IPSET_ARG_IGNORED_NETWORK,
+ IPSET_ARG_NONE,
+ },
+ .need = 0,
+ .full = 0,
+ .help = "",
+ },
+ [IPSET_ADD] = {
+ .args = {
+ IPSET_ARG_TIMEOUT,
+ IPSET_ARG_PACKETS,
+ IPSET_ARG_BYTES,
+ IPSET_ARG_ADT_COMMENT,
+ IPSET_ARG_SKBMARK,
+ IPSET_ARG_SKBPRIO,
+ IPSET_ARG_SKBQUEUE,
+ IPSET_ARG_NONE,
+ },
+ .need = IPSET_FLAG(IPSET_OPT_IP)
+ | IPSET_FLAG(IPSET_OPT_PROTO)
+ | IPSET_FLAG(IPSET_OPT_PORT),
+ .full = IPSET_FLAG(IPSET_OPT_IP)
+ | IPSET_FLAG(IPSET_OPT_IP_TO)
+ | IPSET_FLAG(IPSET_OPT_PROTO)
+ | IPSET_FLAG(IPSET_OPT_PORT)
+ | IPSET_FLAG(IPSET_OPT_PORT_TO),
+ .help = "IP,[PROTO:]PORT",
+ },
+ [IPSET_DEL] = {
+ .args = {
+ IPSET_ARG_NONE,
+ },
+ .need = IPSET_FLAG(IPSET_OPT_IP)
+ | IPSET_FLAG(IPSET_OPT_PROTO)
+ | IPSET_FLAG(IPSET_OPT_PORT),
+ .full = IPSET_FLAG(IPSET_OPT_IP)
+ | IPSET_FLAG(IPSET_OPT_IP_TO)
+ | IPSET_FLAG(IPSET_OPT_PROTO)
+ | IPSET_FLAG(IPSET_OPT_PORT)
+ | IPSET_FLAG(IPSET_OPT_PORT_TO),
+ .help = "IP,[PROTO:]PORT",
+ },
+ [IPSET_TEST] = {
+ .args = {
+ IPSET_ARG_NONE,
+ },
+ .need = IPSET_FLAG(IPSET_OPT_IP)
+ | IPSET_FLAG(IPSET_OPT_PROTO)
+ | IPSET_FLAG(IPSET_OPT_PORT),
+ .full = IPSET_FLAG(IPSET_OPT_IP)
+ | IPSET_FLAG(IPSET_OPT_PROTO)
+ | IPSET_FLAG(IPSET_OPT_PORT),
+ .help = "IP,[PROTO:]PORT",
+ },
+ },
+ .usage = "where depending on the INET family\n"
+ " IP is a valid IPv4 or IPv6 address (or hostname).\n"
+ " Adding/deleting multiple elements in IP/CIDR or FROM-TO form\n"
+ " is supported for IPv4.\n"
+ " Adding/deleting multiple elements with TCP/SCTP/UDP/UDPLITE\n"
+ " port range is supported both for IPv4 and IPv6.",
+ .usagefn = ipset_port_usage,
+ .description = "netmask and bitmask support",
+};
+
void _init(void);
void _init(void)
{
@@ -613,4 +720,5 @@ void _init(void)
ipset_type_add(&ipset_hash_ipport4);
ipset_type_add(&ipset_hash_ipport5);
ipset_type_add(&ipset_hash_ipport6);
+ ipset_type_add(&ipset_hash_ipport7);
}
diff --git a/lib/ipset_hash_netnet.c b/lib/ipset_hash_netnet.c
index df993b8..0e176e3 100644
--- a/lib/ipset_hash_netnet.c
+++ b/lib/ipset_hash_netnet.c
@@ -387,6 +387,106 @@ static struct ipset_type ipset_hash_netnet3 = {
.description = "bucketsize, initval support",
};
+/* bitmask support */
+static struct ipset_type ipset_hash_netnet4 = {
+ .name = "hash:net,net",
+ .alias = { "netnethash", NULL },
+ .revision = 4,
+ .family = NFPROTO_IPSET_IPV46,
+ .dimension = IPSET_DIM_TWO,
+ .elem = {
+ [IPSET_DIM_ONE - 1] = {
+ .parse = ipset_parse_ip4_net6,
+ .print = ipset_print_ip,
+ .opt = IPSET_OPT_IP
+ },
+ [IPSET_DIM_TWO - 1] = {
+ .parse = ipset_parse_ip4_net6,
+ .print = ipset_print_ip,
+ .opt = IPSET_OPT_IP2
+ },
+ },
+ .cmd = {
+ [IPSET_CREATE] = {
+ .args = {
+ IPSET_ARG_FAMILY,
+ /* Aliases */
+ IPSET_ARG_INET,
+ IPSET_ARG_INET6,
+ IPSET_ARG_HASHSIZE,
+ IPSET_ARG_MAXELEM,
+ IPSET_ARG_TIMEOUT,
+ IPSET_ARG_COUNTERS,
+ IPSET_ARG_COMMENT,
+ IPSET_ARG_FORCEADD,
+ IPSET_ARG_SKBINFO,
+ IPSET_ARG_BUCKETSIZE,
+ IPSET_ARG_INITVAL,
+ IPSET_ARG_BITMASK,
+ IPSET_ARG_NETMASK,
+ IPSET_ARG_NONE,
+ },
+ .need = 0,
+ .full = 0,
+ .help = "",
+ },
+ [IPSET_ADD] = {
+ .args = {
+ IPSET_ARG_TIMEOUT,
+ IPSET_ARG_NOMATCH,
+ IPSET_ARG_PACKETS,
+ IPSET_ARG_BYTES,
+ IPSET_ARG_ADT_COMMENT,
+ IPSET_ARG_SKBMARK,
+ IPSET_ARG_SKBPRIO,
+ IPSET_ARG_SKBQUEUE,
+ IPSET_ARG_NONE,
+ },
+ .need = IPSET_FLAG(IPSET_OPT_IP)
+ | IPSET_FLAG(IPSET_OPT_IP2),
+ .full = IPSET_FLAG(IPSET_OPT_IP)
+ | IPSET_FLAG(IPSET_OPT_CIDR)
+ | IPSET_FLAG(IPSET_OPT_IP_TO)
+ | IPSET_FLAG(IPSET_OPT_IP2)
+ | IPSET_FLAG(IPSET_OPT_CIDR2)
+ | IPSET_FLAG(IPSET_OPT_IP2_TO),
+ .help = "IP[/CIDR]|FROM-TO,IP[/CIDR]|FROM-TO",
+ },
+ [IPSET_DEL] = {
+ .args = {
+ IPSET_ARG_NONE,
+ },
+ .need = IPSET_FLAG(IPSET_OPT_IP)
+ | IPSET_FLAG(IPSET_OPT_IP2),
+ .full = IPSET_FLAG(IPSET_OPT_IP)
+ | IPSET_FLAG(IPSET_OPT_CIDR)
+ | IPSET_FLAG(IPSET_OPT_IP_TO)
+ | IPSET_FLAG(IPSET_OPT_IP2)
+ | IPSET_FLAG(IPSET_OPT_CIDR2)
+ | IPSET_FLAG(IPSET_OPT_IP2_TO),
+ .help = "IP[/CIDR]|FROM-TO,IP[/CIDR]|FROM-TO",
+ },
+ [IPSET_TEST] = {
+ .args = {
+ IPSET_ARG_NOMATCH,
+ IPSET_ARG_NONE,
+ },
+ .need = IPSET_FLAG(IPSET_OPT_IP)
+ | IPSET_FLAG(IPSET_OPT_IP2),
+ .full = IPSET_FLAG(IPSET_OPT_IP)
+ | IPSET_FLAG(IPSET_OPT_CIDR)
+ | IPSET_FLAG(IPSET_OPT_IP2)
+ | IPSET_FLAG(IPSET_OPT_CIDR2),
+ .help = "IP[/CIDR],IP[/CIDR]",
+ },
+ },
+ .usage = "where depending on the INET family\n"
+ " IP is an IPv4 or IPv6 address (or hostname),\n"
+ " CIDR is a valid IPv4 or IPv6 CIDR prefix.\n"
+ " IP range is not supported with IPv6.",
+ .description = "netmask and bitmask support",
+};
+
void _init(void);
void _init(void)
{
@@ -394,4 +494,5 @@ void _init(void)
ipset_type_add(&ipset_hash_netnet1);
ipset_type_add(&ipset_hash_netnet2);
ipset_type_add(&ipset_hash_netnet3);
+ ipset_type_add(&ipset_hash_netnet4);
}
diff --git a/lib/libipset.map b/lib/libipset.map
index 12d16a4..c69b738 100644
--- a/lib/libipset.map
+++ b/lib/libipset.map
@@ -208,3 +208,12 @@ global:
ipset_print_hexnumber;
} LIBIPSET_4.9;
+LIBIPSET_4.11 {
+global:
+ ipset_xlate_argv;
+} LIBIPSET_4.10;
+
+LIBIPSET_4.12 {
+global:
+ ipset_parse_bitmask;
+} LIBIPSET_4.10;
diff --git a/lib/parse.c b/lib/parse.c
index f3f2d11..4d2d8b3 100644
--- a/lib/parse.c
+++ b/lib/parse.c
@@ -41,6 +41,9 @@
#define syntax_err(fmt, args...) \
ipset_err(session, "Syntax error: " fmt , ## args)
+#define syntax_err_ll(errtype, fmt, args...) \
+ ipset_session_report(session, errtype, "Syntax error: " fmt , ## args)
+
static char *
ipset_strchr(const char *str, const char *sep)
{
@@ -87,7 +90,8 @@ string_to_number_ll(struct ipset_session *session,
const char *str,
unsigned long long min,
unsigned long long max,
- unsigned long long *ret)
+ unsigned long long *ret,
+ enum ipset_err_type errtype)
{
unsigned long long number = 0;
char *end;
@@ -104,23 +108,24 @@ string_to_number_ll(struct ipset_session *session,
errno = ERANGE;
}
if (errno == ERANGE && max)
- return syntax_err("'%s' is out of range %llu-%llu",
- str, min, max);
+ return syntax_err_ll(errtype, "'%s' is out of range %llu-%llu",
+ str, min, max);
else if (errno == ERANGE)
- return syntax_err("'%s' is out of range %llu-%llu",
- str, min, ULLONG_MAX);
+ return syntax_err_ll(errtype, "'%s' is out of range %llu-%llu",
+ str, min, ULLONG_MAX);
else
- return syntax_err("'%s' is invalid as number", str);
+ return syntax_err_ll(errtype, "'%s' is invalid as number", str);
}
static int
string_to_u8(struct ipset_session *session,
- const char *str, uint8_t *ret)
+ const char *str, uint8_t *ret,
+ enum ipset_err_type errtype)
{
int err;
unsigned long long num = 0;
- err = string_to_number_ll(session, str, 0, 255, &num);
+ err = string_to_number_ll(session, str, 0, 255, &num, errtype);
*ret = num;
return err;
@@ -130,7 +135,7 @@ static int
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);
+ int err = string_to_u8(session, str, ret, IPSET_ERROR);
if (!err && (*ret < min || *ret > max))
return syntax_err("'%s' is out of range %u-%u",
@@ -141,12 +146,13 @@ string_to_cidr(struct ipset_session *session,
static int
string_to_u16(struct ipset_session *session,
- const char *str, uint16_t *ret)
+ const char *str, uint16_t *ret,
+ enum ipset_err_type errtype)
{
int err;
unsigned long long num = 0;
- err = string_to_number_ll(session, str, 0, USHRT_MAX, &num);
+ err = string_to_number_ll(session, str, 0, USHRT_MAX, &num, errtype);
*ret = num;
return err;
@@ -159,7 +165,8 @@ string_to_u32(struct ipset_session *session,
int err;
unsigned long long num = 0;
- err = string_to_number_ll(session, str, 0, UINT_MAX, &num);
+ err = string_to_number_ll(session, str, 0, UINT_MAX, &num,
+ IPSET_ERROR);
*ret = num;
return err;
@@ -274,7 +281,10 @@ parse_portname(struct ipset_session *session, const char *str,
uint16_t *port, const char *proto)
{
char *saved, *tmp;
+ const char *protoname;
+ const struct protoent *protoent;
struct servent *service;
+ uint8_t protonum = 0;
saved = tmp = ipset_strdup(session, str);
if (tmp == NULL)
@@ -283,7 +293,15 @@ parse_portname(struct ipset_session *session, const char *str,
if (tmp == NULL)
goto error;
- service = getservbyname(tmp, proto);
+ protoname = proto;
+ if (string_to_u8(session, proto, &protonum, IPSET_WARNING) == 0) {
+ protoent = getprotobynumber(protonum);
+ if (protoent == NULL)
+ goto error;
+ protoname = protoent->p_name;
+ }
+
+ service = getservbyname(tmp, protoname);
if (service != NULL) {
*port = ntohs((uint16_t) service->s_port);
free(saved);
@@ -319,7 +337,7 @@ ipset_parse_port(struct ipset_session *session,
assert(opt == IPSET_OPT_PORT || opt == IPSET_OPT_PORT_TO);
assert(str);
- if (string_to_u16(session, str, &port) == 0) {
+ if (string_to_u16(session, str, &port, IPSET_WARNING) == 0) {
return ipset_session_data_set(session, opt, &port);
}
/* Error is stored as warning in session report */
@@ -469,21 +487,23 @@ ipset_parse_proto(struct ipset_session *session,
{
const struct protoent *protoent;
uint8_t proto = 0;
+ uint8_t protonum = 0;
assert(session);
assert(opt == IPSET_OPT_PROTO);
assert(str);
+ if (string_to_u8(session, str, &protonum, IPSET_WARNING) == 0)
+ return ipset_session_data_set(session, opt, &protonum);
+
+ /* No error, so reset false error messages */
+ ipset_session_report_reset(session);
protoent = getprotobyname(strcasecmp(str, "icmpv6") == 0
? "ipv6-icmp" : str);
- if (protoent == NULL) {
- uint8_t protonum = 0;
- if (string_to_u8(session, str, &protonum) ||
- (protoent = getprotobynumber(protonum)) == NULL)
- return syntax_err("cannot parse '%s' "
- "as a protocol", str);
- }
+ if (protoent == NULL)
+ return syntax_err("cannot parse '%s' "
+ "as a protocol", str);
proto = protoent->p_proto;
if (!proto)
return syntax_err("Unsupported protocol '%s'", str);
@@ -513,8 +533,8 @@ parse_icmp_typecode(struct ipset_session *session,
str, family);
}
*a++ = '\0';
- if ((err = string_to_u8(session, tmp, &type)) != 0 ||
- (err = string_to_u8(session, a, &code)) != 0)
+ if ((err = string_to_u8(session, tmp, &type, IPSET_ERROR)) != 0 ||
+ (err = string_to_u8(session, a, &code, IPSET_ERROR)) != 0)
goto error;
typecode = (type << 8) | code;
@@ -1335,7 +1355,8 @@ ipset_parse_timeout(struct ipset_session *session,
assert(opt == IPSET_OPT_TIMEOUT);
assert(str);
- err = string_to_number_ll(session, str, 0, (UINT_MAX>>1)/1000, &llnum);
+ err = string_to_number_ll(session, str, 0, (UINT_MAX>>1)/1000, &llnum,
+ IPSET_ERROR);
if (err == 0) {
/* Timeout is expected to be 32bits wide, so we have
to convert it here */
@@ -1579,7 +1600,8 @@ ipset_parse_uint64(struct ipset_session *session,
assert(session);
assert(str);
- err = string_to_number_ll(session, str, 0, ULLONG_MAX - 1, &value);
+ err = string_to_number_ll(session, str, 0, ULLONG_MAX - 1, &value,
+ IPSET_ERROR);
if (err)
return err;
@@ -1623,7 +1645,7 @@ ipset_parse_uint16(struct ipset_session *session,
assert(session);
assert(str);
- err = string_to_u16(session, str, &value);
+ err = string_to_u16(session, str, &value, IPSET_ERROR);
if (err == 0)
return ipset_session_data_set(session, opt, &value);
@@ -1651,7 +1673,7 @@ ipset_parse_uint8(struct ipset_session *session,
assert(session);
assert(str);
- if ((err = string_to_u8(session, str, &value)) == 0)
+ if ((err = string_to_u8(session, str, &value, IPSET_ERROR)) == 0)
return ipset_session_data_set(session, opt, &value);
return err;
@@ -1682,6 +1704,9 @@ ipset_parse_netmask(struct ipset_session *session,
assert(str);
data = ipset_session_data(session);
+ if (ipset_data_test(data, IPSET_OPT_BITMASK))
+ return syntax_err("bitmask and netmask are mutually exclusive, provide only one");
+
family = ipset_data_family(data);
if (family == NFPROTO_UNSPEC) {
family = NFPROTO_IPV4;
@@ -1701,6 +1726,46 @@ ipset_parse_netmask(struct ipset_session *session,
}
/**
+ * ipset_parse_bitmask - parse string as a bitmask
+ * @session: session structure
+ * @opt: option kind of the data
+ * @str: string to parse
+ *
+ * Parse string as a bitmask value, depending on family type.
+ * If family is not set yet, 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_bitmask(struct ipset_session *session,
+ enum ipset_opt opt, const char *str)
+{
+ uint8_t family;
+ struct ipset_data *data;
+
+ assert(session);
+ assert(opt == IPSET_OPT_BITMASK);
+ assert(str);
+
+ data = ipset_session_data(session);
+ if (ipset_data_test(data, IPSET_OPT_NETMASK))
+ return syntax_err("bitmask and netmask are mutually exclusive, provide only one");
+
+ family = ipset_data_family(data);
+ if (family == NFPROTO_UNSPEC) {
+ family = NFPROTO_IPV4;
+ ipset_data_set(data, IPSET_OPT_FAMILY, &family);
+ }
+
+ if (parse_ipaddr(session, opt, str, family))
+ return syntax_err("bitmask is not valid for family = %s",
+ family == NFPROTO_IPV4 ? "inet" : "inet6");
+
+ return 0;
+}
+
+/**
* ipset_parse_flag - "parse" option flags
* @session: session structure
* @opt: option kind of the data
diff --git a/lib/print.c b/lib/print.c
index a7ffd81..6ea79cb 100644
--- a/lib/print.c
+++ b/lib/print.c
@@ -265,7 +265,7 @@ ipset_print_ip(char *buf, unsigned int len,
assert(buf);
assert(len > 0);
assert(data);
- assert(opt == IPSET_OPT_IP || opt == IPSET_OPT_IP2);
+ assert(opt == IPSET_OPT_IP || opt == IPSET_OPT_IP2 || opt == IPSET_OPT_BITMASK);
D("len: %u", len);
family = ipset_data_family(data);
@@ -411,10 +411,11 @@ ipset_print_number(char *buf, unsigned int len,
int
ipset_print_hexnumber(char *buf, unsigned int len,
const struct ipset_data *data, enum ipset_opt opt,
- uint8_t env UNUSED)
+ uint8_t env)
{
size_t maxsize;
const void *number;
+ const char *quoted = env & IPSET_ENV_QUOTED ? "\"" : "";
assert(buf);
assert(len > 0);
@@ -424,17 +425,17 @@ ipset_print_hexnumber(char *buf, unsigned int len,
maxsize = ipset_data_sizeof(opt, AF_INET);
D("opt: %u, maxsize %zu", opt, maxsize);
if (maxsize == sizeof(uint8_t))
- return snprintf(buf, len, "0x%02"PRIx8,
- *(const uint8_t *) number);
+ return snprintf(buf, len, "%s0x%02"PRIx8"%s",
+ quoted, *(const uint8_t *) number, quoted);
else if (maxsize == sizeof(uint16_t))
- return snprintf(buf, len, "0x%04"PRIx16,
- *(const uint16_t *) number);
+ return snprintf(buf, len, "%s0x%04"PRIx16"%s",
+ quoted, *(const uint16_t *) number, quoted);
else if (maxsize == sizeof(uint32_t))
- return snprintf(buf, len, "0x%08"PRIx32,
- *(const uint32_t *) number);
+ return snprintf(buf, len, "%s0x%08"PRIx32"%s",
+ quoted, *(const uint32_t *) number, quoted);
else if (maxsize == sizeof(uint64_t))
- return snprintf(buf, len, "0x%016"PRIx64,
- *(const uint64_t *) number);
+ return snprintf(buf, len, "%s0x%016"PRIx64"%s",
+ quoted, *(const uint64_t *) number, quoted);
else
assert(0);
return 0;
@@ -976,6 +977,7 @@ ipset_print_data(char *buf, unsigned int len,
size = ipset_print_elem(buf, len, data, opt, env);
break;
case IPSET_OPT_IP:
+ case IPSET_OPT_BITMASK:
size = ipset_print_ip(buf, len, data, opt, env);
break;
case IPSET_OPT_PORT:
diff --git a/lib/session.c b/lib/session.c
index 1ca26ff..f822288 100644
--- a/lib/session.c
+++ b/lib/session.c
@@ -462,6 +462,10 @@ static const struct ipset_attr_policy create_attrs[] = {
.type = MNL_TYPE_U32,
.opt = IPSET_OPT_MEMSIZE,
},
+ [IPSET_ATTR_BITMASK] = {
+ .type = MNL_TYPE_NESTED,
+ .opt = IPSET_OPT_BITMASK,
+ },
};
static const struct ipset_attr_policy adt_attrs[] = {
@@ -856,6 +860,7 @@ list_adt(struct ipset_session *session, struct nlattr *nla[])
const struct ipset_arg *arg;
size_t offset = 0;
int i, found = 0;
+ static char last_setname[IPSET_MAXNAMELEN] = "";
D("enter");
/* Check and load type, family */
@@ -890,6 +895,13 @@ list_adt(struct ipset_session *session, struct nlattr *nla[])
case IPSET_LIST_XML:
safe_snprintf(session, "<member><elem>");
break;
+ case IPSET_LIST_JSON:
+ /* print separator if a member for this set was printed before */
+ if (STREQ(ipset_data_setname(data), last_setname))
+ safe_snprintf(session, ",");
+ strcpy(last_setname, ipset_data_setname(data));
+ safe_snprintf(session, "\n {\n \"elem\" : \"");
+ break;
case IPSET_LIST_PLAIN:
default:
break;
@@ -898,6 +910,8 @@ list_adt(struct ipset_session *session, struct nlattr *nla[])
safe_dprintf(session, ipset_print_elem, IPSET_OPT_ELEM);
if (session->mode == IPSET_LIST_XML)
safe_snprintf(session, "</elem>");
+ if (session->mode == IPSET_LIST_JSON)
+ safe_snprintf(session, "\"");
for (i = 0; type->cmd[IPSET_ADD].args[i] != IPSET_ARG_NONE; i++) {
arg = ipset_keyword(type->cmd[IPSET_ADD].args[i]);
@@ -925,6 +939,15 @@ list_adt(struct ipset_session *session, struct nlattr *nla[])
safe_dprintf(session, arg->print, arg->opt);
safe_snprintf(session, "</%s>", arg->name[0]);
break;
+ case IPSET_LIST_JSON:
+ if (arg->has_arg == IPSET_NO_ARG) {
+ safe_snprintf(session,
+ ",\n \"%s\" : true", arg->name[0]);
+ break;
+ }
+ safe_snprintf(session, ",\n \"%s\" : ", arg->name[0]);
+ safe_dprintf(session, arg->print, arg->opt);
+ break;
default:
break;
}
@@ -932,6 +955,8 @@ list_adt(struct ipset_session *session, struct nlattr *nla[])
if (session->mode == IPSET_LIST_XML)
safe_snprintf(session, "</member>\n");
+ else if (session->mode == IPSET_LIST_JSON)
+ safe_snprintf(session, "\n }");
else
safe_snprintf(session, "\n");
@@ -968,6 +993,7 @@ list_create(struct ipset_session *session, struct nlattr *nla[])
const struct ipset_arg *arg;
uint8_t family;
int i;
+ static bool firstipset = true;
for (i = IPSET_ATTR_UNSPEC + 1; i <= IPSET_ATTR_CREATE_MAX; i++)
if (nla[i]) {
@@ -1003,6 +1029,19 @@ list_create(struct ipset_session *session, struct nlattr *nla[])
ipset_data_setname(data),
type->name, type->revision);
break;
+ case IPSET_LIST_JSON:
+ if (!firstipset)
+ safe_snprintf(session, ",\n");
+ firstipset = false;
+ safe_snprintf(session,
+ " \{\n"
+ " \"name\" : \"%s\",\n"
+ " \"type\" : \"%s\",\n"
+ " \"revision\" : %u,\n"
+ " \"header\" : \{\n",
+ ipset_data_setname(data),
+ type->name, type->revision);
+ break;
default:
break;
}
@@ -1038,6 +1077,22 @@ list_create(struct ipset_session *session, struct nlattr *nla[])
safe_dprintf(session, arg->print, arg->opt);
safe_snprintf(session, "</%s>", arg->name[0]);
break;
+ case IPSET_LIST_JSON:
+ if (arg->has_arg == IPSET_NO_ARG) {
+ safe_snprintf(session,
+ " \"%s\" : true,\n", arg->name[0]);
+ break;
+ }
+ if (arg->opt == IPSET_OPT_FAMILY) {
+ safe_snprintf(session, " \"%s\" : \"", arg->name[0]);
+ safe_dprintf(session, arg->print, arg->opt);
+ safe_snprintf(session, "\",\n");
+ break;
+ }
+ safe_snprintf(session, " \"%s\" : ", arg->name[0]);
+ safe_dprintf(session, arg->print, arg->opt);
+ safe_snprintf(session, ",\n");
+ break;
default:
break;
}
@@ -1075,6 +1130,21 @@ list_create(struct ipset_session *session, struct nlattr *nla[])
"</header>\n" :
"</header>\n<members>\n");
break;
+ case IPSET_LIST_JSON:
+ safe_snprintf(session, " \"memsize\" : ");
+ safe_dprintf(session, ipset_print_number, IPSET_OPT_MEMSIZE);
+ safe_snprintf(session, ",\n \"references\" : ");
+ safe_dprintf(session, ipset_print_number, IPSET_OPT_REFERENCES);
+ if (ipset_data_test(data, IPSET_OPT_ELEMENTS)) {
+ safe_snprintf(session, ",\n \"numentries\" : ");
+ safe_dprintf(session, ipset_print_number, IPSET_OPT_ELEMENTS);
+ }
+ safe_snprintf(session, "\n");
+ safe_snprintf(session,
+ session->envopts & IPSET_ENV_LIST_HEADER ?
+ " },\n" :
+ " },\n \"members\" : [");
+ break;
default:
break;
}
@@ -1210,11 +1280,24 @@ print_set_done(struct ipset_session *session, bool callback_done)
if (session->saved_setname[0] != '\0')
safe_snprintf(session, "</members>\n</ipset>\n");
break;
+ case IPSET_LIST_JSON:
+ if (session->envopts & IPSET_ENV_LIST_SETNAME)
+ break;
+ if (session->envopts & IPSET_ENV_LIST_HEADER) {
+ if (session->saved_setname[0] != '\0')
+ safe_snprintf(session, " }");
+ break;
+ }
+ if (session->saved_setname[0] != '\0')
+ safe_snprintf(session, "\n ]\n }");
+ break;
default:
break;
}
if (callback_done && session->mode == IPSET_LIST_XML)
safe_snprintf(session, "</ipsets>\n");
+ if (callback_done && session->mode == IPSET_LIST_JSON)
+ safe_snprintf(session, "\n]\n");
return call_outfn(session) ? MNL_CB_ERROR : MNL_CB_STOP;
}
@@ -1223,6 +1306,7 @@ callback_list(struct ipset_session *session, struct nlattr *nla[],
enum ipset_cmd cmd)
{
struct ipset_data *data = session->data;
+ static bool firstipset = true;
if (setjmp(printf_failure)) {
session->saved_setname[0] = '\0';
@@ -1241,7 +1325,13 @@ callback_list(struct ipset_session *session, struct nlattr *nla[],
if (session->mode == IPSET_LIST_XML)
safe_snprintf(session, "<ipset name=\"%s\"/>\n",
ipset_data_setname(data));
- else
+ else if (session->mode == IPSET_LIST_JSON) {
+ if (!firstipset)
+ safe_snprintf(session, ",\n");
+ firstipset = false;
+ safe_snprintf(session, " { \"name\" : \"%s\" }",
+ ipset_data_setname(data));
+ } else
safe_snprintf(session, "%s\n",
ipset_data_setname(data));
return call_outfn(session) ? MNL_CB_ERROR : MNL_CB_OK;
@@ -1721,6 +1811,10 @@ rawdata2attr(struct ipset_session *session, struct nlmsghdr *nlh,
if (attr->type == MNL_TYPE_NESTED) {
/* IP addresses */
struct nlattr *nested;
+
+ if (type == IPSET_ATTR_BITMASK)
+ family = ipset_data_family(session->data);
+
int atype = family == NFPROTO_IPV4 ? IPSET_ATTR_IPADDR_IPV4
: IPSET_ATTR_IPADDR_IPV6;
@@ -2187,18 +2281,27 @@ ipset_cmd(struct ipset_session *session, enum ipset_cmd cmd, uint32_t lineno)
session->cmd = cmd;
session->lineno = lineno;
- /* Set default output mode */
- if (cmd == IPSET_CMD_LIST) {
- if (session->mode == IPSET_LIST_NONE)
- session->mode = IPSET_LIST_PLAIN;
- } else if (cmd == IPSET_CMD_SAVE) {
+ if (cmd == IPSET_CMD_LIST || cmd == IPSET_CMD_SAVE) {
+ /* Set default output mode */
if (session->mode == IPSET_LIST_NONE)
- session->mode = IPSET_LIST_SAVE;
+ session->mode = cmd == IPSET_CMD_LIST ?
+ IPSET_LIST_PLAIN : IPSET_LIST_SAVE;
+ /* Reset just in case there are multiple modes in a session */
+ ipset_envopt_unset(session, IPSET_ENV_QUOTED);
+ switch (session->mode) {
+ case IPSET_LIST_XML:
+ /* Start the root element in XML mode */
+ safe_snprintf(session, "<ipsets>\n");
+ break;
+ case IPSET_LIST_JSON:
+ /* Start the root element in json mode */
+ ipset_envopt_set(session, IPSET_ENV_QUOTED);
+ safe_snprintf(session, "[\n");
+ break;
+ default:
+ break;
+ }
}
- /* Start the root element in XML mode */
- if ((cmd == IPSET_CMD_LIST || cmd == IPSET_CMD_SAVE) &&
- session->mode == IPSET_LIST_XML)
- safe_snprintf(session, "<ipsets>\n");
D("next: build_msg");
/* Build new message or append buffered commands */
diff --git a/src/Makefile.am b/src/Makefile.am
index 438fcec..95dea07 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -12,10 +12,16 @@ AM_LDFLAGS = -static
endif
endif
-dist_man_MANS = ipset.8
+dist_man_MANS = ipset.8 ipset-translate.8
sparse-check: $(ipset_SOURCES:.c=.d)
%.d: %.c
$(IPSET_AM_V_CHECK)\
$(SPARSE) -I.. $(SPARSE_FLAGS) $(AM_CFLAGS) $(AM_CPPFLAGS) $< || :
+
+install-exec-hook:
+ ${LN_S} -f "${sbindir}/ipset" "${DESTDIR}${sbindir}/ipset-translate";
+
+uninstall-hook:
+ rm -f ${DESTDIR}${sbindir}/ipset-translate
diff --git a/src/ipset-translate.8 b/src/ipset-translate.8
new file mode 100644
index 0000000..55ce2a9
--- /dev/null
+++ b/src/ipset-translate.8
@@ -0,0 +1,91 @@
+.\"
+.\" (C) Copyright 2021, Pablo Neira Ayuso <pablo@netfilter.org>
+.\"
+.\" %%%LICENSE_START(GPLv2+_DOC_FULL)
+.\" This is free documentation; 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.
+.\"
+.\" The GNU General Public License's references to "object code"
+.\" and "executables" are to be interpreted as the output of any
+.\" document formatting or typesetting system, including
+.\" intermediate and printed output.
+.\"
+.\" This manual 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 manual; if not, see
+.\" <http://www.gnu.org/licenses/>.
+.\" %%%LICENSE_END
+.\"
+.TH IPSET-TRANSLATE 8 "May 31, 2021"
+
+.SH NAME
+ipset-translate \(em translation tool to migrate from ipset to nftables
+.SH DESCRIPTION
+This tool allows system administrators to translate a given IP sets file
+to \fBnftables(8)\fP.
+
+The only available command is:
+
+.IP \[bu] 2
+ipset-translate restore < file.ipt
+
+.SH USAGE
+The \fBipset-translate\fP tool reads an IP sets file in the syntax produced by
+\fBipset(8)\fP save. No set modifications occur, this tool is a text converter.
+
+.SH EXAMPLES
+Basic operation examples.
+
+Single command translation, assuming the original file:
+
+.nf
+create test1 hash:ip,port family inet counters timeout 300 hashsize 1024 maxelem 65536 bucketsize 12 initval 0xb5c4be5d
+add test1 1.1.1.1,udp:20
+add test1 1.1.1.1,21
+create test2 hash:ip,port family inet hashsize 1024 maxelem 65536 bucketsize 12 initval 0xb5c4be5d
+.fi
+
+which results in the following translation:
+
+.nf
+root@machine:~# ipset-translate restore < file.ipt
+add set inet global test1 { type ipv4_addr . inet_proto . inet_service; counter; timeout 300s; size 65536; }
+add element inet global test1 { 1.1.1.1 . udp . 20 }
+add element inet global test1 { 1.1.1.1 . tcp . 21 }
+add set inet global test2 { type ipv4_addr . inet_proto . inet_service; size 65536; }
+.fi
+
+.SH LIMITATIONS
+A few IP sets options may be not supported because they are not yet implemented
+in \fBnftables(8)\fP.
+
+Contrary to \fBnftables(8)\fP, IP sets are not attached to a specific table.
+The translation utility assumes that sets are created in a table whose name
+is \fBglobal\fP and family is \fBinet\fP. You might want to update the
+resulting translation to use a different table name and family for your sets.
+
+To get up-to-date information about this, please head to
+\fBhttps://wiki.nftables.org/\fP.
+
+.SH SEE ALSO
+\fBnft(8)\fP, \fBipset(8)\fP
+
+.SH AUTHORS
+The nftables framework has been written by the Netfilter Project
+(https://www.netfilter.org).
+
+This manual page was written by Pablo Neira Ayuso
+<pablo@netfilter.org>.
+
+This documentation is free/libre under the terms of the GPLv2+.
+
+This tool was funded through the NGI0 PET Fund, a fund established by NLnet with
+financial support from the European Commission's Next Generation Internet
+programme, under the aegis of DG Communications Networks, Content and Technology
+under grant agreement No 825310.
diff --git a/src/ipset.8 b/src/ipset.8
index 97cece9..04febda 100644
--- a/src/ipset.8
+++ b/src/ipset.8
@@ -21,7 +21,7 @@ ipset \(em administration tool for IP sets
.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 | \fB\-name\fR | \fB\-terse\fR | \fB\-file\fR \fIfilename\fR }
+\fIOPTIONS\fR := { \fB\-exist\fR | \fB\-output\fR { \fBplain\fR | \fBsave\fR | \fBxml\fR } | \fBjson\fR } | \fB\-quiet\fR | \fB\-resolve\fR | \fB\-sorted\fR | \fB\-name\fR | \fB\-terse\fR | \fB\-file\fR \fIfilename\fR }
.PP
\fBipset\fR \fBcreate\fR \fISETNAME\fR \fITYPENAME\fR [ \fICREATE\-OPTIONS\fR ]
.PP
@@ -118,7 +118,7 @@ option is given, the entries are listed/saved sorted (which may be slow).
The option
\fB\-output\fR
can be used to control the format of the listing:
-\fBplain\fR, \fBsave\fR or \fBxml\fR.
+\fBplain\fR, \fBsave\fR, \fBxml\fR or \fBjson\fR.
(The default is
\fBplain\fR.)
If the option
@@ -187,7 +187,7 @@ cannot be abbreviated.
Ignore errors when 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 }
+\fB\-o\fP, \fB\-output\fP { \fBplain\fR | \fBsave\fR | \fBxml\fR | \fBjson\fR }
Select the output format to the
\fBlist\fR
command.
@@ -333,7 +333,7 @@ are hex without 0x prefix.
.IP
ipset create foo hash:ip skbinfo
.IP
-ipset add foo skbmark 0x1111/0xff00ffff skbprio 1:10 skbqueue 10
+ipset add foo 192.168.0.1 skbmark 0x1111/0xff00ffff skbprio 1:10 skbqueue 10
.PP
.SS hashsize
This parameter is valid for the \fBcreate\fR command of all \fBhash\fR type sets.
@@ -524,7 +524,7 @@ The \fBhash:ip\fR set type uses a hash to store IP host addresses (default) or
network addresses. Zero valued IP address cannot be stored in a \fBhash:ip\fR
type of set.
.PP
-\fICREATE\-OPTIONS\fR := [ \fBfamily\fR { \fBinet\fR | \fBinet6\fR } ] [ \fBhashsize\fR \fIvalue\fR ] [ \fBmaxelem\fR \fIvalue\fR ] [ \fBbucketsize\fR \fIvalue\fR ] [ \fBnetmask\fP \fIcidr\fP ] [ \fBtimeout\fR \fIvalue\fR ] [ \fBcounters\fP ] [ \fBcomment\fP ] [ \fBskbinfo\fP ]
+\fICREATE\-OPTIONS\fR := [ \fBfamily\fR { \fBinet\fR | \fBinet6\fR } ] [ \fBhashsize\fR \fIvalue\fR ] [ \fBmaxelem\fR \fIvalue\fR ] [ \fBbucketsize\fR \fIvalue\fR ] [ \fBnetmask\fP \fIcidr\fP ] [ \fBbitmask\fP \fImask\fP ] [ \fBtimeout\fR \fIvalue\fR ] [ \fBcounters\fP ] [ \fBcomment\fP ] [ \fBskbinfo\fP ]
.PP
\fIADD\-ENTRY\fR := \fIipaddr\fR
.PP
@@ -549,6 +549,9 @@ ipset create foo hash:ip netmask 30
ipset add foo 192.168.1.0/24
.IP
ipset test foo 192.168.1.2
+.TP
+\fBbitmask\fP \fImask\fP
+This works similar to \fBnetmask\fP but it will accept any valid IPv4/v6 address. It does not have to be a valid netmask.
.SS hash:mac
The \fBhash:mac\fR set type uses a hash to store MAC addresses. Zero valued MAC addresses cannot be stored in a \fBhash:mac\fR
type of set. For matches on destination MAC addresses, see COMMENTS below.
@@ -648,7 +651,7 @@ over the second, so a nomatch entry could be potentially be ineffective if a mor
first parameter existed with a suitable second parameter.
Network address with zero prefix size cannot be stored in this type of set.
.PP
-\fICREATE\-OPTIONS\fR := [ \fBfamily\fR { \fBinet\fR | \fBinet6\fR } ] [ \fBhashsize\fR \fIvalue\fR ] [ \fBmaxelem\fR \fIvalue\fR ] [ \fBbucketsize\fR \fIvalue\fR ] [ \fBtimeout\fR \fIvalue\fR ] [ \fBcounters\fP ] [ \fBcomment\fP ] [ \fBskbinfo\fP ]
+\fICREATE\-OPTIONS\fR := [ \fBfamily\fR { \fBinet\fR | \fBinet6\fR } ] [ \fBhashsize\fR \fIvalue\fR ] [ \fBmaxelem\fR \fIvalue\fR ] [ \fBbucketsize\fR \fIvalue\fR ] [ \fBnetmask\fP \fIcidr\fP ] [ \fBbitmask\fP \fImask\fP ] [ \fBtimeout\fR \fIvalue\fR ] [ \fBcounters\fP ] [ \fBcomment\fP ] [ \fBskbinfo\fP ]
.PP
\fIADD\-ENTRY\fR := \fInetaddr\fR,\fInetaddr\fR
.PP
@@ -680,6 +683,18 @@ values added to the first parameter of the set. The number of secondary prefixes
further increases this as the list of secondary prefixes is traversed per primary
prefix.
.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\fP prefix 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,
+can be found in the set.
+.TP
+\fBbitmask\fP \fImask\fP
+This works similar to \fBnetmask\fP but it will accept any valid IPv4/v6 address. It does not have to be a valid netmask.
+.PP
Example:
.IP
ipset create foo hash:net,net
@@ -701,7 +716,7 @@ The \fBhash:ip,port\fR set type uses a hash to store IP address and port number
The port number is interpreted together with a protocol (default TCP) and zero
protocol number cannot be used.
.PP
-\fICREATE\-OPTIONS\fR := [ \fBfamily\fR { \fBinet\fR | \fBinet6\fR } ] [ \fBhashsize\fR \fIvalue\fR ] [ \fBmaxelem\fR \fIvalue\fR ] [ \fBbucketsize\fR \fIvalue\fR ] [ \fBtimeout\fR \fIvalue\fR ] [ \fBcounters\fP ] [ \fBcomment\fP ] [ \fBskbinfo\fP ]
+\fICREATE\-OPTIONS\fR := [ \fBfamily\fR { \fBinet\fR | \fBinet6\fR } ] [ \fBhashsize\fR \fIvalue\fR ] [ \fBmaxelem\fR \fIvalue\fR ] [ \fBbucketsize\fR \fIvalue\fR ] [ \fBnetmask\fP \fIcidr\fP ] [ \fBbitmask\fP \fImask\fP ] [ \fBtimeout\fR \fIvalue\fR ] [ \fBcounters\fP ] [ \fBcomment\fP ] [ \fBskbinfo\fP ]
.PP
\fIADD\-ENTRY\fR := \fIipaddr\fR,[\fIproto\fR:]\fIport\fR
.PP
@@ -741,6 +756,18 @@ 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
+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\fP prefix 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,
+can be found in the set.
+.TP
+\fBbitmask\fP \fImask\fP
+This works similar to \fBnetmask\fP but it will accept any valid IPv4/v6 address. It does not have to be a valid netmask.
+.PP
Examples:
.IP
ipset create foo hash:ip,port
diff --git a/src/ipset.c b/src/ipset.c
index ee36a06..162f477 100644
--- a/src/ipset.c
+++ b/src/ipset.c
@@ -6,12 +6,15 @@
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*/
+#define _GNU_SOURCE
#include <assert.h> /* assert */
#include <stdio.h> /* fprintf */
#include <stdlib.h> /* exit */
+#include <string.h> /* strcmp */
#include <config.h>
#include <libipset/ipset.h> /* ipset library */
+#include <libipset/xlate.h> /* translate to nftables */
int
main(int argc, char *argv[])
@@ -29,7 +32,11 @@ main(int argc, char *argv[])
exit(1);
}
- ret = ipset_parse_argv(ipset, argc, argv);
+ if (!strcmp(basename(argv[0]), "ipset-translate")) {
+ ret = ipset_xlate_argv(ipset, argc, argv);
+ } else {
+ ret = ipset_parse_argv(ipset, argc, argv);
+ }
ipset_fini(ipset);
diff --git a/src/ui.c b/src/ui.c
index 55433b8..5b4a1d7 100644
--- a/src/ui.c
+++ b/src/ui.c
@@ -180,7 +180,7 @@ const struct ipset_envopts ipset_envopts[] = {
{ .name = { "-o", "-output" },
.has_arg = IPSET_MANDATORY_ARG, .flag = IPSET_OPT_MAX,
.parse = ipset_parse_output,
- .help = "plain|save|xml\n"
+ .help = "plain|save|xml|json\n"
" Specify output mode for listing sets.\n"
" Default value for \"list\" command is mode \"plain\"\n"
" and for \"save\" command is mode \"save\".",
diff --git a/tests/cidr.sh b/tests/cidr.sh
index b7d695a..2c4d939 100755
--- a/tests/cidr.sh
+++ b/tests/cidr.sh
@@ -37,6 +37,30 @@ NETS="0.0.0.0/1
ipset="../src/ipset"
+if which netmask >/dev/null 2>&1; then
+ net_first_addr() {
+ netmask -r $1 | cut -d - -f 1
+ }
+ net_last_addr() {
+ netmask -r $1 | cut -d - -f 2 | cut -d ' ' -f 1
+ }
+elif which ipcalc >/dev/null 2>&1; then
+ net_first_addr() {
+ ipcalc $1 | awk '/^Address:/{print $2}'
+ }
+ net_last_addr() {
+ # Netmask tool prints broadcast address as last one, so
+ # prefer that instead of HostMax. Also fix for /31 and /32
+ # being recognized as special by ipcalc.
+ ipcalc $1 | awk '/^(Hostroute|HostMax):/{out=$2}
+ /^Broadcast:/{out=$2}
+ END{print out}'
+ }
+else
+ echo "need either netmask or ipcalc tools"
+ exit 1
+fi
+
case "$1" in
net)
$ipset n test hash:net
@@ -46,9 +70,9 @@ net)
done <<<"$NETS"
while IFS= read x; do
- first=`netmask -r $x | cut -d - -f 1`
+ first=`net_first_addr $x`
$ipset test test $first >/dev/null 2>&1
- last=`netmask -r $x | cut -d - -f 2 | cut -d ' ' -f 1`
+ last=`net_last_addr $x`
$ipset test test $last >/dev/null 2>&1
done <<<"$NETS"
@@ -67,9 +91,9 @@ net,port)
n=1
while IFS= read x; do
- first=`netmask -r $x | cut -d - -f 1`
+ first=`net_first_addr $x`
$ipset test test $first,$n >/dev/null 2>&1
- last=`netmask -r $x | cut -d - -f 2 | cut -d ' ' -f 1`
+ last=`net_last_addr $x`
$ipset test test $last,$n >/dev/null 2>&1
n=$((n+1))
done <<<"$NETS"
diff --git a/tests/comment.t b/tests/comment.t
index a4b9973..8f57919 100644
--- a/tests/comment.t
+++ b/tests/comment.t
@@ -113,7 +113,7 @@
# Hash comment: Stress test with comments and timeout
0 ./netnetgen.sh comment timeout | ipset restore
# Hash comment: List set and check the number of elements
-0 n=`ipset -L test|grep '^10.'|wc -l` && test $n -eq 87040
+0 n=`ipset save test|grep 'add test 10.'|wc -l` && test $n -eq 87040
# Hash comment: Destroy test set
0 ipset destroy test
# Hash comment: create set with timeout
diff --git a/tests/hash:ip,port.t b/tests/hash:ip,port.t
index 7a0e821..f65fb59 100644
--- a/tests/hash:ip,port.t
+++ b/tests/hash:ip,port.t
@@ -62,10 +62,10 @@
0 ipset test test 2.0.0.1,tcp:80
# Test element with UDP protocol
0 ipset test test 2.0.0.1,udp:80
-# Add element with vrrp
-0 ipset add test 2.0.0.1,vrrp:0
-# Test element with vrrp
-0 ipset test test 2.0.0.1,vrrp:0
+# Add element with GRE
+0 ipset add test 2.0.0.1,gre:0
+# Test element with GRE
+0 ipset test test 2.0.0.1,gre:0
# Add element with sctp
0 ipset add test 2.0.0.1,sctp:80
# Test element with sctp
@@ -170,4 +170,122 @@
0 ./check_extensions test 2.0.0.20 700 13 12479
# Counters and timeout: destroy set
0 ipset x test
+# Network: Create a set with timeout and netmask
+0 ipset -N test hash:ip,port --hashsize 128 --netmask 24 timeout 4
+# Network: Add zero valued element
+1 ipset -A test 0.0.0.0,80
+# Network: Test zero valued element
+1 ipset -T test 0.0.0.0,80
+# Network: Delete zero valued element
+1 ipset -D test 0.0.0.0,80
+# Network: Add first random network
+0 ipset -A test 2.0.0.1,8080
+# Network: Add second random network
+0 ipset -A test 192.168.68.69,22
+# Network: Test first random value
+0 ipset -T test 2.0.0.255,8080
+# Network: Test second random value
+0 ipset -T test 192.168.68.95,22
+# Network: Test value not added to the set
+1 ipset -T test 2.0.1.0,8080
+# Network: Add third element
+0 ipset -A test 200.100.10.1,22 timeout 0
+# Network: Add third random network
+0 ipset -A test 200.100.0.12,22
+# Network: Delete the same network
+0 ipset -D test 200.100.0.12,22
+# Network: List set
+0 ipset -L test > .foo0 && ./sort.sh .foo0
+# Network: Check listing
+0 ./diff.sh .foo hash:ip,port.t.list3
+# Sleep 5s so that elements can time out
+0 sleep 5
+# Network: List set
+0 ipset -L test > .foo
+# Network: Check listing
+0 ./diff.sh .foo hash:ip,port.t.list4
+# Network: Flush test set
+0 ipset -F test
+# Network: add element with 1s timeout
+0 ipset add test 200.100.0.12,80 timeout 1
+# Network: readd element with 3s timeout
+0 ipset add test 200.100.0.12,80 timeout 3 -exist
+# Network: sleep 2s
+0 sleep 2s
+# Network: check readded element
+0 ipset test test 200.100.0.12,80
+# Network: Delete test set
+0 ipset -X test
+# Network: Create a set with timeout and bitmask
+0 ipset -N test hash:ip,port --hashsize 128 --bitmask 255.255.255.0 timeout 4
+# Network: Add zero valued element
+1 ipset -A test 0.0.0.0,80
+# Network: Test zero valued element
+1 ipset -T test 0.0.0.0,80
+# Network: Delete zero valued element
+1 ipset -D test 0.0.0.0,80
+# Network: Add first random network
+0 ipset -A test 2.0.0.1,8080
+# Network: Add second random network
+0 ipset -A test 192.168.68.69,22
+# Network: Test first random value
+0 ipset -T test 2.0.0.255,8080
+# Network: Test second random value
+0 ipset -T test 192.168.68.95,22
+# Network: Test value not added to the set
+1 ipset -T test 2.0.1.0,8080
+# Network: Add third element
+0 ipset -A test 200.100.10.1,22 timeout 0
+# Network: Add third random network
+0 ipset -A test 200.100.0.12,22
+# Network: Delete the same network
+0 ipset -D test 200.100.0.12,22
+# Network: List set
+0 ipset -L test > .foo0 && ./sort.sh .foo0
+# Network: Check listing
+0 ./diff.sh .foo hash:ip,port.t.list5
+# Sleep 5s so that elements can time out
+0 sleep 5
+# Network: List set
+0 ipset -L test > .foo
+# Network: Check listing
+0 ./diff.sh .foo hash:ip,port.t.list6
+# Network: Flush test set
+0 ipset -F test
+# Network: add element with 1s timeout
+0 ipset add test 200.100.0.12,80 timeout 1
+# Network: readd element with 3s timeout
+0 ipset add test 200.100.0.12,80 timeout 3 -exist
+# Network: sleep 2s
+0 sleep 2s
+# Network: check readded element
+0 ipset test test 200.100.0.12,80
+# Network: Delete test set
+0 ipset -X test
+# Network: Create a set with bitmask which is not a valid netmask
+0 ipset -N test hash:ip,port --hashsize 128 --bitmask 255.255.0.255
+# Network: Add zero valued element
+1 ipset -A test 0.0.0.0
+# Network: Test zero valued element
+1 ipset -T test 0.0.0.0
+# Network: Delete zero valued element
+1 ipset -D test 0.0.0.0
+# Network: Add first random network
+0 ipset -A test 1.2.3.4,22
+# Network: Add second random network
+0 ipset -A test 1.168.122.124,22
+# Network: Test first random value
+0 ipset -T test 1.2.9.4,22
+# Network: Test second random value
+0 ipset -T test 1.168.68.124,22
+# Network: Test value not added to the set
+1 ipset -T test 2.0.1.0,23
+# Network: Test delete value
+0 ipset -D test 1.168.0.124,22
+# Network: List set
+0 ipset -L test > .foo
+# Network: Check listing
+0 ./diff.sh .foo hash:ip,port.t.list7
+# Network: Delete test set
+0 ipset -X test
# eof
diff --git a/tests/hash:ip,port.t.list2 b/tests/hash:ip,port.t.list2
index ffaedb5..2550422 100644
--- a/tests/hash:ip,port.t.list2
+++ b/tests/hash:ip,port.t.list2
@@ -6,6 +6,6 @@ Size in memory: 480
References: 0
Number of entries: 3
Members:
+2.0.0.1,gre:0
2.0.0.1,tcp:80
2.0.0.1,udp:80
-2.0.0.1,vrrp:0
diff --git a/tests/hash:ip,port.t.list3 b/tests/hash:ip,port.t.list3
new file mode 100644
index 0000000..b2cdc28
--- /dev/null
+++ b/tests/hash:ip,port.t.list3
@@ -0,0 +1,11 @@
+Name: test
+Type: hash:ip,port
+Revision: 7
+Header: family inet hashsize 128 maxelem 65536 timeout 4 bucketsize 12 initval 0xf49ba001 netmask 24
+Size in memory: 408
+References: 0
+Number of entries: 3
+Members:
+192.168.68.0,tcp:22 timeout 3
+2.0.0.0,tcp:8080 timeout 3
+200.100.10.0,tcp:22 timeout 0
diff --git a/tests/hash:ip,port.t.list4 b/tests/hash:ip,port.t.list4
new file mode 100644
index 0000000..c28987a
--- /dev/null
+++ b/tests/hash:ip,port.t.list4
@@ -0,0 +1,9 @@
+Name: test
+Type: hash:ip,port
+Revision: 7
+Header: family inet hashsize 128 maxelem 65536 timeout 4 bucketsize 12 initval 0x18b2277a netmask 24
+Size in memory: 408
+References: 0
+Number of entries: 1
+Members:
+200.100.10.0,tcp:22 timeout 0
diff --git a/tests/hash:ip,port.t.list5 b/tests/hash:ip,port.t.list5
new file mode 100644
index 0000000..b5fa817
--- /dev/null
+++ b/tests/hash:ip,port.t.list5
@@ -0,0 +1,11 @@
+Name: test
+Type: hash:ip,port
+Revision: 7
+Header: family inet hashsize 128 maxelem 65536 timeout 4 bucketsize 12 initval 0x6a0e903a bitmask 255.255.255.0
+Size in memory: 408
+References: 0
+Number of entries: 3
+Members:
+192.168.68.0,tcp:22 timeout 3
+2.0.0.0,tcp:8080 timeout 3
+200.100.10.0,tcp:22 timeout 0
diff --git a/tests/hash:ip,port.t.list6 b/tests/hash:ip,port.t.list6
new file mode 100644
index 0000000..33969cf
--- /dev/null
+++ b/tests/hash:ip,port.t.list6
@@ -0,0 +1,9 @@
+Name: test
+Type: hash:ip,port
+Revision: 7
+Header: family inet hashsize 128 maxelem 65536 timeout 4 bucketsize 12 initval 0x2fcffdca bitmask 255.255.255.0
+Size in memory: 408
+References: 0
+Number of entries: 1
+Members:
+200.100.10.0,tcp:22 timeout 0
diff --git a/tests/hash:ip,port.t.list7 b/tests/hash:ip,port.t.list7
new file mode 100644
index 0000000..f223657
--- /dev/null
+++ b/tests/hash:ip,port.t.list7
@@ -0,0 +1,9 @@
+Name: test
+Type: hash:ip,port
+Revision: 7
+Header: family inet hashsize 128 maxelem 65536 bucketsize 12 initval 0x98bdfa72 bitmask 255.255.0.255
+Size in memory: 312
+References: 0
+Number of entries: 1
+Members:
+1.2.0.4,tcp:22
diff --git a/tests/hash:ip.t b/tests/hash:ip.t
index 3239701..3771437 100644
--- a/tests/hash:ip.t
+++ b/tests/hash:ip.t
@@ -72,7 +72,7 @@
0 n=`ipset list test|grep '^10.0'|wc -l` && test $n -eq 1024
# IP: Destroy sets
0 ipset -X
-# Network: Create a set with timeout
+# Network: Create a set with timeout and netmask
0 ipset -N test iphash --hashsize 128 --netmask 24 timeout 4
# Network: Add zero valued element
1 ipset -A test 0.0.0.0
@@ -136,6 +136,12 @@
0 ipset del test 10.0.0.1-10.0.0.10
# Range: Check number of elements
0 n=`ipset save test|wc -l` && test $n -eq 1
+# Range: Flush set
+0 ipset flush test
+# Range: Add elements in multiple internal batches
+0 ipset add test 10.1.0.0-10.1.64.255
+# Range: Check number of elements
+0 n=`ipset save test|grep '^add test 10.1' | wc -l` && test $n -eq 16640
# Range: Delete test set
0 ipset destroy test
# Timeout: Check that resizing keeps timeout values
@@ -210,4 +216,78 @@ skip which sendip
0 ./check_extensions test 10.255.255.64 600 6 $((6*40))
# Counters and timeout: destroy set
0 ipset x test
+# Network: Create a set with timeout and bitmask
+0 ipset -N test iphash --hashsize 128 --bitmask 255.255.255.0 timeout 4
+# Network: Add zero valued element
+1 ipset -A test 0.0.0.0
+# Network: Test zero valued element
+1 ipset -T test 0.0.0.0
+# Network: Delete zero valued element
+1 ipset -D test 0.0.0.0
+# Network: Add first random network
+0 ipset -A test 2.0.0.1
+# Network: Add second random network
+0 ipset -A test 192.168.68.69
+# Network: Test first random value
+0 ipset -T test 2.0.0.255
+# Network: Test second random value
+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: 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 > .foo0 && ./sort.sh .foo0
+# Network: Check listing
+0 ./diff.sh .foo hash:ip.t.list4
+# Sleep 5s so that elements can time out
+0 sleep 5
+# Network: List set
+0 ipset -L test > .foo
+# Network: Check listing
+0 ./diff.sh .foo hash:ip.t.list5
+# Network: Flush test set
+0 ipset -F test
+# Network: add element with 1s timeout
+0 ipset add test 200.100.0.12 timeout 1
+# Network: readd element with 3s timeout
+0 ipset add test 200.100.0.12 timeout 3 -exist
+# Network: sleep 2s
+0 sleep 2s
+# Network: check readded element
+0 ipset test test 200.100.0.12
+# Network: Delete test set
+0 ipset -X test
+# Network: Create a set with both bitmask and netmask
+1 ipset -N test iphash --hashsize 128 --bitmask 255.255.0.255 --netmask 24
+# Network: Create a set with bitmask which is not a valid netmask
+0 ipset -N test iphash --hashsize 128 --bitmask 255.255.0.255
+# Network: Add zero valued element
+1 ipset -A test 0.0.0.0
+# Network: Test zero valued element
+1 ipset -T test 0.0.0.0
+# Network: Delete zero valued element
+1 ipset -D test 0.0.0.0
+# Network: Add first random network
+0 ipset -A test 1.2.3.4
+# Network: Add second random network
+0 ipset -A test 1.2.4.5
+# Network: Test first random value
+0 ipset -T test 1.2.9.4
+# Network: Test second random value
+0 ipset -T test 1.2.9.5
+# Network: Test value not added to the set
+1 ipset -T test 2.0.1.0
+# Network: Test delete value
+0 ipset -D test 1.2.0.5
+# Network: List set
+0 ipset -L test > .foo
+# Network: Check listing
+0 ./diff.sh .foo hash:ip.t.list6
+# Network: Delete test set
+0 ipset -X test
# eof
diff --git a/tests/hash:ip.t.list4 b/tests/hash:ip.t.list4
new file mode 100644
index 0000000..5f92afa
--- /dev/null
+++ b/tests/hash:ip.t.list4
@@ -0,0 +1,11 @@
+Name: test
+Type: hash:ip
+Revision: 5
+Header: family inet hashsize 128 maxelem 65536 bitmask 255.255.255.0 timeout 4 bucketsize 12 initval 0xfe970e91
+Size in memory: 528
+References: 0
+Number of entries: 3
+Members:
+192.168.68.0 timeout 3
+2.0.0.0 timeout 3
+200.100.10.0 timeout 0
diff --git a/tests/hash:ip.t.list5 b/tests/hash:ip.t.list5
new file mode 100644
index 0000000..9a29e75
--- /dev/null
+++ b/tests/hash:ip.t.list5
@@ -0,0 +1,9 @@
+Name: test
+Type: hash:ip
+Revision: 5
+Header: family inet hashsize 128 maxelem 65536 bitmask 255.255.255.0 timeout 4 bucketsize 12 initval 0xbc66e38a
+Size in memory: 528
+References: 0
+Number of entries: 1
+Members:
+200.100.10.0 timeout 0
diff --git a/tests/hash:ip.t.list6 b/tests/hash:ip.t.list6
new file mode 100644
index 0000000..44c5a49
--- /dev/null
+++ b/tests/hash:ip.t.list6
@@ -0,0 +1,9 @@
+Name: test
+Type: hash:ip
+Revision: 6
+Header: family inet hashsize 128 maxelem 65536 bitmask 255.255.0.255 bucketsize 12 initval 0xd7d821e1
+Size in memory: 296
+References: 0
+Number of entries: 1
+Members:
+1.2.0.4
diff --git a/tests/hash:net,iface.t b/tests/hash:net,iface.t
index e594cca..444f230 100644
--- a/tests/hash:net,iface.t
+++ b/tests/hash:net,iface.t
@@ -132,6 +132,10 @@
0 (set -e; for x in `seq 0 63`; do ipset add test 10.0.0.0/16,eth$x; done)
# Check listing
0 n=`ipset list test | grep -v Revision: | wc -l` && test $n -eq 71
+# Flush test set
+0 ipset flush test
+# Try to add more than 64 clashing entries
+1 (set -e; for x in `seq 0 64`; do ipset add test 10.0.0.0/16,eth$x; done)
# Delete test set
0 ipset destroy test
# Check all possible CIDR values
diff --git a/tests/hash:net,net.t b/tests/hash:net,net.t
index feb13d9..41189b7 100644
--- a/tests/hash:net,net.t
+++ b/tests/hash:net,net.t
@@ -166,4 +166,110 @@
0 ./check_extensions test 2.0.0.0/25,2.0.0.0/25 700 13 12479
# Counters and timeout: destroy set
0 ipset x test
+# Network: Create a set with timeout and netmask
+0 ipset -N test hash:net,net --hashsize 128 --netmask 24 timeout 4
+# Network: Add first random network
+0 ipset -A test 2.0.10.1,2.10.10.254
+# Network: Add second random network
+0 ipset -A test 192.168.68.1,192.168.68.254
+# Network: Test first random value
+0 ipset -T test 2.0.10.11,2.10.10.25
+# Network: Test second random value
+0 ipset -T test 192.168.68.11,192.168.68.5
+# Network: Test value not added to the set
+1 ipset -T test 2.10.1.0,21.0.1.0
+# Network: Add third element
+0 ipset -A test 200.100.10.1,200.100.10.100 timeout 0
+# Network: Add third random network
+0 ipset -A test 200.100.0.12,200.100.0.13
+# Network: Delete the same network
+0 ipset -D test 200.100.0.12,200.100.0.13
+# Network: List set
+0 ipset -L test > .foo0 && ./sort.sh .foo0
+# Network: Check listing
+0 ./diff.sh .foo hash:net,net.t.list3
+# Sleep 5s so that elements can time out
+0 sleep 5
+# Network: List set
+0 ipset -L test > .foo
+# Network: Check listing
+0 ./diff.sh .foo hash:net,net.t.list4
+# Network: Flush test set
+0 ipset -F test
+# Network: add element with 1s timeout
+0 ipset add test 200.100.0.12,80.20.0.12 timeout 1
+# Network: readd element with 3s timeout
+0 ipset add test 200.100.0.12,80.20.0.12 timeout 3 -exist
+# Network: sleep 2s
+0 sleep 2s
+# Network: check readded element
+0 ipset test test 200.100.0.12,80.20.0.12
+# Network: Delete test set
+0 ipset -X test
+# Network: Create a set with timeout and bitmask
+0 ipset -N test hash:net,net --hashsize 128 --bitmask 255.255.255.0 timeout 4
+# Network: Add first random network
+0 ipset -A test 2.0.10.1,2.10.10.254
+# Network: Add second random network
+0 ipset -A test 192.168.68.1,192.168.68.254
+# Network: Test first random value
+0 ipset -T test 2.0.10.11,2.10.10.25
+# Network: Test second random value
+0 ipset -T test 192.168.68.11,192.168.68.5
+# Network: Test value not added to the set
+1 ipset -T test 2.10.1.0,21.0.1.0
+# Network: Add third element
+0 ipset -A test 200.100.10.1,200.100.10.100 timeout 0
+# Network: Add third random network
+0 ipset -A test 200.100.0.12,200.100.0.13
+# Network: Delete the same network
+0 ipset -D test 200.100.0.12,200.100.0.13
+# Network: List set
+0 ipset -L test > .foo0 && ./sort.sh .foo0
+# Network: Check listing
+0 ./diff.sh .foo hash:net,net.t.list5
+# Sleep 5s so that elements can time out
+0 sleep 5
+# Network: List set
+0 ipset -L test > .foo
+# Network: Check listing
+0 ./diff.sh .foo hash:net,net.t.list6
+# Network: Flush test set
+0 ipset -F test
+# Network: add element with 1s timeout
+0 ipset add test 200.100.0.12,80.20.0.12 timeout 1
+# Network: readd element with 3s timeout
+0 ipset add test 200.100.0.12,80.20.0.12 timeout 3 -exist
+# Network: sleep 2s
+0 sleep 2s
+# Network: check readded element
+0 ipset test test 200.100.0.12,80.20.0.12
+# Network: Delete test set
+0 ipset -X test
+# Network: Create a set with bitmask which is not a valid netmask
+0 ipset -N test hash:net,net --hashsize 128 --bitmask 255.255.0.255
+# Network: Add zero valued element
+1 ipset -A test 0.0.0.0
+# Network: Test zero valued element
+1 ipset -T test 0.0.0.0
+# Network: Delete zero valued element
+1 ipset -D test 0.0.0.0
+# Network: Add first random network
+0 ipset -A test 1.2.3.4,22.23.24.25
+# Network: Add second random network
+0 ipset -A test 1.168.122.124,122.23.45.50
+# Network: Test first random value
+0 ipset -T test 1.2.43.4,22.23.2.25
+# Network: Test second random value
+0 ipset -T test 1.168.12.124,122.23.4.50
+# Network: Test value not added to the set
+1 ipset -T test 2.168.122.124,22.23.45.50
+# Network: Test delete value
+0 ipset -D test 1.168.12.124,122.23.0.50
+# Network: List set
+0 ipset -L test > .foo
+# Network: Check listing
+0 ./diff.sh .foo hash:net,net.t.list7
+# Network: Delete test set
+0 ipset -X test
# eof
diff --git a/tests/hash:net,net.t.list3 b/tests/hash:net,net.t.list3
new file mode 100644
index 0000000..fc5b97a
--- /dev/null
+++ b/tests/hash:net,net.t.list3
@@ -0,0 +1,11 @@
+Name: test
+Type: hash:net,net
+Revision: 4
+Header: family inet hashsize 128 maxelem 65536 timeout 4 bucketsize 12 initval 0xe17e4732 netmask 24
+Size in memory: 848
+References: 0
+Number of entries: 3
+Members:
+192.168.68.0,192.168.68.0 timeout 3
+2.0.10.0,2.10.10.0 timeout 3
+200.100.10.0,200.100.10.0 timeout 0
diff --git a/tests/hash:net,net.t.list4 b/tests/hash:net,net.t.list4
new file mode 100644
index 0000000..908cab6
--- /dev/null
+++ b/tests/hash:net,net.t.list4
@@ -0,0 +1,9 @@
+Name: test
+Type: hash:net,net
+Revision: 4
+Header: family inet hashsize 128 maxelem 65536 timeout 4 bucketsize 12 initval 0xb69e293e netmask 24
+Size in memory: 848
+References: 0
+Number of entries: 1
+Members:
+200.100.10.0,200.100.10.0 timeout 0
diff --git a/tests/hash:net,net.t.list5 b/tests/hash:net,net.t.list5
new file mode 100644
index 0000000..0ff37fb
--- /dev/null
+++ b/tests/hash:net,net.t.list5
@@ -0,0 +1,11 @@
+Name: test
+Type: hash:net,net
+Revision: 4
+Header: family inet hashsize 128 maxelem 65536 timeout 4 bucketsize 12 initval 0xe17e4732 bitmask 255.255.255.0
+Size in memory: 848
+References: 0
+Number of entries: 3
+Members:
+192.168.68.0,192.168.68.0 timeout 3
+2.0.10.0,2.10.10.0 timeout 3
+200.100.10.0,200.100.10.0 timeout 0
diff --git a/tests/hash:net,net.t.list6 b/tests/hash:net,net.t.list6
new file mode 100644
index 0000000..84beb5b
--- /dev/null
+++ b/tests/hash:net,net.t.list6
@@ -0,0 +1,9 @@
+Name: test
+Type: hash:net,net
+Revision: 4
+Header: family inet hashsize 128 maxelem 65536 timeout 4 bucketsize 12 initval 0xb69e293e bitmask 255.255.255.0
+Size in memory: 848
+References: 0
+Number of entries: 1
+Members:
+200.100.10.0,200.100.10.0 timeout 0
diff --git a/tests/hash:net,net.t.list7 b/tests/hash:net,net.t.list7
new file mode 100644
index 0000000..6601795
--- /dev/null
+++ b/tests/hash:net,net.t.list7
@@ -0,0 +1,9 @@
+Name: test
+Type: hash:net,net
+Revision: 4
+Header: family inet hashsize 128 maxelem 65536 bucketsize 12 initval 0x6223fef7 bitmask 255.255.0.255
+Size in memory: 736
+References: 0
+Number of entries: 1
+Members:
+1.2.0.4,22.23.0.25
diff --git a/tests/hash:net,port,net.t b/tests/hash:net,port,net.t
index f5a578a..2c9516b 100644
--- a/tests/hash:net,port,net.t
+++ b/tests/hash:net,port,net.t
@@ -52,6 +52,12 @@
0 ipset add test 10.0.0.0-10.0.3.255,tcp:80-82,192.168.0.0-192.168.2.255
# Check that correct number of elements are added
0 n=`ipset list test|grep '^10.0'|wc -l` && test $n -eq 6
+# Flush set
+0 ipset flush test
+# Add 0/0 networks
+0 ipset add test 0.0.0.0/0,tcp:1-2,192.168.230.128/25
+# Check that correct number of elements are added
+0 n=`ipset list test|grep '^0'|wc -l` && test $n -eq 2
# Destroy set
0 ipset -X test
# Create test set with timeout support
diff --git a/tests/netnetgen.sh b/tests/netnetgen.sh
index f2a31cc..32aac18 100755
--- a/tests/netnetgen.sh
+++ b/tests/netnetgen.sh
@@ -6,7 +6,7 @@ while [ -n "$1" ]; do
comment=" comment"
;;
timeout)
- timeout=" timeout 5"
+ timeout=" timeout 60"
;;
*)
;;
diff --git a/tests/restore.t b/tests/restore.t
index ffde2d1..dda143f 100644
--- a/tests/restore.t
+++ b/tests/restore.t
@@ -6,4 +6,28 @@
0 ipset x
# Check auto-increasing maximal number of sets
0 ./setlist_resize.sh
+# Create bitmap set with timeout
+0 ipset create test1 bitmap:ip range 2.0.0.1-2.1.0.0 timeout 5
+# Add element to bitmap set
+0 ipset add test1 2.0.0.2 timeout 30
+# Create hash set with timeout
+0 ipset -N test2 iphash --hashsize 128 timeout 4
+# Add element to hash set
+0 ipset add test2 2.0.0.3 timeout 30
+# Create list set with timeout
+0 ipset -N test3 list:set timeout 3
+# Add bitmap set to list set
+0 ipset a test3 test1 timeout 30
+# Add hash set to list set
+0 ipset a test3 test2 timeout 30
+# Flush list set
+0 ipset f test3
+# Destroy all sets
+0 ipset x
+# Remove the ip_set_list_set kernel module
+0 rmmod ip_set_list_set
+# Remove the ip_set_bitmap_ip kernel module
+0 rmmod ip_set_bitmap_ip
+# Remove the ip_set_hash_ip kernel module
+0 rmmod ip_set_hash_ip
# eof
diff --git a/tests/setlist_resize.sh b/tests/setlist_resize.sh
index 3255656..848f1d1 100755
--- a/tests/setlist_resize.sh
+++ b/tests/setlist_resize.sh
@@ -12,7 +12,7 @@ for x in ip_set_list_set ip_set_hash_netiface ip_set_hash_ipportnet \
ip_set_hash_netportnet ip_set_hash_ipmark ip_set_hash_mac \
ip_set_bitmap_port ip_set_bitmap_ipmac \
ip_set_bitmap_ip xt_set ip_set; do
- rmmod $x
+ rmmod $x >/dev/null 2>&1
done
create() {
@@ -31,6 +31,6 @@ for x in `seq 1 $loop`; do
test `$ipset l -n | wc -l` -eq 1024 || exit 1
$ipset x
test `lsmod|grep -w ^ip_set_hash_ip | awk '{print $3}'` -eq 0 || exit 1
- rmmod ip_set_hash_ip
- rmmod ip_set
+ rmmod ip_set_hash_ip >/dev/null 2>&1
+ rmmod ip_set >/dev/null 2>&1
done
diff --git a/tests/xlate/ipset-translate b/tests/xlate/ipset-translate
new file mode 120000
index 0000000..91980c1
--- /dev/null
+++ b/tests/xlate/ipset-translate
@@ -0,0 +1 @@
+../../src/ipset \ No newline at end of file
diff --git a/tests/xlate/runtest.sh b/tests/xlate/runtest.sh
new file mode 100755
index 0000000..8b42f0b
--- /dev/null
+++ b/tests/xlate/runtest.sh
@@ -0,0 +1,35 @@
+#!/bin/bash
+
+DIFF=$(which diff)
+if [ ! -x "$DIFF" ] ; then
+ echo "ERROR: missing diff"
+ exit 1
+fi
+
+ipset=${IPSET_BIN:-../../src/ipset}
+ipset_xlate=${IPSET_XLATE_BIN:-$(dirname $0)/ipset-translate}
+
+$ipset restore < xlate.t
+rc=$?
+$ipset destroy
+if [ $rc -ne 0 ]
+then
+ echo -e "[\033[0;31mERROR\033[0m] invalid test input"
+ exit 1
+fi
+
+TMP=$(mktemp)
+$ipset_xlate restore < xlate.t &> $TMP
+if [ $? -ne 0 ]
+then
+ cat $TMP
+ echo -e "[\033[0;31mERROR\033[0m] failed to run ipset-translate"
+ exit 1
+fi
+${DIFF} -u xlate.t.nft $TMP
+if [ $? -eq 0 ]
+then
+ echo -e "[\033[0;32mOK\033[0m] tests are fine!"
+else
+ echo -e "[\033[0;31mERROR\033[0m] unexpected ipset to nftables translation"
+fi
diff --git a/tests/xlate/xlate.t b/tests/xlate/xlate.t
new file mode 100644
index 0000000..38cbc78
--- /dev/null
+++ b/tests/xlate/xlate.t
@@ -0,0 +1,57 @@
+create hip1 hash:ip
+add hip1 192.168.10.2
+add hip1 192.168.10.3
+create hip2 hash:ip hashsize 128 bucketsize 255 timeout 4
+add hip2 192.168.10.3
+add hip2 192.168.10.4 timeout 10
+create hip3 hash:ip counters
+add hip3 192.168.10.3 packets 5 bytes 3456
+create hip4 hash:ip netmask 24
+add hip4 192.168.10.0
+create hip5 hash:ip maxelem 24
+add hip5 192.168.10.0
+create hip6 hash:ip comment
+add hip6 192.168.10.1
+add hip6 192.168.10.2 comment "this is a comment"
+create ipp1 hash:ip,port
+add ipp1 192.168.10.1,0
+add ipp1 192.168.10.2,5
+create ipp2 hash:ip,port timeout 4
+add ipp2 192.168.10.1,0 timeout 12
+add ipp2 192.168.10.2,5
+create ipp3 hash:ip,port counters
+add ipp3 192.168.10.3,20 packets 5 bytes 3456
+create ipp4 hash:ip,port timeout 4 counters
+add ipp4 192.168.10.3,20 packets 5 bytes 3456
+create bip1 bitmap:ip range 2.0.0.1-2.0.1.1 timeout 5
+create bip2 bitmap:ip range 10.0.0.0/8 netmask 24 timeout 5
+add bip2 10.10.10.0
+add bip2 10.10.20.0 timeout 12
+create net1 hash:net
+add net1 192.168.10.0/24
+create net2 hash:net,net
+add net2 192.168.10.0/24,192.168.20.0/24
+create hm1 hash:mac
+add hm1 aa:bb:cc:dd:ee:ff
+create him1 hash:ip,mac
+add him1 1.1.1.1,aa:bb:cc:dd:ee:ff
+create ni1 hash:net,iface
+add ni1 1.1.1.0/24,eth0
+create nip1 hash:net,port
+add nip1 1.1.1.0/24,22
+create npn1 hash:net,port,net
+add npn1 1.1.1.0/24,22,2.2.2.0/24
+create nn1 hash:net,net
+add nn1 1.1.1.0/24,2.2.2.0/24
+create ipn1 hash:ip,port,net
+add ipn1 1.1.1.1,22,2.2.2.0/24
+create ipi1 hash:ip,port,ip
+add ipi1 1.1.1.1,22,2.2.2.2
+create im1 hash:ip,mark
+add im1 1.1.1.1,0x123456
+create bp1 bitmap:port range 1-1024
+add bp1 22
+create bim1 bitmap:ip,mac range 1.1.1.0/24
+add bim1 1.1.1.1,aa:bb:cc:dd:ee:ff
+create hn6 hash:net family inet6
+add hn6 fe80::/64
diff --git a/tests/xlate/xlate.t.nft b/tests/xlate/xlate.t.nft
new file mode 100644
index 0000000..8fb2a29
--- /dev/null
+++ b/tests/xlate/xlate.t.nft
@@ -0,0 +1,58 @@
+add table inet global
+add set inet global hip1 { type ipv4_addr; }
+add element inet global hip1 { 192.168.10.2 }
+add element inet global hip1 { 192.168.10.3 }
+add set inet global hip2 { type ipv4_addr; timeout 4s; }
+add element inet global hip2 { 192.168.10.3 }
+add element inet global hip2 { 192.168.10.4 timeout 10s }
+add set inet global hip3 { type ipv4_addr; counter; }
+add element inet global hip3 { 192.168.10.3 counter packets 5 bytes 3456 }
+add set inet global hip4 { type ipv4_addr; flags interval; }
+add element inet global hip4 { 192.168.10.0/24 }
+add set inet global hip5 { type ipv4_addr; size 24; }
+add element inet global hip5 { 192.168.10.0 }
+add set inet global hip6 { type ipv4_addr; }
+add element inet global hip6 { 192.168.10.1 }
+add element inet global hip6 { 192.168.10.2 comment "this is a comment" }
+add set inet global ipp1 { type ipv4_addr . inet_proto . inet_service; }
+add element inet global ipp1 { 192.168.10.1 . tcp . 0 }
+add element inet global ipp1 { 192.168.10.2 . tcp . 5 }
+add set inet global ipp2 { type ipv4_addr . inet_proto . inet_service; timeout 4s; }
+add element inet global ipp2 { 192.168.10.1 . tcp . 0 timeout 12s }
+add element inet global ipp2 { 192.168.10.2 . tcp . 5 }
+add set inet global ipp3 { type ipv4_addr . inet_proto . inet_service; counter; }
+add element inet global ipp3 { 192.168.10.3 . tcp . 20 counter packets 5 bytes 3456 }
+add set inet global ipp4 { type ipv4_addr . inet_proto . inet_service; counter; timeout 4s; }
+add element inet global ipp4 { 192.168.10.3 . tcp . 20 counter packets 5 bytes 3456 }
+add set inet global bip1 { type ipv4_addr; timeout 5s; }
+add set inet global bip2 { type ipv4_addr; timeout 5s; flags interval; }
+add element inet global bip2 { 10.10.10.0/24 }
+add element inet global bip2 { 10.10.20.0/24 timeout 12s }
+add set inet global net1 { type ipv4_addr; flags interval; }
+add element inet global net1 { 192.168.10.0/24 }
+add set inet global net2 { type ipv4_addr . ipv4_addr; flags interval; }
+add element inet global net2 { 192.168.10.0/24 . 192.168.20.0/24 }
+add set inet global hm1 { type ether_addr; }
+add element inet global hm1 { aa:bb:cc:dd:ee:ff }
+add set inet global him1 { type ipv4_addr . ether_addr; }
+add element inet global him1 { 1.1.1.1 . aa:bb:cc:dd:ee:ff }
+add set inet global ni1 { type ipv4_addr . ifname; flags interval; }
+add element inet global ni1 { 1.1.1.0/24 . eth0 }
+add set inet global nip1 { type ipv4_addr . inet_proto . inet_service; flags interval; }
+add element inet global nip1 { 1.1.1.0/24 . tcp . 22 }
+add set inet global npn1 { type ipv4_addr . inet_proto . inet_service . ipv4_addr; flags interval; }
+add element inet global npn1 { 1.1.1.0/24 . tcp . 22 . 2.2.2.0/24 }
+add set inet global nn1 { type ipv4_addr . ipv4_addr; flags interval; }
+add element inet global nn1 { 1.1.1.0/24 . 2.2.2.0/24 }
+add set inet global ipn1 { type ipv4_addr . inet_proto . inet_service . ipv4_addr; flags interval; }
+add element inet global ipn1 { 1.1.1.1 . tcp . 22 . 2.2.2.0/24 }
+add set inet global ipi1 { type ipv4_addr . inet_proto . inet_service . ipv4_addr; }
+add element inet global ipi1 { 1.1.1.1 . tcp . 22 . 2.2.2.2 }
+add set inet global im1 { type ipv4_addr . mark; }
+add element inet global im1 { 1.1.1.1 . 0x00123456 }
+add set inet global bp1 { type inet_service; }
+add element inet global bp1 { 22 }
+add set inet global bim1 { type ipv4_addr . ether_addr; }
+add element inet global bim1 { 1.1.1.1 . aa:bb:cc:dd:ee:ff }
+add set inet global hn6 { type ipv6_addr; flags interval; }
+add element inet global hn6 { fe80::/64 }