diff options
75 files changed, 1643 insertions, 341 deletions
@@ -16,6 +16,8 @@ Makefile.in *.mod.o.cmd *.mod.cmd *.mod +*.order.cmd +*.symvers.cmd .tmp_versions Module.symvers modules.order @@ -1,3 +1,51 @@ +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) diff --git a/Make_global.am b/Make_global.am index ed92cfe..9ba6adf 100644 --- a/Make_global.am +++ b/Make_global.am @@ -69,7 +69,7 @@ # interface. # curr:rev:age -LIBVERSION = 16:0:3 +LIBVERSION = 17:0:4 AM_CPPFLAGS = $(kinclude_CFLAGS) $(all_includes) -I$(top_srcdir)/include diff --git a/configure.ac b/configure.ac index 1acc976..db9e3f8 100644 --- a/configure.ac +++ b/configure.ac @@ -1,10 +1,10 @@ dnl Boilerplate -AC_INIT([ipset], [7.15], [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 @@ -14,6 +14,7 @@ LT_CONFIG_LTDL_DIR([libltdl]) LTDL_INIT([nonrecursive]) PKG_PROG_PKG_CONFIG +PKG_INSTALLDIR dnl Shortcut: Linux supported alone case "$host" in @@ -27,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 @@ -76,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" != "" @@ -204,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 @@ -443,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) @@ -453,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) @@ -716,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 @@ -746,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' \ @@ -837,11 +866,15 @@ else AC_SUBST(HAVE_NLMSG_UNICAST, undef) fi -AC_MSG_CHECKING([kernel source for kvzalloc() in mm.h]) +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/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/kernel/ChangeLog b/kernel/ChangeLog index 0ee716d..1b05fbd 100644 --- a/kernel/ChangeLog +++ b/kernel/ChangeLog @@ -1,3 +1,63 @@ +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) diff --git a/kernel/include/linux/netfilter/ipset/ip_set.h b/kernel/include/linux/netfilter/ipset/ip_set.h index 3a6963c..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; }; @@ -200,7 +202,7 @@ struct ip_set_region { }; /* Max range where every element is added/deleted in one step */ -#define IPSET_MAX_RANGE (1<<20) +#define IPSET_MAX_RANGE (1<<14) /* The max revision number supported by any set type + 1 */ #define IPSET_REVISION_MAX 9 @@ -245,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 */ @@ -528,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 4d2c446..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 @@ -406,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; @@ -418,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) @@ -507,6 +507,10 @@ static inline struct nlmsghdr *nfnl_msg_put(struct sk_buff *skb, u32 portid, 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) { @@ -516,6 +520,21 @@ 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 @@ -607,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 0fdafb7..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); @@ -1082,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; @@ -1161,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); @@ -1178,17 +1179,50 @@ 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, @@ -1204,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 @@ -1217,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)) { @@ -1239,6 +1272,9 @@ IPSET_CBFN(ip_set_destroy, struct net *net, struct sock *ctnl, inst->is_destroyed = false; } else { 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) { @@ -1249,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: @@ -1350,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. @@ -1408,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; @@ -1750,13 +1792,22 @@ CALL_AD(struct net *net, struct sock *ctnl, struct sk_buff *skb, 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; @@ -1775,11 +1826,12 @@ CALL_AD(struct net *net, struct sock *ctnl, struct sk_buff *skb, 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, @@ -2429,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; @@ -2446,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); @@ -2524,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 baa5e14..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; @@ -148,22 +150,20 @@ hash_ip4_uadt(struct ip_set *set, struct nlattr *tb[], hosts = h->netmask == 32 ? 1 : 2 << (32 - h->netmask - 1); - /* 64bit division is not allowed on 32bit */ - if (((u64)ip_to - ip + 1) >> (32 - h->netmask) > IPSET_MAX_RANGE) - return -ERANGE; - - 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; @@ -188,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) { @@ -230,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; @@ -269,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; @@ -296,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 7e7eede..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]) @@ -150,13 +150,14 @@ hash_ipmark4_uadt(struct ip_set *set, struct nlattr *tb[], ip_set_mask_from_to(ip, ip_to, cidr); } - if (((u64)ip_to - ip + 1) > IPSET_MAX_RANGE) - return -ERANGE; - 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 09837c3..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]) { @@ -174,17 +185,18 @@ hash_ipport4_uadt(struct ip_set *set, struct nlattr *tb[], swap(port, port_to); } - if (((u64)ip_to - ip + 1)*(port_to - port + 1) > IPSET_MAX_RANGE) - return -ERANGE; - if (retried) ip = ntohl(h->next.ip); 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)) @@ -254,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); } @@ -299,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]) { @@ -357,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 0be8f53..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; @@ -181,17 +181,18 @@ hash_ipportip4_uadt(struct ip_set *set, struct nlattr *tb[], swap(port, port_to); } - if (((u64)ip_to - ip + 1)*(port_to - port + 1) > IPSET_MAX_RANGE) - return -ERANGE; - if (retried) ip = ntohl(h->next.ip); 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 8f3c069..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; @@ -254,9 +254,6 @@ hash_ipportnet4_uadt(struct ip_set *set, struct nlattr *tb[], swap(port, port_to); } - if (((u64)ip_to - ip + 1)*(port_to - port + 1) > IPSET_MAX_RANGE) - return -ERANGE; - ip2_to = ip2_from; if (tb[IPSET_ATTR_IP2_TO]) { ret = ip_set_get_hostipaddr4(tb[IPSET_ATTR_IP2_TO], &ip2_to); @@ -283,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 5dfd0ed..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, ipn, n = 0; + u32 ip = 0, ip_to = 0, i = 0; int ret; if (tb[IPSET_ATTR_LINENO]) @@ -189,19 +189,16 @@ hash_net4_uadt(struct ip_set *set, struct nlattr *tb[], if (ip + UINT_MAX == ip_to) return -IPSET_ERR_HASH_RANGE; } - ipn = ip; - do { - ipn = ip_set_range_to_cidr(ipn, ip_to, &e.cidr); - n++; - } while (ipn++ < ip_to); - - if (n > IPSET_MAX_RANGE) - return -ERANGE; 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 b25400a..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, ipn, n = 0; + u32 ip = 0, ip_to = 0, i = 0; int ret; if (tb[IPSET_ATTR_LINENO]) @@ -257,19 +257,16 @@ hash_netiface4_uadt(struct ip_set *set, struct nlattr *tb[], } else { ip_set_mask_from_to(ip, ip_to, e.cidr); } - ipn = ip; - do { - ipn = ip_set_range_to_cidr(ipn, ip_to, &e.cidr); - n++; - } while (ipn++ < ip_to); - - if (n > IPSET_MAX_RANGE) - return -ERANGE; 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); @@ -404,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 3d09eef..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,13 +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, ipn; - u64 n = 0, m = 0; + u32 ip2 = 0, ip2_from = 0, ip2_to = 0, i = 0; int ret; if (tb[IPSET_ATTR_LINENO]) @@ -213,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; @@ -245,19 +247,6 @@ hash_netnet4_uadt(struct ip_set *set, struct nlattr *tb[], } else { ip_set_mask_from_to(ip2_from, ip2_to, e.cidr[1]); } - ipn = ip; - do { - ipn = ip_set_range_to_cidr(ipn, ip_to, &e.cidr[0]); - n++; - } while (ipn++ < ip_to); - ipn = ip2_from; - do { - ipn = ip_set_range_to_cidr(ipn, ip2_to, &e.cidr[1]); - m++; - } while (ipn++ < ip2_to); - - if (n*m > IPSET_MAX_RANGE) - return -ERANGE; if (retried) { ip = ntohl(h->next.ip[0]); @@ -270,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)) @@ -404,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); } @@ -414,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]) @@ -453,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]); @@ -484,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 cf70324..8a2a7c5 100644 --- a/kernel/net/netfilter/ipset/ip_set_hash_netport.c +++ b/kernel/net/netfilter/ipset/ip_set_hash_netport.c @@ -155,12 +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, ipn; - u64 n = 0; + u32 port, port_to, p = 0, ip = 0, ip_to = 0, i = 0; bool with_ports = false; u8 cidr; int ret; @@ -237,14 +236,6 @@ hash_netport4_uadt(struct ip_set *set, struct nlattr *tb[], } else { ip_set_mask_from_to(ip, ip_to, e.cidr + 1); } - ipn = ip; - do { - ipn = ip_set_range_to_cidr(ipn, ip_to, &cidr); - n++; - } while (ipn++ < ip_to); - - if (n*(port_to - port + 1) > IPSET_MAX_RANGE) - return -ERANGE; if (retried) { ip = ntohl(h->next.ip); @@ -256,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 57be102..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,17 +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, ipn; - u64 n = 0, m = 0; + u32 ip2_from = 0, ip2_to = 0, ip2, i = 0; bool with_ports = false; int ret; @@ -286,19 +296,6 @@ hash_netportnet4_uadt(struct ip_set *set, struct nlattr *tb[], } else { ip_set_mask_from_to(ip2_from, ip2_to, e.cidr[1]); } - ipn = ip; - do { - ipn = ip_set_range_to_cidr(ipn, ip_to, &e.cidr[0]); - n++; - } while (ipn++ < ip_to); - ipn = ip2_from; - do { - ipn = ip_set_range_to_cidr(ipn, ip2_to, &e.cidr[1]); - m++; - } while (ipn++ < ip2_to); - - if (n*m*(port_to - port + 1) > IPSET_MAX_RANGE) - return -ERANGE; if (retried) { ip = ntohl(h->next.ip[0]); @@ -311,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 @@ -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 * @@ -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 76bab74..49c97a1 100644 --- a/lib/errcode.c +++ b/lib/errcode.c @@ -44,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 73e67db..c910d88 100644 --- a/lib/ipset.c +++ b/lib/ipset.c @@ -30,6 +30,7 @@ #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; @@ -234,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\".", @@ -428,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); @@ -936,10 +939,10 @@ static const char *cmd_prefix[] = { [IPSET_TEST] = "test SETNAME", }; -static const struct ipset_xlate_set * +static struct ipset_xlate_set * ipset_xlate_set_get(struct ipset *ipset, const char *name) { - const struct ipset_xlate_set *set; + struct ipset_xlate_set *set; list_for_each_entry(set, &ipset->xlate_sets, list) { if (!strcmp(set->name, name)) @@ -949,18 +952,6 @@ ipset_xlate_set_get(struct ipset *ipset, const char *name) return NULL; } -static const struct ipset_type *ipset_xlate_type_get(struct ipset *ipset, - const char *name) -{ - const struct ipset_xlate_set *set; - - set = ipset_xlate_set_get(ipset, name); - if (!set) - return NULL; - - return set->type; -} - static int ipset_parser(struct ipset *ipset, int oargc, char *oargv[]) { @@ -970,7 +961,7 @@ ipset_parser(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; @@ -1139,6 +1130,7 @@ ipset_parser(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); @@ -1148,11 +1140,11 @@ ipset_parser(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); @@ -1282,8 +1274,16 @@ ipset_parser(struct ipset *ipset, int oargc, char *oargv[]) if (!ipset->xlate) { type = ipset_type_get(session, cmd); } else { - type = ipset_xlate_type_get(ipset, arg0); - ipset_session_data_set(session, IPSET_OPT_TYPE, type); + 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); @@ -1552,7 +1552,7 @@ ipset_fini(struct ipset *ipset) } /* Ignore the set family, use inet. */ -static const char *ipset_xlate_family(uint8_t family) +static const char *ipset_xlate_family(uint8_t family UNUSED) { return "inet"; } @@ -1709,6 +1709,10 @@ ipset_xlate_type_to_nftables(int family, enum ipset_xlate_set_type type, else if (family == AF_INET6) return "ipv6_addr"; break; + case IPSET_XLATE_TYPE_UNKNOWN: + break; + default: + break; } /* This should not ever happen. */ return "unknown"; @@ -1733,7 +1737,6 @@ static int ipset_xlate(struct ipset *ipset, enum ipset_cmd cmd, char buf[64]; bool concat; char *term; - int i; session = ipset_session(ipset); data = ipset_session_data(session); @@ -1847,7 +1850,7 @@ static int ipset_xlate(struct ipset *ipset, enum ipset_cmd cmd, return -1; case IPSET_CMD_LIST: if (!set) { - printf("list sets %s\n", + printf("list sets %s %s\n", ipset_xlate_family(family), table); } else { printf("list set %s %s %s\n", @@ -1875,9 +1878,6 @@ static int ipset_xlate(struct ipset *ipset, enum ipset_cmd cmd, cmd == IPSET_CMD_DEL ? "delete" : "get", ipset_xlate_family(family), table, set); - typename = ipset_data_get(data, IPSET_OPT_TYPENAME); - type = ipset_xlate_set_type(typename); - xlate_set = (struct ipset_xlate_set *) ipset_xlate_set_get(ipset, set); if (xlate_set && xlate_set->interval) @@ -1906,6 +1906,8 @@ static int ipset_xlate(struct ipset *ipset, enum ipset_cmd cmd, } 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]); @@ -1968,7 +1970,6 @@ 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); - const char *filename; enum ipset_cmd cmd; FILE *f = stdin; int ret = 0; @@ -1977,7 +1978,7 @@ static int ipset_xlate_restore(struct ipset *ipset) if (ipset->filename) { f = fopen(ipset->filename, "r"); if (!f) { - fprintf(stderr, "cannot open file `%s'\n", filename); + fprintf(stderr, "cannot open file `%s'\n", ipset->filename); return -1; } } @@ -1997,7 +1998,7 @@ static int ipset_xlate_restore(struct ipset *ipset) ret = build_argv(ipset, c); if (ret < 0) - return ret; + break; cmd = ipset_parser(ipset, ipset->newargc, ipset->newargv); if (cmd < 0) @@ -2011,7 +2012,7 @@ static int ipset_xlate_restore(struct ipset *ipset) ipset_data_reset(data); } - if (filename) + if (ipset->filename) fclose(f); 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 c380f9c..c69b738 100644 --- a/lib/libipset.map +++ b/lib/libipset.map @@ -213,3 +213,7 @@ 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 974eaf8..4d2d8b3 100644 --- a/lib/parse.c +++ b/lib/parse.c @@ -280,7 +280,8 @@ static int parse_portname(struct ipset_session *session, const char *str, uint16_t *port, const char *proto) { - char *saved, *tmp, *protoname; + char *saved, *tmp; + const char *protoname; const struct protoent *protoent; struct servent *service; uint8_t protonum = 0; @@ -292,7 +293,7 @@ parse_portname(struct ipset_session *session, const char *str, if (tmp == NULL) goto error; - protoname = (char *)proto; + protoname = proto; if (string_to_u8(session, proto, &protonum, IPSET_WARNING) == 0) { protoent = getprotobynumber(protonum); if (protoent == NULL) @@ -1703,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; @@ -1722,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/ipset.8 b/src/ipset.8 index 269b9b5..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. @@ -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 6d42b60..162f477 100644 --- a/src/ipset.c +++ b/src/ipset.c @@ -6,6 +6,7 @@ * 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 */ @@ -31,7 +32,7 @@ main(int argc, char *argv[]) exit(1); } - if (!strcmp(argv[0], "ipset-translate")) { + if (!strcmp(basename(argv[0]), "ipset-translate")) { ret = ipset_xlate_argv(ipset, argc, argv); } else { ret = ipset_parse_argv(ipset, argc, argv); @@ -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 index a2a02c0..8b42f0b 100755 --- a/tests/xlate/runtest.sh +++ b/tests/xlate/runtest.sh @@ -6,14 +6,20 @@ if [ ! -x "$DIFF" ] ; then exit 1 fi -IPSET_XLATE=$(which ipset-translate) -if [ ! -x "$IPSET_XLATE" ] ; then - echo "ERROR: ipset-translate is not installed yet" +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-translate restore < xlate.t &> $TMP +$ipset_xlate restore < xlate.t &> $TMP if [ $? -ne 0 ] then cat $TMP diff --git a/tests/xlate/xlate.t b/tests/xlate/xlate.t index b1e7d28..38cbc78 100644 --- a/tests/xlate/xlate.t +++ b/tests/xlate/xlate.t @@ -11,8 +11,8 @@ add hip4 192.168.10.0 create hip5 hash:ip maxelem 24 add hip5 192.168.10.0 create hip6 hash:ip comment -add hip5 192.168.10.1 -add hip5 192.168.10.2 comment "this is a 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 @@ -23,7 +23,7 @@ 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.1.0.1 timeout 5 +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 @@ -53,3 +53,5 @@ 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 index 96eba3b..8fb2a29 100644 --- a/tests/xlate/xlate.t.nft +++ b/tests/xlate/xlate.t.nft @@ -12,8 +12,8 @@ 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 hip5 { 192.168.10.1 } -add element inet global hip5 { 192.168.10.2 comment "this is a comment" } +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 } @@ -54,3 +54,5 @@ 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 } |