From 3fd6b24ace319b139ec3c4e3031a5f05d21e304e Mon Sep 17 00:00:00 2001 From: Jozsef Kadlecsik Date: Tue, 15 Jun 2010 13:30:55 +0200 Subject: ipset 5 in an almost ready state - milestone Reworked protocol and internal interfaces, missing set types added, backward compatibility verified, lots of tests added (and thanks to the tests, bugs fixed), even the manpage is rewritten ;-). Countless changes everywhere... The missing bits before announcing ipset 5: - net namespace support - new iptables/ip6tables extension library - iptables/ip6tables match and target tests (backward/forward compatibility) - tests on catching syntax errors --- Make_global.am | 2 +- Makefile.am | 1 + include/libipset/data.h | 11 +- include/libipset/debug.h | 35 + include/libipset/linux_ip_set.h | 51 +- include/libipset/linux_ip_set_list.h | 14 + include/libipset/parse.h | 24 +- include/libipset/print.h | 6 +- include/libipset/session.h | 22 +- include/libipset/types.h | 36 +- include/libipset/ui.h | 24 +- include/libipset/utils.h | 6 - kernel/Kbuild | 11 +- kernel/include/linux/netfilter/ip_set.h | 246 ++--- kernel/include/linux/netfilter/ip_set_bitmap.h | 6 +- kernel/include/linux/netfilter/ip_set_chash.h | 1096 ++++++++++++++++++++++ kernel/include/linux/netfilter/ip_set_getport.h | 25 +- kernel/include/linux/netfilter/ip_set_hash.h | 5 +- kernel/include/linux/netfilter/ip_set_jhash.h | 152 ++-- kernel/include/linux/netfilter/ip_set_kernel.h | 20 + kernel/include/linux/netfilter/ip_set_list.h | 21 + kernel/include/linux/netfilter/ip_set_slist.h | 86 ++ kernel/include/linux/netfilter/ip_set_timeout.h | 35 +- kernel/include/linux/netfilter/ipt_set.h | 21 - kernel/include/linux/netfilter/xt_set.h | 55 ++ kernel/ip_set.c | 824 +++++++++++------ kernel/ip_set_bitmap_ip.c | 173 ++-- kernel/ip_set_bitmap_ipmac.c | 486 ++++++---- kernel/ip_set_bitmap_port.c | 141 +-- kernel/ip_set_hash_ip.c | 539 ++++++----- kernel/ip_set_hash_ip_src.c | 473 ---------- kernel/ip_set_hash_ipport.c | 538 ++++++++--- kernel/ip_set_hash_ipportip.c | 580 ++++++++---- kernel/ip_set_hash_ipportnet.c | 696 +++++++++----- kernel/ip_set_hash_net.c | 556 +++++++++--- kernel/ip_set_iptreemap.c | 700 -------------- kernel/ip_set_list_set.c | 731 ++++++++++----- kernel/ip_set_tree_ip.c | 464 ---------- kernel/ipt_SET.c | 242 ----- kernel/ipt_set.c | 238 ----- kernel/xt_set.c | 356 ++++++++ lib/Makefile.am | 4 +- lib/PROTOCOL | 6 +- lib/data.c | 67 +- lib/mnl.c | 2 + lib/parse.c | 449 +++++++-- lib/print.c | 27 +- lib/session.c | 98 +- lib/types.c | 58 +- lib/utils.c | 103 --- netlink.patch | 16 +- src/Makefile.am | 1 - src/errcode.c | 26 +- src/ipset.8 | 1109 ++++++++++++----------- src/ipset.c | 194 ++-- src/ipset_bitmap_ip.c | 16 +- src/ipset_bitmap_ipmac.c | 14 +- src/ipset_bitmap_port.c | 12 +- src/ipset_hash_ip.c | 52 +- src/ipset_hash_ipport.c | 66 +- src/ipset_hash_ipportip.c | 66 +- src/ipset_hash_ipportnet.c | 83 +- src/ipset_hash_net.c | 59 +- src/ipset_iptreemap.c | 208 ----- src/ipset_list_set.c | 31 +- src/ipset_tree_ip.c | 75 -- src/ui.c | 231 +++-- tests/Makefile | 450 +++++++++ tests/Makefile.am | 5 + tests/Makefile.in | 450 +++++++++ tests/bitmap:ip.t | 16 +- tests/bitmap:ip.t.list0 | 3 +- tests/bitmap:ip.t.list1 | 3 +- tests/bitmap:ip.t.list2 | 3 +- tests/bitmap:ip.t.list3 | 3 +- tests/bitmap:ip.t.list4 | 13 + tests/bitmap:ip.t.list5 | 9 + tests/bitmap:ip.t.list6 | 265 ++++++ tests/hash:ip,port,ip.t | 51 ++ tests/hash:ip,port,ip.t.list0 | 11 + tests/hash:ip,port,ip.t.list1 | 7 + tests/hash:ip,port.t | 43 + tests/hash:ip,port.t.list0 | 11 + tests/hash:ip,port.t.list1 | 7 + tests/hash:ip.t | 14 +- tests/hash:ip.t.list0 | 5 +- tests/hash:ip.t.list1 | 5 +- tests/hash:ip.t.list2 | 9 + tests/hash:ip.t.list3 | 10 + tests/hash:ip6,port,ip6.t | 51 ++ tests/hash:ip6,port,ip6.t.list0 | 11 + tests/hash:ip6,port,ip6.t.list1 | 7 + tests/hash:ip6,port,net6.t | 45 + tests/hash:ip6,port,net6.t.list0 | 11 + tests/hash:ip6,port.t | 43 + tests/hash:ip6,port.t.list0 | 11 + tests/hash:ip6,port.t.list1 | 7 + tests/hash:ip6.t | 77 ++ tests/hash:ip6.t.list0 | 8 + tests/hash:ip6.t.list1 | 8 + tests/hash:ip6.t.list2 | 9 + tests/hash:ip6.t.list3 | 10 + tests/hash:net.t | 49 + tests/hash:net.t.list0 | 11 + tests/hash:net.t.list1 | 7 + tests/hash:net6.t | 49 + tests/hash:net6.t.list0 | 11 + tests/hash:net6.t.list1 | 7 + tests/iphash.t | 10 +- tests/iphash.t.list0 | 5 +- tests/iphash.t.list1 | 5 +- tests/ipmap.t | 2 +- tests/ipmap.t.list0 | 3 +- tests/ipmap.t.list1 | 3 +- tests/ipmap.t.list2 | 3 +- tests/ipmap.t.list3 | 3 +- tests/ipmap.t.list4 | 3 +- tests/ipporthash.t | 44 +- tests/ipporthash.t.list0 | 8 +- tests/ipporthash.t.list1 | 8 +- tests/ipportiphash.t | 40 +- tests/ipportiphash.t.list0 | 8 +- tests/ipportiphash.t.list1 | 6 +- tests/ipportnethash.t | 26 +- tests/ipportnethash.t.list0 | 8 +- tests/ipportnethash.t.list1 | 8 +- tests/iptree.t | 2 +- tests/iptree.t.list0 | 7 +- tests/iptreemap.t | 4 +- tests/iptreemap.t.list0 | 35 +- tests/macipmap.t | 12 +- tests/macipmap.t.list0 | 3 +- tests/macipmap.t.list1 | 3 +- tests/macipmap.t.list2 | 3 +- tests/macipmap.t.list3 | 10 + tests/nethash.t | 30 +- tests/nethash.t.list0 | 8 +- tests/portmap.t | 6 + tests/portmap.t.list0 | 3 +- tests/portmap.t.list1 | 3 +- tests/portmap.t.list3 | 9 + tests/runtest.sh | 19 +- tests/setlist.t | 6 + tests/setlist.t.list0 | 5 +- tests/sort.sh | 4 +- 145 files changed, 9165 insertions(+), 5846 deletions(-) create mode 100644 include/libipset/debug.h create mode 100644 include/libipset/linux_ip_set_list.h create mode 100644 kernel/include/linux/netfilter/ip_set_chash.h create mode 100644 kernel/include/linux/netfilter/ip_set_kernel.h create mode 100644 kernel/include/linux/netfilter/ip_set_list.h create mode 100644 kernel/include/linux/netfilter/ip_set_slist.h delete mode 100644 kernel/include/linux/netfilter/ipt_set.h create mode 100644 kernel/include/linux/netfilter/xt_set.h delete mode 100644 kernel/ip_set_hash_ip_src.c delete mode 100644 kernel/ip_set_iptreemap.c delete mode 100644 kernel/ip_set_tree_ip.c delete mode 100644 kernel/ipt_SET.c delete mode 100644 kernel/ipt_set.c create mode 100644 kernel/xt_set.c delete mode 100644 lib/utils.c delete mode 100644 src/ipset_iptreemap.c delete mode 100644 src/ipset_tree_ip.c create mode 100644 tests/Makefile create mode 100644 tests/Makefile.am create mode 100644 tests/Makefile.in create mode 100644 tests/bitmap:ip.t.list4 create mode 100644 tests/bitmap:ip.t.list5 create mode 100644 tests/bitmap:ip.t.list6 create mode 100644 tests/hash:ip,port,ip.t create mode 100644 tests/hash:ip,port,ip.t.list0 create mode 100644 tests/hash:ip,port,ip.t.list1 create mode 100644 tests/hash:ip,port.t create mode 100644 tests/hash:ip,port.t.list0 create mode 100644 tests/hash:ip,port.t.list1 create mode 100644 tests/hash:ip.t.list2 create mode 100644 tests/hash:ip.t.list3 create mode 100644 tests/hash:ip6,port,ip6.t create mode 100644 tests/hash:ip6,port,ip6.t.list0 create mode 100644 tests/hash:ip6,port,ip6.t.list1 create mode 100644 tests/hash:ip6,port,net6.t create mode 100644 tests/hash:ip6,port,net6.t.list0 create mode 100644 tests/hash:ip6,port.t create mode 100644 tests/hash:ip6,port.t.list0 create mode 100644 tests/hash:ip6,port.t.list1 create mode 100644 tests/hash:ip6.t create mode 100644 tests/hash:ip6.t.list0 create mode 100644 tests/hash:ip6.t.list1 create mode 100644 tests/hash:ip6.t.list2 create mode 100644 tests/hash:ip6.t.list3 create mode 100644 tests/hash:net.t create mode 100644 tests/hash:net.t.list0 create mode 100644 tests/hash:net.t.list1 create mode 100644 tests/hash:net6.t create mode 100644 tests/hash:net6.t.list0 create mode 100644 tests/hash:net6.t.list1 create mode 100644 tests/macipmap.t.list3 create mode 100644 tests/portmap.t.list3 diff --git a/Make_global.am b/Make_global.am index 5c65103..ac9a2ca 100644 --- a/Make_global.am +++ b/Make_global.am @@ -39,7 +39,7 @@ AM_CFLAGS = -std=gnu99 \ -Wwrite-strings \ -Wno-missing-field-initializers \ -Werror \ - -g -ggdb + -g -ggdb -gdwarf-2 -g3 endif if ! ENABLE_VERBOSE diff --git a/Makefile.am b/Makefile.am index 2eb7a00..e5c1c83 100644 --- a/Makefile.am +++ b/Makefile.am @@ -33,6 +33,7 @@ update_includes: ./update ip_set.h ./update ip_set_bitmap.h ./update ip_set_hash.h + ./update ip_set_list.h tests: cd tests; ./runtest.sh diff --git a/include/libipset/data.h b/include/libipset/data.h index 0ebc1eb..936b807 100644 --- a/include/libipset/data.h +++ b/include/libipset/data.h @@ -50,7 +50,8 @@ enum ipset_opt { IPSET_OPT_EXIST, IPSET_OPT_BEFORE, /* Internal options */ - IPSET_OPT_FLAGS = 48, + IPSET_OPT_FLAGS = 48, /* IPSET_FLAG_EXIST| */ + IPSET_OPT_CADT_FLAGS, /* IPSET_FLAG_BEFORE| */ IPSET_OPT_ELEM, IPSET_OPT_TYPE, IPSET_OPT_LINENO, @@ -63,7 +64,10 @@ enum ipset_opt { #define IPSET_FLAGS_ALL (~0LL) #define IPSET_CREATE_FLAGS \ - ( IPSET_FLAG(IPSET_OPT_IP) \ + ( IPSET_FLAG(IPSET_OPT_FAMILY) \ + | IPSET_FLAG(IPSET_OPT_TYPENAME)\ + | IPSET_FLAG(IPSET_OPT_TYPE) \ + | IPSET_FLAG(IPSET_OPT_IP) \ | IPSET_FLAG(IPSET_OPT_IP_TO) \ | IPSET_FLAG(IPSET_OPT_CIDR) \ | IPSET_FLAG(IPSET_OPT_PORT) \ @@ -89,14 +93,17 @@ enum ipset_opt { | IPSET_FLAG(IPSET_OPT_NAMEREF) \ | IPSET_FLAG(IPSET_OPT_IP2) \ | IPSET_FLAG(IPSET_OPT_CIDR2) \ + | IPSET_FLAG(IPSET_OPT_CADT_FLAGS)\ | IPSET_FLAG(IPSET_OPT_BEFORE)) struct ipset_data; +extern void ipset_strncpy(char *dst, const char *src, size_t len); extern bool ipset_data_flags_test(const struct ipset_data *data, uint64_t flags); extern void ipset_data_flags_set(struct ipset_data *data, uint64_t flags); extern void ipset_data_flags_unset(struct ipset_data *data, uint64_t flags); +extern bool ipset_data_ignored(struct ipset_data *data, enum ipset_opt opt); extern int ipset_data_set(struct ipset_data *data, enum ipset_opt opt, const void *value); diff --git a/include/libipset/debug.h b/include/libipset/debug.h new file mode 100644 index 0000000..1e32ce2 --- /dev/null +++ b/include/libipset/debug.h @@ -0,0 +1,35 @@ +/* Copyright 2007-2010 Jozsef Kadlecsik (kadlec@blackhole.kfki.hu) + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ +#ifndef LIBIPSET_DEBUG_H +#define LIBIPSET_DEBUG_H + +#define IPSET_DEBUG + +#ifdef IPSET_DEBUG +#include +#include +#include +#define D(fmt, args...) \ + fprintf(stderr, "%s: %s: " fmt "\n", __FILE__, __FUNCTION__ , ## args) +#define IF_D(test, fmt, args...) \ + if (test) \ + D(fmt , ## args) + +static inline void +dump_nla(struct nlattr *nla[], int maxlen) +{ + int i; + for (i = 0; i < maxlen; i++) + D("nla[%u] does%s exist", i, nla[i] ? "" : " NOT"); +} +#else +#define D(fmt, args...) +#define IF_D(test, fmt, args...) +#define dump_nla(nla, maxlen) +#endif + +#endif /* LIBIPSET_DEBUG_H */ diff --git a/include/libipset/linux_ip_set.h b/include/libipset/linux_ip_set.h index 254fb21..4af75ba 100644 --- a/include/libipset/linux_ip_set.h +++ b/include/libipset/linux_ip_set.h @@ -11,14 +11,10 @@ * published by the Free Software Foundation. */ -#if 1 -#define IP_SET_DEBUG -#endif - /* The protocol version */ #define IPSET_PROTOCOL 5 -/* The max length of strings: set and type identifiers */ +/* The max length of strings including NUL: set and type identifiers */ #define IPSET_MAXNAMELEN 32 /* Message types and commands */ @@ -43,6 +39,7 @@ enum ipset_cmd { IPSET_CMD_RESTORE = IPSET_MSG_MAX, /* Enter restore mode */ IPSET_CMD_HELP, /* Get help */ IPSET_CMD_VERSION, /* Get program version */ + IPSET_CMD_QUIT, /* Quit from interactive mode */ IPSET_CMD_MAX, @@ -58,6 +55,7 @@ enum { IPSET_ATTR_SETNAME2 = IPSET_ATTR_TYPENAME, /* rename/swap */ IPSET_ATTR_REVISION, /* Settype revision */ IPSET_ATTR_FAMILY, /* Settype family */ + IPSET_ATTR_FLAGS, /* Flags at command level */ IPSET_ATTR_DATA, /* Nested attributes */ IPSET_ATTR_ADT, /* Multiple data containers */ IPSET_ATTR_LINENO, /* Restore lineno */ @@ -77,8 +75,8 @@ enum { IPSET_ATTR_PORT_FROM = IPSET_ATTR_PORT, IPSET_ATTR_PORT_TO, IPSET_ATTR_TIMEOUT, - IPSET_ATTR_FLAGS, - /* IPSET_ATTR_LINENO */ + IPSET_ATTR_CADT_FLAGS, + IPSET_ATTR_CADT_LINENO = IPSET_ATTR_LINENO, /* Reserve empty slots */ IPSET_ATTR_CADT_MAX = 16, /* Create-only specific attributes */ @@ -123,15 +121,19 @@ enum ipset_errno { IPSET_ERR_INVALID_NETMASK, IPSET_ERR_INVALID_FAMILY, IPSET_ERR_TIMEOUT, + IPSET_ERR_REFERENCED, + /* Type specific error codes */ IPSET_ERR_TYPE_SPECIFIC = 160, }; - -enum ipset_data_flags { + +enum ipset_cmd_flags { IPSET_FLAG_BIT_EXIST = 0, IPSET_FLAG_EXIST = (1 << IPSET_FLAG_BIT_EXIST), - - IPSET_FLAG_BIT_BEFORE = 2, +}; + +enum ipset_cadt_flags { + IPSET_FLAG_BIT_BEFORE = 0, IPSET_FLAG_BEFORE = (1 << IPSET_FLAG_BIT_BEFORE), }; @@ -140,32 +142,9 @@ enum ipset_adt { IPSET_ADD, IPSET_DEL, IPSET_TEST, - IPSET_CREATE, + IPSET_ADT_MAX, + IPSET_CREATE = IPSET_ADT_MAX, IPSET_CADT_MAX, }; -#ifndef __KERNEL__ -#ifdef IP_SET_DEBUG -#include -#include -#include -#define D(format, args...) do { \ - fprintf(stderr, "%s: %s: ", __FILE__, __FUNCTION__); \ - fprintf(stderr, format "\n" , ## args); \ -} while (0) -static inline void -dump_nla(struct nlattr *nla[], int maxlen) -{ - int i; - - for (i = 0; i < maxlen; i++) - D("nla[%u] does%s exist", i, !nla[i] ? " NOT" : ""); -} - -#else -#define D(format, args...) -#define dump_nla(nla, maxlen) -#endif -#endif /* !__KERNEL__ */ - #endif /* __IP_SET_H */ diff --git a/include/libipset/linux_ip_set_list.h b/include/libipset/linux_ip_set_list.h new file mode 100644 index 0000000..cf282c5 --- /dev/null +++ b/include/libipset/linux_ip_set_list.h @@ -0,0 +1,14 @@ +#ifndef __IP_SET_LIST_H +#define __IP_SET_LIST_H + +/* List type specific error codes */ +enum { + IPSET_ERR_NAME = IPSET_ERR_TYPE_SPECIFIC, + IPSET_ERR_LOOP, + IPSET_ERR_BEFORE, + IPSET_ERR_NAMEREF, + IPSET_ERR_LIST_FULL, + IPSET_ERR_REF_EXIST, +}; + +#endif /* __IP_SET_LIST_H */ diff --git a/include/libipset/parse.h b/include/libipset/parse.h index 09c1db4..143e2b3 100644 --- a/include/libipset/parse.h +++ b/include/libipset/parse.h @@ -17,6 +17,9 @@ struct ipset_session; +typedef int (*ipset_parsefn)(struct ipset_session *s, + enum ipset_opt opt, const char *str); + extern int ipset_parse_ether(struct ipset_session *session, enum ipset_opt opt, const char *str); extern int ipset_parse_single_port(struct ipset_session *session, @@ -24,7 +27,7 @@ extern int ipset_parse_single_port(struct ipset_session *session, extern int ipset_parse_port(struct ipset_session *session, enum ipset_opt opt, const char *str); extern int ipset_parse_family(struct ipset_session *session, - int opt, const char *str); + enum ipset_opt opt, const char *str); extern int ipset_parse_ip(struct ipset_session *session, enum ipset_opt opt, const char *str); extern int ipset_parse_single_ip(struct ipset_session *session, @@ -35,8 +38,16 @@ extern int ipset_parse_range(struct ipset_session *session, enum ipset_opt opt, const char *str); extern int ipset_parse_netrange(struct ipset_session *session, enum ipset_opt opt, const char *str); +extern int ipset_parse_iprange(struct ipset_session *session, + enum ipset_opt opt, const char *str); +extern int ipset_parse_ipnet(struct ipset_session *session, + enum ipset_opt opt, const char *str); extern int ipset_parse_name(struct ipset_session *session, enum ipset_opt opt, const char *str); +extern int ipset_parse_before(struct ipset_session *session, + enum ipset_opt opt, const char *str); +extern int ipset_parse_after(struct ipset_session *session, + enum ipset_opt opt, const char *str); extern int ipset_parse_setname(struct ipset_session *session, enum ipset_opt opt, const char *str); extern int ipset_parse_uint32(struct ipset_session *session, @@ -51,7 +62,18 @@ extern int ipset_parse_typename(struct ipset_session *session, enum ipset_opt opt, const char *str); extern int ipset_parse_output(struct ipset_session *session, int opt, const char *str); +extern int ipset_parse_ignored(struct ipset_session *session, + enum ipset_opt opt, const char *str); extern int ipset_parse_elem(struct ipset_session *session, enum ipset_opt opt, const char *str); +extern int ipset_call_parser(struct ipset_session *session, + ipset_parsefn parse, const char *optstr, + enum ipset_opt optional, const char *str); + +/* Compatibility parser functions */ +extern int ipset_parse_iptimeout(struct ipset_session *session, + enum ipset_opt opt, const char *str); +extern int ipset_parse_name_compat(struct ipset_session *session, + enum ipset_opt opt, const char *str); #endif /* LIBIPSET_PARSE_H */ diff --git a/include/libipset/print.h b/include/libipset/print.h index 343386b..dbb70f3 100644 --- a/include/libipset/print.h +++ b/include/libipset/print.h @@ -9,11 +9,15 @@ #include /* enum ipset_opt */ +typedef int (*ipset_printfn)(char *buf, unsigned int len, + const struct ipset_data *data, enum ipset_opt opt, + uint8_t env); + extern int ipset_print_ether(char *buf, unsigned int len, const struct ipset_data *data, enum ipset_opt opt, uint8_t env); extern int ipset_print_family(char *buf, unsigned int len, - const struct ipset_data *data, int opt, + const struct ipset_data *data, enum ipset_opt opt, uint8_t env); extern int ipset_print_type(char *buf, unsigned int len, const struct ipset_data *data, enum ipset_opt opt, diff --git a/include/libipset/session.h b/include/libipset/session.h index 71b8e02..cc0940e 100644 --- a/include/libipset/session.h +++ b/include/libipset/session.h @@ -12,7 +12,6 @@ #include /* printf */ #include /* enum ipset_cmd */ -#include /* enum ipset_envopt */ /* Report and output buffer sizes */ #define IPSET_ERRORBUFLEN 1024 @@ -54,6 +53,23 @@ extern const char * ipset_session_warning(const struct ipset_session *session); #define ipset_session_data_get(session, opt) \ ipset_data_get(ipset_session_data(session), opt) +/* Environment option flags */ +enum ipset_envopt { + IPSET_ENV_BIT_SORTED = 0, + IPSET_ENV_SORTED = (1 << IPSET_ENV_BIT_SORTED), + IPSET_ENV_BIT_QUIET = 1, + IPSET_ENV_QUIET = (1 << IPSET_ENV_BIT_QUIET), + IPSET_ENV_BIT_RESOLVE = 2, + IPSET_ENV_RESOLVE = (1 << IPSET_ENV_BIT_RESOLVE), + IPSET_ENV_BIT_EXIST = 3, + IPSET_ENV_EXIST = (1 << IPSET_ENV_BIT_EXIST), +}; + +extern int ipset_envopt_parse(struct ipset_session *session, + int env, const char *str); +extern bool ipset_envopt_test(struct ipset_session *session, + enum ipset_envopt env); + enum ipset_output_mode { IPSET_LIST_NONE, IPSET_LIST_PLAIN, @@ -61,10 +77,6 @@ enum ipset_output_mode { IPSET_LIST_XML, }; -extern int ipset_envopt_parse(struct ipset_session *session, - int env, const char *str); -extern bool ipset_envopt_test(struct ipset_session *session, - enum ipset_envopt env); extern int ipset_session_output(struct ipset_session *session, enum ipset_output_mode mode); diff --git a/include/libipset/types.h b/include/libipset/types.h index 461931a..45d5e3d 100644 --- a/include/libipset/types.h +++ b/include/libipset/types.h @@ -10,6 +10,8 @@ #include /* uintxx_t */ #include /* enum ipset_opt */ +#include /* ipset_parsefn */ +#include /* ipset_printfn */ #include /* IPSET_MAXNAMELEN */ #define AF_INET46 255 @@ -39,15 +41,9 @@ enum { struct ipset_session; -typedef int (*ipset_parsefn)(struct ipset_session *s, - enum ipset_opt opt, const char *str); -typedef int (*ipset_printfn)(char *buf, unsigned int len, - const struct ipset_data *data, enum ipset_opt opt, - uint8_t env); - /* Parse and print type-specific arguments */ struct ipset_arg { - const char *name[3]; /* option names */ + const char *name[2]; /* option names */ int has_arg; /* mandatory/optional/no arg */ enum ipset_opt opt; /* argumentum type */ ipset_parsefn parse; /* parser function */ @@ -81,13 +77,13 @@ struct ipset_elem { */ struct ipset_type { char name[IPSET_MAXNAMELEN]; /* type name */ - char alias[IPSET_MAXNAMELEN]; /* name alias */ uint8_t revision; /* revision number */ uint8_t family; /* supported family */ uint8_t dimension; /* elem dimension */ int8_t kernel_check; /* kernel check */ bool last_elem_optional; /* last element optional */ struct ipset_elem elem[IPSET_DIM_MAX]; /* parse elem */ + ipset_parsefn compat_parse_elem; /* compatibility parser */ const struct ipset_arg *args[IPSET_CADT_MAX]; /* create/ADT args except elem */ uint64_t mandatory[IPSET_CADT_MAX]; /* create/ADT mandatory flags */ uint64_t full[IPSET_CADT_MAX]; /* full args flags */ @@ -95,13 +91,18 @@ struct ipset_type { const char *usage; /* terse usage */ struct ipset_type *next; + const char *alias[]; /* name alias(es) */ }; -extern int ipset_cache_add(const char *name, const struct ipset_type *type); +extern int ipset_cache_add(const char *name, const struct ipset_type *type, + uint8_t family); extern int ipset_cache_del(const char *name); extern int ipset_cache_rename(const char *from, const char *to); extern int ipset_cache_swap(const char *from, const char *to); +extern int ipset_cache_init(void); +extern void ipset_cache_fini(void); + extern const struct ipset_type * ipset_type_get(struct ipset_session *session, enum ipset_cmd cmd); extern const struct ipset_type * ipset_type_check(struct ipset_session *session); @@ -109,20 +110,7 @@ extern const struct ipset_type * ipset_type_check(struct ipset_session *session) extern int ipset_type_add(struct ipset_type *type); extern const struct ipset_type * ipset_types(void); extern const char * ipset_typename_resolve(const char *str); - -extern int ipset_types_init(void); -extern void ipset_types_fini(void); - -/* The known set types: (typename, revision, family) is unique */ -extern struct ipset_type ipset_bitmap_ip0; -extern struct ipset_type ipset_bitmap_ipmac0; -extern struct ipset_type ipset_bitmap_port0; -extern struct ipset_type ipset_hash_ip0; -extern struct ipset_type ipset_hash_net0; -extern struct ipset_type ipset_hash_ipport0; -extern struct ipset_type ipset_hash_ipportip0; -extern struct ipset_type ipset_hash_ipportnet0; -extern struct ipset_type ipset_tree_ip0; -extern struct ipset_type ipset_list_set0; +extern bool ipset_match_typename(const char *str, + const struct ipset_type *t); #endif /* LIBIPSET_TYPES_H */ diff --git a/include/libipset/ui.h b/include/libipset/ui.h index 044e586..f8eeae0 100644 --- a/include/libipset/ui.h +++ b/include/libipset/ui.h @@ -9,25 +9,14 @@ /* Commands in userspace */ struct ipset_commands { - const char *name[6]; - const char *help; + enum ipset_cmd cmd; int has_arg; + const char *name[2]; + const char *help; }; extern const struct ipset_commands ipset_commands[]; -/* Environment option flags */ -enum ipset_envopt { - IPSET_ENV_BIT_SORTED = 0, - IPSET_ENV_SORTED = (1 << IPSET_ENV_BIT_SORTED), - IPSET_ENV_BIT_QUIET = 1, - IPSET_ENV_QUIET = (1 << IPSET_ENV_BIT_QUIET), - IPSET_ENV_BIT_RESOLVE = 2, - IPSET_ENV_RESOLVE = (1 << IPSET_ENV_BIT_RESOLVE), - IPSET_ENV_BIT_EXIST = 3, - IPSET_ENV_EXIST = (1 << IPSET_ENV_BIT_EXIST), -}; - struct ipset_session; struct ipset_data; @@ -35,7 +24,7 @@ struct ipset_data; struct ipset_envopts { int flag; int has_arg; - const char *name[3]; + const char *name[2]; const char *help; int (*parse)(struct ipset_session *s, int flag, const char *str); int (*print)(char *buf, unsigned int len, @@ -44,4 +33,9 @@ struct ipset_envopts { extern const struct ipset_envopts ipset_envopts[]; +extern bool ipset_match_cmd(const char *arg, const char * const name[]); +extern bool ipset_match_option(const char *arg, const char * const name[]); +extern bool ipset_match_envopt(const char *arg, const char * const name[]); +extern void ipset_shift_argv(int *argc, char *argv[], int from); + #endif /* LIBIPSET_UI_H */ diff --git a/include/libipset/utils.h b/include/libipset/utils.h index 2d12e91..672bfa9 100644 --- a/include/libipset/utils.h +++ b/include/libipset/utils.h @@ -7,7 +7,6 @@ #ifndef LIBIPSET_UTILS_H #define LIBIPSET_UTILS_H -#include /* bool */ #include /* strcmp */ #include /* struct in[6]_addr */ @@ -37,9 +36,4 @@ in6cpy(struct in6_addr *dest, const struct in6_addr *src) memcpy(dest, src, sizeof(struct in6_addr)); } -extern char * ipset_strchr(const char *str, const char *sep); -extern bool ipset_name_match(const char *arg, const char * const name[]); -extern void ipset_shift_argv(int *argc, char *argv[], int from); -extern void ipset_strncpy(char *dst, const char *src, size_t len); - #endif /* LIBIPSET_UTILS_H */ diff --git a/kernel/Kbuild b/kernel/Kbuild index c171711..9c7771a 100644 --- a/kernel/Kbuild +++ b/kernel/Kbuild @@ -1,14 +1,11 @@ EXTRA_CFLAGS := -I$(M)/include \ -DCONFIG_IP_SET_MAX=$(IP_SET_MAX) -obj-m += ip_set.o -#ipt_set.o ipt_SET.o +obj-m += ip_set.o xt_set.o obj-m += ip_set_bitmap_ip.o ip_set_bitmap_ipmac.o ip_set_bitmap_port.o -obj-m += ip_set_hash_ip.o -#obj-m += ip_set_iphash.o ip_set_nethash.o ip_set_ipporthash.o -#obj-m += ip_set_ipportiphash.o ip_set_ipportnethash.o -#obj-m += ip_set_iptree.o ip_set_iptreemap.o -#obj-m += ip_set_setlist.o +obj-m += ip_set_hash_ip.o ip_set_hash_ipport.o ip_set_hash_ipportip.o +obj-m += ip_set_hash_net.o ip_set_hash_ipportnet.o +obj-m += ip_set_list_set.o # It's for me... incdirs := $(M) $(M)/include/linux/netfilter diff --git a/kernel/include/linux/netfilter/ip_set.h b/kernel/include/linux/netfilter/ip_set.h index d0b47a0..e700503 100644 --- a/kernel/include/linux/netfilter/ip_set.h +++ b/kernel/include/linux/netfilter/ip_set.h @@ -11,14 +11,10 @@ * published by the Free Software Foundation. */ -#if 1 -#define IP_SET_DEBUG -#endif - /* The protocol version */ #define IPSET_PROTOCOL 5 -/* The max length of strings: set and type identifiers */ +/* The max length of strings including NUL: set and type identifiers */ #define IPSET_MAXNAMELEN 32 /* Message types and commands */ @@ -43,6 +39,7 @@ enum ipset_cmd { IPSET_CMD_RESTORE = IPSET_MSG_MAX, /* Enter restore mode */ IPSET_CMD_HELP, /* Get help */ IPSET_CMD_VERSION, /* Get program version */ + IPSET_CMD_QUIT, /* Quit from interactive mode */ IPSET_CMD_MAX, @@ -58,6 +55,7 @@ enum { IPSET_ATTR_SETNAME2 = IPSET_ATTR_TYPENAME, /* rename/swap */ IPSET_ATTR_REVISION, /* Settype revision */ IPSET_ATTR_FAMILY, /* Settype family */ + IPSET_ATTR_FLAGS, /* Flags at command level */ IPSET_ATTR_DATA, /* Nested attributes */ IPSET_ATTR_ADT, /* Multiple data containers */ IPSET_ATTR_LINENO, /* Restore lineno */ @@ -77,8 +75,8 @@ enum { IPSET_ATTR_PORT_FROM = IPSET_ATTR_PORT, IPSET_ATTR_PORT_TO, IPSET_ATTR_TIMEOUT, - IPSET_ATTR_FLAGS, - /* IPSET_ATTR_LINENO */ + IPSET_ATTR_CADT_FLAGS, + IPSET_ATTR_CADT_LINENO = IPSET_ATTR_LINENO, /* Reserve empty slots */ IPSET_ATTR_CADT_MAX = 16, /* Create-only specific attributes */ @@ -123,15 +121,19 @@ enum ipset_errno { IPSET_ERR_INVALID_NETMASK, IPSET_ERR_INVALID_FAMILY, IPSET_ERR_TIMEOUT, + IPSET_ERR_REFERENCED, + /* Type specific error codes */ IPSET_ERR_TYPE_SPECIFIC = 160, }; - -enum ipset_data_flags { + +enum ipset_cmd_flags { IPSET_FLAG_BIT_EXIST = 0, IPSET_FLAG_EXIST = (1 << IPSET_FLAG_BIT_EXIST), - - IPSET_FLAG_BIT_BEFORE = 2, +}; + +enum ipset_cadt_flags { + IPSET_FLAG_BIT_BEFORE = 0, IPSET_FLAG_BEFORE = (1 << IPSET_FLAG_BIT_BEFORE), }; @@ -140,35 +142,13 @@ enum ipset_adt { IPSET_ADD, IPSET_DEL, IPSET_TEST, - IPSET_CREATE, + IPSET_ADT_MAX, + IPSET_CREATE = IPSET_ADT_MAX, IPSET_CADT_MAX, }; -#ifndef __KERNEL__ -#ifdef IP_SET_DEBUG -#include -#include -#include -#define D(format, args...) do { \ - fprintf(stderr, "%s: %s: ", __FILE__, __FUNCTION__); \ - fprintf(stderr, format "\n" , ## args); \ -} while (0) -static inline void -dump_nla(struct nlattr *nla[], int maxlen) -{ - int i; - - for (i = 0; i < maxlen; i++) - D("nla[%u] does%s exist", i, !nla[i] ? " NOT" : ""); -} - -#else -#define D(format, args...) -#define dump_nla(nla, maxlen) -#endif -#endif /* !__KERNEL__ */ - #ifdef __KERNEL__ +#include #include #include #include @@ -176,19 +156,27 @@ dump_nla(struct nlattr *nla[], int maxlen) /* Sets are identified by an index in kernel space. Tweak with ip_set_id_t * and IPSET_INVALID_ID if you want to increase the max number of sets. */ -typedef uint16_t ip_set_id_t; +typedef u16 ip_set_id_t; #define IPSET_INVALID_ID 65535 +enum ip_set_dim { + IPSET_DIM_ZERO = 0, + IPSET_DIM_ONE, + IPSET_DIM_TWO, + IPSET_DIM_THREE, + /* Max dimension in elements. + * If changed, new revision of iptables match/target is required. + */ + IPSET_DIM_MAX = 6, +}; + /* Option flags for kernel operations */ enum ip_set_kopt { - /* Bit 0 is reserved */ - IPSET_SRC_FLAG = 1, - IPSET_SRC = (1 << IPSET_SRC_FLAG), - IPSET_DST_FLAG = 2, - IPSET_DST = (1 << IPSET_DST_FLAG), - IPSET_INV_FLAG = 3, - IPSET_INV = (1 << IPSET_INV_FLAG), + IPSET_INV_MATCH = (1 << IPSET_DIM_ZERO), + IPSET_DIM_ONE_SRC = (1 << IPSET_DIM_ONE), + IPSET_DIM_TWO_SRC = (1 << IPSET_DIM_TWO), + IPSET_DIM_THREE_SRC = (1 << IPSET_DIM_THREE), }; /* Set features */ @@ -203,72 +191,60 @@ enum ip_set_feature { IPSET_TYPE_IP2 = (1 << IPSET_TYPE_IP2_FLAG), IPSET_TYPE_NAME_FLAG = 4, IPSET_TYPE_NAME = (1 << IPSET_TYPE_NAME_FLAG), + /* Actually just a flag for dumping */ + IPSET_DUMP_LAST_FLAG = 7, + IPSET_DUMP_LAST = (1 << IPSET_DUMP_LAST_FLAG), }; +/* Calculate the bytes required to store the inclusive range of a-b */ static inline int -bitmap_bytes(uint32_t a, uint32_t b) +bitmap_bytes(u32 a, u32 b) { return 4 * ((((b - a + 8) / 8) + 3) / 4); } -#define ip_set_printk(format, args...) \ - do { \ - printk("%s: %s: ", __FILE__, __FUNCTION__); \ - printk(format "\n" , ## args); \ - } while (0) - -#if defined(IP_SET_DEBUG) -#define D(format, args...) \ - do { \ - printk("%s: %s (DBG): ", __FILE__, __FUNCTION__);\ - printk(format "\n" , ## args); \ - } while (0) - -static inline void -dump_nla(const struct nlattr * const nla[], int maxlen) -{ - int i; - - for (i = 0; i < maxlen; i++) - printk("nlattr[%u] does%s exist\n", i, nla[i] ? "" : " NOT"); -} -#else -#define D(format, args...) -#define dump_nla(nla, maxlen) -#endif - struct ip_set; +typedef int (*ipset_adtfn)(struct ip_set *set, void *value, + gfp_t gfp_flags, u32 timeout); + /* Set type, variant-specific part */ struct ip_set_type_variant { /* Kernelspace: test/add/del entries */ int (*kadt)(struct ip_set *set, const struct sk_buff * skb, - enum ipset_adt adt, uint8_t pf, const uint8_t *flags); + enum ipset_adt adt, u8 pf, u8 dim, u8 flags); /* Userspace: test/add/del entries */ int (*uadt)(struct ip_set *set, struct nlattr *head, int len, - enum ipset_adt adt, uint32_t *lineno, uint32_t flags); + enum ipset_adt adt, u32 *lineno, u32 flags); + + /* Low level add/del/test entries */ + ipset_adtfn adt[IPSET_ADT_MAX]; /* When adding entries and set is full, try to resize the set */ - int (*resize)(struct ip_set *set, uint8_t retried); + int (*resize)(struct ip_set *set, gfp_t gfp_flags, bool retried); /* Destroy the set */ void (*destroy)(struct ip_set *set); /* Flush the elements */ void (*flush)(struct ip_set *set); - + /* Expire entries before listing */ + void (*expire)(struct ip_set *set); /* List set header data */ int (*head)(struct ip_set *set, struct sk_buff *skb); /* List elements */ int (*list)(struct ip_set *set, struct sk_buff *skb, struct netlink_callback *cb); + + /* Return true if "b" set is the same as "a" + * according to the set parameters */ + bool (*same_set)(const struct ip_set *a, const struct ip_set *b); }; /* Flags for the set type variants */ enum ip_set_type_flags { - IP_SET_FLAG_VMALLOC_BIT = 0, - IP_SET_FLAG_VMALLOC = (1 << IP_SET_FLAG_VMALLOC_BIT), - IP_SET_FLAG_TIMEOUT_BIT = 1, - IP_SET_FLAG_TIMEOUT = (1 << IP_SET_FLAG_TIMEOUT_BIT), + /* Set members created by kmalloc */ + IP_SET_FLAG_KMALLOC_BIT = 0, + IP_SET_FLAG_KMALLOC = (1 << IP_SET_FLAG_KMALLOC_BIT), }; /* The core set type structure */ @@ -278,17 +254,19 @@ struct ip_set_type { /* Typename */ char name[IPSET_MAXNAMELEN]; /* Protocol version */ - uint8_t protocol; + u8 protocol; /* Set features to control swapping */ - uint8_t features; + u8 features; + /* Set type dimension */ + u8 dimension; /* Supported family: may be AF_UNSPEC for both AF_INET/AF_INET6 */ - uint8_t family; + u8 family; /* Type revision */ - uint8_t revision; + u8 revision; /* Create set */ int (*create)(struct ip_set *set, - struct nlattr *head, int len, uint32_t flags); + struct nlattr *head, int len, u32 flags); /* Set this to THIS_MODULE if you are a module, otherwise NULL */ struct module *me; @@ -310,86 +288,90 @@ struct ip_set { /* The type variant doing the real job */ const struct ip_set_type_variant *variant; /* The actual INET family */ - uint8_t family; + u8 family; /* Set type flags, filled/modified by create/resize */ - uint8_t flags; + u8 flags; /* The type specific data */ void *data; }; /* register and unregister set references */ -extern ip_set_id_t ip_set_get_byname(const char name[IPSET_MAXNAMELEN]); +extern ip_set_id_t ip_set_get_byname(const char *name, struct ip_set **set); extern void ip_set_put_byindex(ip_set_id_t index); +extern const char * ip_set_name_byindex(ip_set_id_t index); +extern ip_set_id_t ip_set_nfnl_get(const char *name); +extern ip_set_id_t ip_set_nfnl_get_byindex(ip_set_id_t index); +extern void ip_set_nfnl_put(ip_set_id_t index); /* API for iptables set match, and SET target */ extern int ip_set_add(ip_set_id_t id, const struct sk_buff *skb, - uint8_t family, const uint8_t *flags); + u8 family, u8 dim, u8 flags); extern int ip_set_del(ip_set_id_t id, const struct sk_buff *skb, - uint8_t family, const uint8_t *flags); + u8 family, u8 dim, u8 flags); extern int ip_set_test(ip_set_id_t id, const struct sk_buff *skb, - uint8_t family, const uint8_t *flags); + u8 family, u8 dim, u8 flags); /* Allocate members */ static inline void * -ip_set_alloc(size_t size, gfp_t gfp_mask, uint8_t *flags) +ip_set_alloc(size_t size, gfp_t gfp_mask, u8 *flags) { - void *members = kzalloc(size, gfp_mask); + void *members = kzalloc(size, gfp_mask | __GFP_NOWARN); if (members) { - *flags &= ~IP_SET_FLAG_VMALLOC; - D("allocated with kmalloc %p", members); + *flags |= IP_SET_FLAG_KMALLOC; + pr_debug("%p: allocated with kmalloc", members); return members; } members = __vmalloc(size, gfp_mask | __GFP_ZERO, PAGE_KERNEL); if (!members) return NULL; - *flags |= IP_SET_FLAG_VMALLOC; - D("allocated with vmalloc %p", members); + *flags &= ~IP_SET_FLAG_KMALLOC; + pr_debug("%p: allocated with vmalloc", members); return members; } static inline void -ip_set_free(void *members, uint8_t flags) +ip_set_free(void *members, u8 flags) { - D("free with %s %p", flags & IP_SET_FLAG_VMALLOC ? "vmalloc" : "kmalloc", - members); - if (flags & IP_SET_FLAG_VMALLOC) - vfree(members); - else + pr_debug("%p: free with %s", members, + flags & IP_SET_FLAG_KMALLOC ? "kmalloc" : "vmalloc"); + if (flags & IP_SET_FLAG_KMALLOC) kfree(members); + else + vfree(members); } /* Useful converters */ -static inline uint32_t +static inline u32 ip_set_get_h32(const struct nlattr *attr) { - uint32_t value = nla_get_u32(attr); + u32 value = nla_get_u32(attr); return attr->nla_type & NLA_F_NET_BYTEORDER ? ntohl(value) : value; } -static inline uint16_t +static inline u16 ip_set_get_h16(const struct nlattr *attr) { - uint16_t value = nla_get_u16(attr); + u16 value = nla_get_u16(attr); return attr->nla_type & NLA_F_NET_BYTEORDER ? ntohs(value) : value; } -static inline uint32_t +static inline u32 ip_set_get_n32(const struct nlattr *attr) { - uint32_t value = nla_get_u32(attr); + u32 value = nla_get_u32(attr); return attr->nla_type & NLA_F_NET_BYTEORDER ? value : htonl(value); } -static inline uint16_t +static inline u16 ip_set_get_n16(const struct nlattr *attr) { - uint16_t value = nla_get_u16(attr); + u16 value = nla_get_u16(attr); return attr->nla_type & NLA_F_NET_BYTEORDER ? value : htons(value); } @@ -404,31 +386,49 @@ ip_set_get_n16(const struct nlattr *attr) NLA_PUT_BE16(skb, type | NLA_F_NET_BYTEORDER, value) /* Get address from skbuff */ -static inline uint32_t -ip4addr(const struct sk_buff *skb, const uint8_t *flags) +static inline u32 +ip4addr(const struct sk_buff *skb, bool src) { - return flags[0] & IPSET_SRC ? ip_hdr(skb)->saddr - : ip_hdr(skb)->daddr; + return src ? ip_hdr(skb)->saddr : ip_hdr(skb)->daddr; } static inline void -ip4addrptr(const struct sk_buff *skb, const uint8_t *flags, uint32_t *addr) +ip4addrptr(const struct sk_buff *skb, bool src, u32 *addr) { - *addr = flags[0] & IPSET_SRC ? ip_hdr(skb)->saddr - : ip_hdr(skb)->daddr; + *addr = src ? ip_hdr(skb)->saddr : ip_hdr(skb)->daddr; } static inline void -ip6addrptr(const struct sk_buff *skb, const uint8_t *flags, - struct in6_addr *addr) +ip6addrptr(const struct sk_buff *skb, bool src, struct in6_addr *addr) { - memcpy(addr, flags[0] & IPSET_SRC ? &ipv6_hdr(skb)->saddr - : &ipv6_hdr(skb)->daddr, + memcpy(addr, src ? &ipv6_hdr(skb)->saddr : &ipv6_hdr(skb)->daddr, sizeof(*addr)); } -#define pack_ip_port(map, ip, port) \ - (port + ((ip - ((map)->first_ip)) << 16)) +/* Interface to iptables/ip6tables */ + +#define SO_IP_SET 83 + +union ip_set_name_index { + char name[IPSET_MAXNAMELEN]; + ip_set_id_t index; +}; + +#define IP_SET_OP_GET_BYNAME 0x00000006 /* Get set index by name */ +struct ip_set_req_get_set { + unsigned op; + unsigned version; + union ip_set_name_index set; +}; + +#define IP_SET_OP_GET_BYINDEX 0x00000007 /* Get set name by index */ +/* Uses ip_set_req_get_set */ + +#define IP_SET_OP_VERSION 0x00000100 /* Ask kernel version */ +struct ip_set_req_version { + unsigned op; + unsigned version; +}; #endif /* __KERNEL__ */ diff --git a/kernel/include/linux/netfilter/ip_set_bitmap.h b/kernel/include/linux/netfilter/ip_set_bitmap.h index 49d0f5c..0d067d0 100644 --- a/kernel/include/linux/netfilter/ip_set_bitmap.h +++ b/kernel/include/linux/netfilter/ip_set_bitmap.h @@ -12,10 +12,10 @@ enum { /* Common functions */ -static inline uint32_t -range_to_mask(uint32_t from, uint32_t to, uint8_t *bits) +static inline u32 +range_to_mask(u32 from, u32 to, u8 *bits) { - uint32_t mask = 0xFFFFFFFE; + u32 mask = 0xFFFFFFFE; *bits = 32; while (--(*bits) > 0 && mask && (to & mask) != from) diff --git a/kernel/include/linux/netfilter/ip_set_chash.h b/kernel/include/linux/netfilter/ip_set_chash.h new file mode 100644 index 0000000..0d77a5d --- /dev/null +++ b/kernel/include/linux/netfilter/ip_set_chash.h @@ -0,0 +1,1096 @@ +#ifndef _IP_SET_CHASH_H +#define _IP_SET_CHASH_H + +#include +#include +#include + +#define CONCAT(a, b, c) a##b##c +#define TOKEN(a, b, c) CONCAT(a, b, c) + +/* Cache friendly hash with resizing when linear searching becomes too long. + * Internally jhash is used with the assumption that the size of the stored + * data is a multiple of sizeof(u32). If storage supports timeout, the + * timeout field must be the last one in the data structure. + */ + +/* Number of elements to store in an array block */ +#define CHASH_DEFAULT_ARRAY_SIZE 4 +/* Number of arrays: max ARRAY_SIZE * CHAIN_LIMIT "long" chains */ +#define CHASH_DEFAULT_CHAIN_LIMIT 3 + +struct chash_nets { + u32 nets; /* number of elements per cidr */ + u8 cidr; /* the cidr values added to the set */ +}; + +struct chash { + struct slist *htable; /* Hashtable of single linked lists */ + u32 maxelem; /* Max elements in the hash */ + u32 elements; /* Current element (vs timeout) */ + u32 initval; /* random jhash init value */ + u32 timeout; /* timeout value, if enabled */ + struct timer_list gc; /* garbage collection when timeout enabled */ + u8 htable_bits; /* size of hash table == 2^htable_bits */ + u8 array_size; /* number of elements in an array */ + u8 chain_limit; /* max number of arrays */ +#ifdef IP_SET_HASH_WITH_NETMASK + u8 netmask; /* netmask value for subnets to store */ +#endif +#ifdef IP_SET_HASH_WITH_NETS + struct chash_nets nets[0]; /* book keeping of networks */ +#endif +}; + +static inline u8 +htable_bits(u32 hashsize) +{ + /* Assume that hashsize == 2^htable_bits */ + u8 bits = fls(hashsize - 1); + if (jhash_size(bits) != hashsize) + /* Round up to the first 2^n value */ + bits = fls(hashsize); + + return bits; +} + +static inline void +add_cidr(struct chash_nets *nets, u8 host_mask, u8 cidr) +{ + u8 i; + + pr_debug("add_cidr %u", cidr); + for (i = 0; i < host_mask - 1 && nets[i].cidr; i++) { + /* Add in increasing prefix order, so larger cidr first */ + if (nets[i].cidr < cidr) + swap(nets[i].cidr, cidr); + } + if (i < host_mask - 1) + nets[i].cidr = cidr; +} + +static inline void +del_cidr(struct chash_nets *nets, u8 host_mask, u8 cidr) +{ + u8 i; + + pr_debug("del_cidr %u", cidr); + for (i = 0; i < host_mask - 2 && nets[i].cidr; i++) { + if (nets[i].cidr == cidr) + nets[i].cidr = cidr = nets[i+1].cidr; + } + nets[host_mask - 2].cidr = 0; +} + +static void +chash_destroy(struct slist *t, u8 htable_bits, u8 flags) +{ + struct slist *n, *tmp; + u32 i; + + for (i = 0; i < jhash_size(htable_bits); i++) + slist_for_each_safe(n, tmp, &t[i]) + /* FIXME: slab cache */ + kfree(n); + + ip_set_free(t, flags); +} + +static size_t +chash_memsize(const struct chash *h, size_t dsize, u8 host_mask) +{ + struct slist *n; + u32 i; + size_t memsize = sizeof(*h) +#ifdef IP_SET_HASH_WITH_NETS + + sizeof(struct chash_nets) * (host_mask - 1) +#endif + + jhash_size(h->htable_bits) * sizeof(struct slist); + + for (i = 0; i < jhash_size(h->htable_bits); i++) + slist_for_each(n, &h->htable[i]) + memsize += sizeof(struct slist) + + h->array_size * dsize; + + return memsize; +} + +static void +ip_set_hash_flush(struct ip_set *set) +{ + struct chash *h = set->data; + struct slist *n, *tmp; + u32 i; + + for (i = 0; i < jhash_size(h->htable_bits); i++) { + slist_for_each_safe(n, tmp, &h->htable[i]) + /* FIXME: slab cache */ + kfree(n); + h->htable[i].next = NULL; + } +#ifdef IP_SET_HASH_WITH_NETS + memset(h->nets, 0, sizeof(struct chash_nets) + * (set->family == AF_INET ? 31 : 127)); +#endif + h->elements = 0; +} + +static void +ip_set_hash_destroy(struct ip_set *set) +{ + struct chash *h = set->data; + + if (with_timeout(h->timeout)) + del_timer_sync(&h->gc); + + chash_destroy(h->htable, h->htable_bits, set->flags); + kfree(h); + + set->data = NULL; +} + +#define JHASH2(data, initval, htable_bits) \ +jhash2((u32 *)(data), sizeof(struct type_pf_elem)/sizeof(u32), initval) \ + & jhash_mask(htable_bits) + +#endif /* _IP_SET_CHASH_H */ + +/* Type/family dependent function prototypes */ + +#define type_pf_data_equal TOKEN(TYPE, PF, _data_equal) +#define type_pf_data_isnull TOKEN(TYPE, PF, _data_isnull) +#define type_pf_data_copy TOKEN(TYPE, PF, _data_copy) +#define type_pf_data_swap TOKEN(TYPE, PF, _data_swap) +#define type_pf_data_zero_out TOKEN(TYPE, PF, _data_zero_out) +#define type_pf_data_netmask TOKEN(TYPE, PF, _data_netmask) +#define type_pf_data_list TOKEN(TYPE, PF, _data_list) +#define type_pf_data_tlist TOKEN(TYPE, PF, _data_tlist) + +#define type_pf_elem TOKEN(TYPE, PF, _elem) +#define type_pf_telem TOKEN(TYPE, PF, _telem) +#define type_pf_data_timeout TOKEN(TYPE, PF, _data_timeout) +#define type_pf_data_expired TOKEN(TYPE, PF, _data_expired) +#define type_pf_data_swap_timeout TOKEN(TYPE, PF, _data_swap_timeout) +#define type_pf_data_timeout_set TOKEN(TYPE, PF, _data_timeout_set) + +#define type_pf_chash_readd TOKEN(TYPE, PF, _chash_readd) +#define type_pf_chash_del_elem TOKEN(TYPE, PF, _chash_del_elem) +#define type_pf_chash_add TOKEN(TYPE, PF, _chash_add) +#define type_pf_chash_del TOKEN(TYPE, PF, _chash_del) +#define type_pf_chash_test_cidrs TOKEN(TYPE, PF, _chash_test_cidrs) +#define type_pf_chash_test TOKEN(TYPE, PF, _chash_test) + +#define type_pf_chash_treadd TOKEN(TYPE, PF, _chash_treadd) +#define type_pf_chash_del_telem TOKEN(TYPE, PF, _chash_del_telem) +#define type_pf_chash_expire TOKEN(TYPE, PF, _chash_expire) +#define type_pf_chash_tadd TOKEN(TYPE, PF, _chash_tadd) +#define type_pf_chash_tdel TOKEN(TYPE, PF, _chash_tdel) +#define type_pf_chash_ttest_cidrs TOKEN(TYPE, PF, _chash_ttest_cidrs) +#define type_pf_chash_ttest TOKEN(TYPE, PF, _chash_ttest) + +#define type_pf_resize TOKEN(TYPE, PF, _resize) +#define type_pf_tresize TOKEN(TYPE, PF, _tresize) +#define type_pf_flush ip_set_hash_flush +#define type_pf_destroy ip_set_hash_destroy +#define type_pf_head TOKEN(TYPE, PF, _head) +#define type_pf_list TOKEN(TYPE, PF, _list) +#define type_pf_tlist TOKEN(TYPE, PF, _tlist) +#define type_pf_same_set TOKEN(TYPE, PF, _same_set) +#define type_pf_kadt TOKEN(TYPE, PF, _kadt) +#define type_pf_uadt TOKEN(TYPE, PF, _uadt) +#define type_pf_gc TOKEN(TYPE, PF, _gc) +#define type_pf_gc_init TOKEN(TYPE, PF, _gc_init) +#define type_pf_variant TOKEN(TYPE, PF, _variant) +#define type_pf_tvariant TOKEN(TYPE, PF, _tvariant) + +/* Flavour without timeout */ + +#define chash_data(n, i) \ +(struct type_pf_elem *)((char *)(n) + sizeof(struct slist) + (i)*sizeof(struct type_pf_elem)) + +static int +type_pf_chash_readd(struct chash *h, struct slist *t, u8 htable_bits, + const struct type_pf_elem *value, gfp_t gfp_flags) +{ + struct slist *n, *prev; + struct type_pf_elem *data; + void *tmp; + int i = 0, j = 0; + u32 hash = JHASH2(value, h->initval, htable_bits); + + slist_for_each_prev(prev, n, &t[hash]) { + for (i = 0; i < h->array_size; i++) { + data = chash_data(n, i); + if (type_pf_data_isnull(data)) { + tmp = n; + goto found; + } + } + j++; + } + if (j < h->chain_limit) { + tmp = kzalloc(h->array_size * sizeof(struct type_pf_elem) + + sizeof(struct slist), gfp_flags); + if (!tmp) + return -ENOMEM; + prev->next = (struct slist *) tmp; + data = chash_data(tmp, 0); + } else { + /* Rehashing */ + return -EAGAIN; + } +found: + type_pf_data_copy(data, value); + return 0; +} + +static void +type_pf_chash_del_elem(struct chash *h, struct slist *prev, + struct slist *n, int i) +{ + struct type_pf_elem *data = chash_data(n, i); + struct slist *tmp; + int j; + + if (n->next != NULL) { + for (prev = n, tmp = n->next; + tmp->next != NULL; + prev = tmp, tmp = tmp->next) + /* Find last array */; + j = 0; + } else { + /* Already at last array */ + tmp = n; + j = i; + } + /* Find last non-empty element */ + for (; j < h->array_size - 1; j++) + if (type_pf_data_isnull(chash_data(tmp, j + 1))) + break; + + if (!(tmp == n && i == j)) { + type_pf_data_swap(data, chash_data(tmp, j)); + } +#ifdef IP_SET_HASH_WITH_NETS + if (--h->nets[data->cidr-1].nets == 0) + del_cidr(h->nets, HOST_MASK, data->cidr); +#endif + if (j == 0) { + prev->next = NULL; + kfree(tmp); + } else + type_pf_data_zero_out(chash_data(tmp, j)); + + h->elements--; +} + +static int +type_pf_resize(struct ip_set *set, gfp_t gfp_flags, bool retried) +{ + struct chash *h = set->data; + u8 htable_bits = h->htable_bits; + struct slist *t, *n; + const struct type_pf_elem *data; + u32 i, j; + u8 oflags, flags; + int ret; + +retry: + ret = 0; + htable_bits++; + if (!htable_bits) + /* In case we have plenty of memory :-) */ + return -IPSET_ERR_HASH_FULL; + t = ip_set_alloc(jhash_size(htable_bits) * sizeof(struct slist), + gfp_flags, &flags); + if (!t) + return -ENOMEM; + + write_lock_bh(&set->lock); + flags = oflags = set->flags; + for (i = 0; i < jhash_size(h->htable_bits); i++) { +next_slot: + slist_for_each(n, &h->htable[i]) { + for (j = 0; j < h->array_size; j++) { + data = chash_data(n, j); + if (type_pf_data_isnull(data)) { + i++; + goto next_slot; + } + ret = type_pf_chash_readd(h, t, htable_bits, + data, gfp_flags); + if (ret < 0) { + write_unlock_bh(&set->lock); + chash_destroy(t, htable_bits, flags); + if (ret == -EAGAIN) + goto retry; + return ret; + } + } + } + } + + n = h->htable; + i = h->htable_bits; + + h->htable = t; + h->htable_bits = htable_bits; + set->flags = flags; + write_unlock_bh(&set->lock); + + chash_destroy(n, i, oflags); + + return 0; +} + +static int +type_pf_chash_add(struct ip_set *set, void *value, + gfp_t gfp_flags, u32 timeout) +{ + struct chash *h = set->data; + const struct type_pf_elem *d = value; + struct slist *n, *prev, *t = h->htable; + struct type_pf_elem *data; + void *tmp; + int i = 0, j = 0; + u32 hash; + +#ifdef IP_SET_HASH_WITH_NETS + if (h->elements >= h->maxelem || h->nets[d->cidr-1].nets == UINT_MAX) +#else + if (h->elements >= h->maxelem) +#endif + return -IPSET_ERR_HASH_FULL; + + hash = JHASH2(value, h->initval, h->htable_bits); + slist_for_each_prev(prev, n, &t[hash]) { + for (i = 0; i < h->array_size; i++) { + data = chash_data(n, i); + if (type_pf_data_isnull(data)) { + tmp = n; + goto found; + } + if (type_pf_data_equal(data, d)) + return -IPSET_ERR_EXIST; + } + j++; + } + if (j < h->chain_limit) { + tmp = kzalloc(h->array_size * sizeof(struct type_pf_elem) + + sizeof(struct slist), gfp_flags); + if (!tmp) + return -ENOMEM; + prev->next = (struct slist *) tmp; + data = chash_data(tmp, 0); + } else { + /* Rehashing */ + return -EAGAIN; + } +found: + type_pf_data_copy(data, d); +#ifdef IP_SET_HASH_WITH_NETS + if (h->nets[d->cidr-1].nets++ == 0) + add_cidr(h->nets, HOST_MASK, d->cidr); +#endif + h->elements++; + return 0; +} + +static int +type_pf_chash_del(struct ip_set *set, void *value, + gfp_t gfp_flags, u32 timeout) +{ + struct chash *h = set->data; + const struct type_pf_elem *d = value; + struct slist *n, *prev; + int i; + struct type_pf_elem *data; + u32 hash = JHASH2(value, h->initval, h->htable_bits); + + slist_for_each_prev(prev, n, &h->htable[hash]) + for (i = 0; i < h->array_size; i++) { + data = chash_data(n, i); + if (type_pf_data_isnull(data)) + return -IPSET_ERR_EXIST; + if (type_pf_data_equal(data, d)) { + type_pf_chash_del_elem(h, prev, n, i); + return 0; + } + } + + return -IPSET_ERR_EXIST; +} + +#ifdef IP_SET_HASH_WITH_NETS +static inline int +type_pf_chash_test_cidrs(struct ip_set *set, + struct type_pf_elem *d, + gfp_t gfp_flags, u32 timeout) +{ + struct chash *h = set->data; + struct slist *n; + const struct type_pf_elem *data; + int i, j = 0; + u32 hash; + u8 host_mask = set->family == AF_INET ? 32 : 128; + +retry: + pr_debug("test by nets"); + for (; j < host_mask - 1 && h->nets[j].cidr; j++) { + type_pf_data_netmask(d, h->nets[j].cidr); + hash = JHASH2(d, h->initval, h->htable_bits); + slist_for_each(n, &h->htable[hash]) + for (i = 0; i < h->array_size; i++) { + data = chash_data(n, i); + if (type_pf_data_isnull(data)) { + j++; + goto retry; + } + if (type_pf_data_equal(data, d)) + return 1; + } + } + return 0; +} +#endif + +static inline int +type_pf_chash_test(struct ip_set *set, void *value, + gfp_t gfp_flags, u32 timeout) +{ + struct chash *h = set->data; + struct type_pf_elem *d = value; + struct slist *n; + const struct type_pf_elem *data; + int i; + u32 hash; +#ifdef IP_SET_HASH_WITH_NETS + u8 host_mask = set->family == AF_INET ? 32 : 128; + + if (d->cidr == host_mask) + return type_pf_chash_test_cidrs(set, d, gfp_flags, timeout); +#endif + + hash = JHASH2(d, h->initval, h->htable_bits); + slist_for_each(n, &h->htable[hash]) + for (i = 0; i < h->array_size; i++) { + data = chash_data(n, i); + if (type_pf_data_isnull(data)) + return 0; + if (type_pf_data_equal(data, d)) + return 1; + } + return 0; +} + +static int +type_pf_head(struct ip_set *set, struct sk_buff *skb) +{ + const struct chash *h = set->data; + struct nlattr *nested; + size_t memsize; + + read_lock_bh(&set->lock); + memsize = chash_memsize(h, with_timeout(h->timeout) + ? sizeof(struct type_pf_telem) + : sizeof(struct type_pf_elem), + set->family == AF_INET ? 32 : 128); + read_unlock_bh(&set->lock); + + nested = ipset_nest_start(skb, IPSET_ATTR_DATA); + if (!nested) + goto nla_put_failure; + NLA_PUT_NET32(skb, IPSET_ATTR_HASHSIZE, + htonl(jhash_size(h->htable_bits))); + NLA_PUT_NET32(skb, IPSET_ATTR_MAXELEM, htonl(h->maxelem)); +#ifdef IP_SET_HASH_WITH_NETMASK + if (h->netmask != HOST_MASK) + NLA_PUT_U8(skb, IPSET_ATTR_NETMASK, h->netmask); +#endif + NLA_PUT_NET32(skb, IPSET_ATTR_REFERENCES, + htonl(atomic_read(&set->ref) - 1)); + NLA_PUT_NET32(skb, IPSET_ATTR_MEMSIZE, htonl(memsize)); + if (with_timeout(h->timeout)) + NLA_PUT_NET32(skb, IPSET_ATTR_TIMEOUT, htonl(h->timeout)); + ipset_nest_end(skb, nested); + + return 0; +nla_put_failure: + return -EFAULT; +} + +static int +type_pf_list(struct ip_set *set, + struct sk_buff *skb, struct netlink_callback *cb) +{ + const struct chash *h = set->data; + struct nlattr *atd, *nested; + struct slist *n; + const struct type_pf_elem *data; + u32 first = cb->args[2]; + int i; + + atd = ipset_nest_start(skb, IPSET_ATTR_ADT); + if (!atd) + return -EFAULT; + pr_debug("list hash set %s", set->name); + for (; cb->args[2] < jhash_size(h->htable_bits); cb->args[2]++) { + slist_for_each(n, &h->htable[cb->args[2]]) { + for (i = 0; i < h->array_size; i++) { + data = chash_data(n, i); + if (type_pf_data_isnull(data)) + break; + pr_debug("list hash %lu slist %p i %u", + cb->args[2], n, i); + nested = ipset_nest_start(skb, IPSET_ATTR_DATA); + if (!nested) { + if (cb->args[2] == first) { + nla_nest_cancel(skb, atd); + return -EFAULT; + } else + goto nla_put_failure; + } + if (type_pf_data_list(skb, data)) + goto nla_put_failure; + ipset_nest_end(skb, nested); + } + } + } + ipset_nest_end(skb, atd); + /* Set listing finished */ + cb->args[2] = 0; + + return 0; + +nla_put_failure: + nla_nest_cancel(skb, nested); + ipset_nest_end(skb, atd); + return 0; +} + +static int +type_pf_kadt(struct ip_set *set, const struct sk_buff * skb, + enum ipset_adt adt, u8 pf, u8 dim, u8 flags); +static int +type_pf_uadt(struct ip_set *set, struct nlattr *head, int len, + enum ipset_adt adt, u32 *lineno, u32 flags); + +static const struct ip_set_type_variant type_pf_variant __read_mostly = { + .kadt = type_pf_kadt, + .uadt = type_pf_uadt, + .adt = { + [IPSET_ADD] = type_pf_chash_add, + [IPSET_DEL] = type_pf_chash_del, + [IPSET_TEST] = type_pf_chash_test, + }, + .destroy = type_pf_destroy, + .flush = type_pf_flush, + .head = type_pf_head, + .list = type_pf_list, + .resize = type_pf_resize, + .same_set = type_pf_same_set, +}; + +/* Flavour with timeout support */ + +#define chash_tdata(n, i) \ +(struct type_pf_elem *)((char *)(n) + sizeof(struct slist) + (i)*sizeof(struct type_pf_telem)) + +static inline u32 +type_pf_data_timeout(const struct type_pf_elem *data) +{ + const struct type_pf_telem *tdata = + (const struct type_pf_telem *) data; + + return tdata->timeout; +} + +static inline bool +type_pf_data_expired(const struct type_pf_elem *data) +{ + const struct type_pf_telem *tdata = + (const struct type_pf_telem *) data; + + return ip_set_timeout_expired(tdata->timeout); +} + +static inline void +type_pf_data_swap_timeout(struct type_pf_elem *src, + struct type_pf_elem *dst) +{ + struct type_pf_telem *x = (struct type_pf_telem *) src; + struct type_pf_telem *y = (struct type_pf_telem *) dst; + + swap(x->timeout, y->timeout); +} + +static inline void +type_pf_data_timeout_set(struct type_pf_elem *data, u32 timeout) +{ + struct type_pf_telem *tdata = (struct type_pf_telem *) data; + + tdata->timeout = ip_set_timeout_set(timeout); +} + +static int +type_pf_chash_treadd(struct chash *h, struct slist *t, u8 htable_bits, + const struct type_pf_elem *value, + gfp_t gfp_flags, u32 timeout) +{ + struct slist *n, *prev; + struct type_pf_elem *data; + void *tmp; + int i = 0, j = 0; + u32 hash = JHASH2(value, h->initval, htable_bits); + + slist_for_each_prev(prev, n, &t[hash]) { + for (i = 0; i < h->array_size; i++) { + data = chash_tdata(n, i); + if (type_pf_data_isnull(data)) { + tmp = n; + goto found; + } + } + j++; + } + if (j < h->chain_limit) { + tmp = kzalloc(h->array_size * sizeof(struct type_pf_telem) + + sizeof(struct slist), gfp_flags); + if (!tmp) + return -ENOMEM; + prev->next = (struct slist *) tmp; + data = chash_tdata(tmp, 0); + } else { + /* Rehashing */ + return -EAGAIN; + } +found: + type_pf_data_copy(data, value); + type_pf_data_timeout_set(data, timeout); + return 0; +} + +static void +type_pf_chash_del_telem(struct chash *h, struct slist *prev, + struct slist *n, int i) +{ + struct type_pf_elem *d, *data = chash_tdata(n, i); + struct slist *tmp; + int j; + + pr_debug("del %u", i); + if (n->next != NULL) { + for (prev = n, tmp = n->next; + tmp->next != NULL; + prev = tmp, tmp = tmp->next) + /* Find last array */; + j = 0; + } else { + /* Already at last array */ + tmp = n; + j = i; + } + /* Find last non-empty element */ + for (; j < h->array_size - 1; j++) + if (type_pf_data_isnull(chash_tdata(tmp, j + 1))) + break; + + d = chash_tdata(tmp, j); + if (!(tmp == n && i == j)) { + type_pf_data_swap(data, d); + type_pf_data_swap_timeout(data, d); + } +#ifdef IP_SET_HASH_WITH_NETS + if (--h->nets[data->cidr-1].nets == 0) + del_cidr(h->nets, HOST_MASK, data->cidr); +#endif + if (j == 0) { + prev->next = NULL; + kfree(tmp); + } else + type_pf_data_zero_out(d); + + h->elements--; +} + +static void +type_pf_chash_expire(struct chash *h) +{ + struct slist *n, *prev; + struct type_pf_elem *data; + u32 i; + int j; + + for (i = 0; i < jhash_size(h->htable_bits); i++) + slist_for_each_prev(prev, n, &h->htable[i]) + for (j = 0; j < h->array_size; j++) { + data = chash_tdata(n, j); + if (type_pf_data_isnull(data)) + break; + if (type_pf_data_expired(data)) { + pr_debug("expire %u/%u", i, j); + type_pf_chash_del_telem(h, prev, n, j); + } + } +} + +static int +type_pf_tresize(struct ip_set *set, gfp_t gfp_flags, bool retried) +{ + struct chash *h = set->data; + u8 htable_bits = h->htable_bits; + struct slist *t, *n; + const struct type_pf_elem *data; + u32 i, j; + u8 oflags, flags; + int ret; + + /* Try to cleanup once */ + if (!retried) { + i = h->elements; + write_lock_bh(&set->lock); + type_pf_chash_expire(set->data); + write_unlock_bh(&set->lock); + if (h->elements < i) + return 0; + } + +retry: + ret = 0; + htable_bits++; + if (!htable_bits) + /* In case we have plenty of memory :-) */ + return -IPSET_ERR_HASH_FULL; + t = ip_set_alloc(jhash_size(htable_bits) * sizeof(struct slist), + gfp_flags, &flags); + if (!t) + return -ENOMEM; + + write_lock_bh(&set->lock); + flags = oflags = set->flags; + for (i = 0; i < jhash_size(h->htable_bits); i++) { +next_slot: + slist_for_each(n, &h->htable[i]) { + for (j = 0; j < h->array_size; j++) { + data = chash_tdata(n, j); + if (type_pf_data_isnull(data)) { + i++; + goto next_slot; + } + ret = type_pf_chash_treadd(h, t, htable_bits, + data, gfp_flags, + type_pf_data_timeout(data)); + if (ret < 0) { + write_unlock_bh(&set->lock); + chash_destroy(t, htable_bits, flags); + if (ret == -EAGAIN) + goto retry; + return ret; + } + } + } + } + + n = h->htable; + i = h->htable_bits; + + h->htable = t; + h->htable_bits = htable_bits; + set->flags = flags; + write_unlock_bh(&set->lock); + + chash_destroy(n, i, oflags); + + return 0; +} + +static int +type_pf_chash_tadd(struct ip_set *set, void *value, + gfp_t gfp_flags, u32 timeout) +{ + struct chash *h = set->data; + const struct type_pf_elem *d = value; + struct slist *n, *prev, *t = h->htable; + struct type_pf_elem *data; + void *tmp; + int i = 0, j = 0; + u32 hash; + + if (h->elements >= h->maxelem) + /* FIXME: when set is full, we slow down here */ + type_pf_chash_expire(h); +#ifdef IP_SET_HASH_WITH_NETS + if (h->elements >= h->maxelem || h->nets[d->cidr-1].nets == UINT_MAX) +#else + if (h->elements >= h->maxelem) +#endif + return -IPSET_ERR_HASH_FULL; + + hash = JHASH2(d, h->initval, h->htable_bits); + slist_for_each_prev(prev, n, &t[hash]) { + for (i = 0; i < h->array_size; i++) { + data = chash_tdata(n, i); + if (type_pf_data_isnull(data) + || type_pf_data_expired(data)) { + tmp = n; + goto found; + } + if (type_pf_data_equal(data, d)) + return -IPSET_ERR_EXIST; + } + j++; + } + if (j < h->chain_limit) { + tmp = kzalloc(h->array_size * sizeof(struct type_pf_telem) + + sizeof(struct slist), gfp_flags); + if (!tmp) + return -ENOMEM; + prev->next = (struct slist *) tmp; + data = chash_tdata(tmp, 0); + } else { + /* Rehashing */ + return -EAGAIN; + } +found: + if (type_pf_data_isnull(data)) { + h->elements++; +#ifdef IP_SET_HASH_WITH_NETS + } else { + if (--h->nets[data->cidr-1].nets == 0) + del_cidr(h->nets, HOST_MASK, data->cidr); + } + if (h->nets[d->cidr-1].nets++ == 0) { + add_cidr(h->nets, HOST_MASK, d->cidr); +#endif + } + type_pf_data_copy(data, d); + type_pf_data_timeout_set(data, timeout); + return 0; +} + +static int +type_pf_chash_tdel(struct ip_set *set, void *value, + gfp_t gfp_flags, u32 timeout) +{ + struct chash *h = set->data; + const struct type_pf_elem *d = value; + struct slist *n, *prev; + int i, ret = 0; + struct type_pf_elem *data; + u32 hash = JHASH2(value, h->initval, h->htable_bits); + + slist_for_each_prev(prev, n, &h->htable[hash]) + for (i = 0; i < h->array_size; i++) { + data = chash_tdata(n, i); + if (type_pf_data_isnull(data)) + return -IPSET_ERR_EXIST; + if (type_pf_data_equal(data, d)) { + if (type_pf_data_expired(data)) + ret = -IPSET_ERR_EXIST; + type_pf_chash_del_telem(h, prev, n, i); + return ret; + } + } + + return -IPSET_ERR_EXIST; +} + +#ifdef IP_SET_HASH_WITH_NETS +static inline int +type_pf_chash_ttest_cidrs(struct ip_set *set, + struct type_pf_elem *d, + gfp_t gfp_flags, u32 timeout) +{ + struct chash *h = set->data; + struct type_pf_elem *data; + struct slist *n; + int i, j = 0; + u32 hash; + u8 host_mask = set->family == AF_INET ? 32 : 128; + +retry: + for (; j < host_mask - 1 && h->nets[j].cidr; j++) { + type_pf_data_netmask(d, h->nets[j].cidr); + hash = JHASH2(d, h->initval, h->htable_bits); + slist_for_each(n, &h->htable[hash]) + for (i = 0; i < h->array_size; i++) { + data = chash_tdata(n, i); + if (type_pf_data_isnull(data)) { + j++; + goto retry; + } + if (type_pf_data_equal(data, d)) + return !type_pf_data_expired(data); + } + } + return 0; +} +#endif + +static inline int +type_pf_chash_ttest(struct ip_set *set, void *value, + gfp_t gfp_flags, u32 timeout) +{ + struct chash *h = set->data; + struct type_pf_elem *data, *d = value; + struct slist *n; + int i; + u32 hash; +#ifdef IP_SET_HASH_WITH_NETS + u8 host_mask = set->family == AF_INET ? 32 : 128; + + if (d->cidr == host_mask) + return type_pf_chash_ttest_cidrs(set, d, gfp_flags, + timeout); +#endif + hash = JHASH2(d, h->initval, h->htable_bits); + slist_for_each(n, &h->htable[hash]) + for (i = 0; i < h->array_size; i++) { + data = chash_tdata(n, i); + if (type_pf_data_isnull(data)) + return 0; + if (type_pf_data_equal(data, d)) + return !type_pf_data_expired(data); + } + return 0; +} + +static int +type_pf_tlist(struct ip_set *set, + struct sk_buff *skb, struct netlink_callback *cb) +{ + const struct chash *h = set->data; + struct nlattr *atd, *nested; + struct slist *n; + const struct type_pf_elem *data; + u32 first = cb->args[2]; + int i; + + atd = ipset_nest_start(skb, IPSET_ATTR_ADT); + if (!atd) + return -EFAULT; + for (; cb->args[2] < jhash_size(h->htable_bits); cb->args[2]++) { + slist_for_each(n, &h->htable[cb->args[2]]) { + for (i = 0; i < h->array_size; i++) { + data = chash_tdata(n, i); + pr_debug("list %p %u", n, i); + if (type_pf_data_isnull(data)) + break; + if (type_pf_data_expired(data)) + continue; + pr_debug("do list %p %u", n, i); + nested = ipset_nest_start(skb, IPSET_ATTR_DATA); + if (!nested) { + if (cb->args[2] == first) { + nla_nest_cancel(skb, atd); + return -EFAULT; + } else + goto nla_put_failure; + } + if (type_pf_data_tlist(skb, data)) + goto nla_put_failure; + ipset_nest_end(skb, nested); + } + } + } + ipset_nest_end(skb, atd); + /* Set listing finished */ + cb->args[2] = 0; + + return 0; + +nla_put_failure: + nla_nest_cancel(skb, nested); + ipset_nest_end(skb, atd); + return 0; +} + +static const struct ip_set_type_variant type_pf_tvariant __read_mostly = { + .kadt = type_pf_kadt, + .uadt = type_pf_uadt, + .adt = { + [IPSET_ADD] = type_pf_chash_tadd, + [IPSET_DEL] = type_pf_chash_tdel, + [IPSET_TEST] = type_pf_chash_ttest, + }, + .destroy = type_pf_destroy, + .flush = type_pf_flush, + .head = type_pf_head, + .list = type_pf_tlist, + .resize = type_pf_tresize, + .same_set = type_pf_same_set, +}; + +static void +type_pf_gc(unsigned long ul_set) +{ + struct ip_set *set = (struct ip_set *) ul_set; + struct chash *h = set->data; + + pr_debug("called"); + write_lock_bh(&set->lock); + type_pf_chash_expire(h); + write_unlock_bh(&set->lock); + + h->gc.expires = jiffies + IPSET_GC_PERIOD(h->timeout) * HZ; + add_timer(&h->gc); +} + +static inline void +type_pf_gc_init(struct ip_set *set) +{ + struct chash *h = set->data; + + init_timer(&h->gc); + h->gc.data = (unsigned long) set; + h->gc.function = type_pf_gc; + h->gc.expires = jiffies + IPSET_GC_PERIOD(h->timeout) * HZ; + add_timer(&h->gc); + pr_debug("gc initialized, run in every %u", IPSET_GC_PERIOD(h->timeout)); +} + +#undef type_pf_data_equal +#undef type_pf_data_isnull +#undef type_pf_data_copy +#undef type_pf_data_swap +#undef type_pf_data_zero_out +#undef type_pf_data_list +#undef type_pf_data_tlist + +#undef type_pf_elem +#undef type_pf_telem +#undef type_pf_data_timeout +#undef type_pf_data_expired +#undef type_pf_data_swap_timeout +#undef type_pf_data_netmask +#undef type_pf_data_timeout_set + +#undef type_pf_chash_readd +#undef type_pf_chash_del_elem +#undef type_pf_chash_add +#undef type_pf_chash_del +#undef type_pf_chash_test_cidrs +#undef type_pf_chash_test + +#undef type_pf_chash_treadd +#undef type_pf_chash_del_telem +#undef type_pf_chash_expire +#undef type_pf_chash_tadd +#undef type_pf_chash_tdel +#undef type_pf_chash_ttest_cidrs +#undef type_pf_chash_ttest + +#undef type_pf_resize +#undef type_pf_tresize +#undef type_pf_flush +#undef type_pf_destroy +#undef type_pf_head +#undef type_pf_list +#undef type_pf_tlist +#undef type_pf_same_set +#undef type_pf_kadt +#undef type_pf_uadt +#undef type_pf_gc +#undef type_pf_gc_init +#undef type_pf_variant +#undef type_pf_tvariant diff --git a/kernel/include/linux/netfilter/ip_set_getport.h b/kernel/include/linux/netfilter/ip_set_getport.h index 855f12a..ffa89f1 100644 --- a/kernel/include/linux/netfilter/ip_set_getport.h +++ b/kernel/include/linux/netfilter/ip_set_getport.h @@ -8,8 +8,8 @@ #define IPSET_INVALID_PORT 65536 /* We must handle non-linear skbs */ -static uint32_t -get_port(uint8_t pf, const struct sk_buff *skb, const uint8_t *flags) +static bool +get_port(u8 pf, const struct sk_buff *skb, bool src, u16 *port) { unsigned short protocol; unsigned int protoff; @@ -30,19 +30,19 @@ get_port(uint8_t pf, const struct sk_buff *skb, const uint8_t *flags) protohdr = ipv6_find_hdr(skb, &protoff, -1, &frag_off); if (protohdr < 0) - return IPSET_INVALID_PORT; + return false; protocol = protohdr; fragoff = frag_off; break; } default: - return IPSET_INVALID_PORT; + return false; } /* See comments at tcp_match in ip_tables.c */ if (fragoff) - return IPSET_INVALID_PORT; + return false; switch (protocol) { case IPPROTO_TCP: { @@ -52,9 +52,10 @@ get_port(uint8_t pf, const struct sk_buff *skb, const uint8_t *flags) th = skb_header_pointer(skb, protoff, sizeof(_tcph), &_tcph); if (th == NULL) /* No choice either */ - return IPSET_INVALID_PORT; + return false; - return flags[0] & IPSET_SRC ? th->source : th->dest; + *port = src ? th->source : th->dest; + break; } case IPPROTO_UDP: { struct udphdr _udph; @@ -63,14 +64,16 @@ get_port(uint8_t pf, const struct sk_buff *skb, const uint8_t *flags) uh = skb_header_pointer(skb, protoff, sizeof(_udph), &_udph); if (uh == NULL) /* No choice either */ - return IPSET_INVALID_PORT; + return false; - return flags[0] & IPSET_SRC ? uh->source : uh->dest; + *port = src ? uh->source : uh->dest; + break; } default: - return IPSET_INVALID_PORT; + return false; } + return true; } -#endif /* __KERNEL__ */ +#endif /* __KERNEL__ */ #endif /*_IP_SET_GETPORT_H*/ diff --git a/kernel/include/linux/netfilter/ip_set_hash.h b/kernel/include/linux/netfilter/ip_set_hash.h index dd183b7..c1a6964 100644 --- a/kernel/include/linux/netfilter/ip_set_hash.h +++ b/kernel/include/linux/netfilter/ip_set_hash.h @@ -9,12 +9,11 @@ enum { #ifdef __KERNEL__ -#define initval_t uint32_t - #define IPSET_DEFAULT_HASHSIZE 1024 +#define IPSET_MIMINAL_HASHSIZE 64 #define IPSET_DEFAULT_MAXELEM 65536 #define IPSET_DEFAULT_PROBES 4 -#define IPSET_DEFAULT_RESIZE 50 +#define IPSET_DEFAULT_RESIZE 100 #endif /* __KERNEL__ */ diff --git a/kernel/include/linux/netfilter/ip_set_jhash.h b/kernel/include/linux/netfilter/ip_set_jhash.h index 90bfcc3..d5e0d6d 100644 --- a/kernel/include/linux/netfilter/ip_set_jhash.h +++ b/kernel/include/linux/netfilter/ip_set_jhash.h @@ -1,7 +1,6 @@ #ifndef _LINUX_JHASH_H #define _LINUX_JHASH_H - -/* jhash.h: Jenkins hash support. +/* jhash.c: Jenkins hash support. * * Copyright (C) 2006. Bob Jenkins (bob_jenkins@burtleburtle.net) * @@ -17,141 +16,106 @@ * if SELF_TEST is defined. You can use this free for any purpose. It's in * the public domain. It has no warranty. * - * Copyright (C) 2009 Jozsef Kadlecsik (kadlec@blackhole.kfki.hu) + * Copyright (C) 2009-2010 Jozsef Kadlecsik (kadlec@blackhole.kfki.hu) * * I've modified Bob's hash to be useful in the Linux kernel, and - * any bugs present are my fault. Jozsef + * any bugs present are my fault. The generic jhash is left out intentionally. + * Jozsef */ - -#define __rot(x,k) (((x)<<(k)) | ((x)>>(32-(k)))) - -/* __jhash_mix - mix 3 32-bit values reversibly. */ -#define __jhash_mix(a,b,c) \ -{ \ - a -= c; a ^= __rot(c, 4); c += b; \ - b -= a; b ^= __rot(a, 6); a += c; \ - c -= b; c ^= __rot(b, 8); b += a; \ - a -= c; a ^= __rot(c,16); c += b; \ - b -= a; b ^= __rot(a,19); a += c; \ - c -= b; c ^= __rot(b, 4); b += a; \ +#ifdef __KERNEL__ +#include + +/* Best hash sizes are of power of two */ +#define jhash_size(n) ((u32)1<<(n)) +/* Mask the hash value, i.e (value & jhash_mask(n)) instead of (value % n) */ +#define jhash_mask(n) (jhash_size(n)-1) + +/* __jhash_rot - rotate 32 bit */ +#define __jhash_rot(x,k) (((x)<<(k)) | ((x)>>(32-(k)))) + +/* __jhash_mix -- mix 3 32-bit values reversibly. */ +#define __jhash_mix(a,b,c) \ +{ \ + a -= c; a ^= __jhash_rot(c, 4); c += b; \ + b -= a; b ^= __jhash_rot(a, 6); a += c; \ + c -= b; c ^= __jhash_rot(b, 8); b += a; \ + a -= c; a ^= __jhash_rot(c,16); c += b; \ + b -= a; b ^= __jhash_rot(a,19); a += c; \ + c -= b; c ^= __jhash_rot(b, 4); b += a; \ } /* __jhash_final - final mixing of 3 32-bit values (a,b,c) into c */ -#define __jhash_final(a,b,c) \ -{ \ - c ^= b; c -= __rot(b,14); \ - a ^= c; a -= __rot(c,11); \ - b ^= a; b -= __rot(a,25); \ - c ^= b; c -= __rot(b,16); \ - a ^= c; a -= __rot(c,4); \ - b ^= a; b -= __rot(a,14); \ - c ^= b; c -= __rot(b,24); \ +#define __jhash_final(a,b,c) \ +{ \ + c ^= b; c -= __jhash_rot(b,14); \ + a ^= c; a -= __jhash_rot(c,11); \ + b ^= a; b -= __jhash_rot(a,25); \ + c ^= b; c -= __jhash_rot(b,16); \ + a ^= c; a -= __jhash_rot(c,4); \ + b ^= a; b -= __jhash_rot(a,14); \ + c ^= b; c -= __jhash_rot(b,24); \ } -/* An arbitrary value */ -#define JHASH_RANDOM_PARAM 0xdeadbeef - -/* The most generic version, hashes an arbitrary sequence - * of bytes. No alignment or length assumptions are made about - * the input key. The result depends on endianness. - */ -static inline u32 jhash(const void *key, u32 length, u32 initval) -{ - u32 a,b,c; - const u8 *k = key; - - /* Set up the internal state */ - a = b = c = JHASH_RANDOM_PARAM + length + initval; - - /* all but the last block: affect some 32 bits of (a,b,c) */ - while (length > 12) { - a += (k[0] + ((u32)k[1]<<8) + ((u32)k[2]<<16) + ((u32)k[3]<<24)); - b += (k[4] + ((u32)k[5]<<8) + ((u32)k[6]<<16) + ((u32)k[7]<<24)); - c += (k[8] + ((u32)k[9]<<8) + ((u32)k[10]<<16) + ((u32)k[11]<<24)); - __jhash_mix(a, b, c); - length -= 12; - k += 12; - } - - /* last block: affect all 32 bits of (c) */ - /* all the case statements fall through */ - switch (length) { - case 12: c += (u32)k[11]<<24; - case 11: c += (u32)k[10]<<16; - case 10: c += (u32)k[9]<<8; - case 9 : c += k[8]; - case 8 : b += (u32)k[7]<<24; - case 7 : b += (u32)k[6]<<16; - case 6 : b += (u32)k[5]<<8; - case 5 : b += k[4]; - case 4 : a += (u32)k[3]<<24; - case 3 : a += (u32)k[2]<<16; - case 2 : a += (u32)k[1]<<8; - case 1 : a += k[0]; - __jhash_final(a, b, c); - case 0 : - break; - } - - return c; -} +#define JHASH_INITVAL 0xdeadbeef -/* A special optimized version that handles 1 or more of u32s. - * The length parameter here is the number of u32s in the key. +/* jhash2 - hash an array of u32's + * @k: the key which must be an array of u32's + * @length: the number of u32's in the key + * @initval: the previous hash, or an arbitray value + * + * Returns the hash value of the key. */ static inline u32 jhash2(const u32 *k, u32 length, u32 initval) { u32 a, b, c; /* Set up the internal state */ - a = b = c = JHASH_RANDOM_PARAM + (length<<2) + initval; + a = b = c = JHASH_INITVAL + (length<<2) + initval; - /* handle most of the key */ + /* Handle most of the key */ while (length > 3) { a += k[0]; b += k[1]; c += k[2]; - __jhash_mix(a, b, c); + __jhash_mix(a,b,c); length -= 3; k += 3; } - - /* handle the last 3 u32's */ - /* all the case statements fall through */ - switch (length) { + + /* Handle the last 3 u32's: all the case statements fall through */ + switch(length) { case 3: c += k[2]; case 2: b += k[1]; case 1: a += k[0]; - __jhash_final(a, b, c); - case 0: /* case 0: nothing left to add */ + __jhash_final(a,b,c); + case 0: /* Nothing left to add */ break; } return c; } -/* A special ultra-optimized versions that knows they are hashing exactly - * 3, 2 or 1 word(s). - */ +/* jhash_3words - hash exactly 3, 2 or 1 word(s) */ static inline u32 jhash_3words(u32 a, u32 b, u32 c, u32 initval) { - a += JHASH_RANDOM_PARAM + initval; - b += JHASH_RANDOM_PARAM + initval; - c += JHASH_RANDOM_PARAM + initval; - - __jhash_final(a, b, c); + a += JHASH_INITVAL; + b += JHASH_INITVAL; + c += initval; + __jhash_final(a,b,c); + return c; } - static inline u32 jhash_2words(u32 a, u32 b, u32 initval) { - return jhash_3words(0, a, b, initval); + return jhash_3words(a, b, 0, initval); } static inline u32 jhash_1word(u32 a, u32 initval) { - return jhash_3words(0, 0, a, initval); + return jhash_3words(a, 0, 0, initval); } +#endif /* __KERNEL__ */ + #endif /* _LINUX_JHASH_H */ diff --git a/kernel/include/linux/netfilter/ip_set_kernel.h b/kernel/include/linux/netfilter/ip_set_kernel.h new file mode 100644 index 0000000..d6e033b --- /dev/null +++ b/kernel/include/linux/netfilter/ip_set_kernel.h @@ -0,0 +1,20 @@ +#ifndef _IP_SET_KERNEL_H +#define _IP_SET_KERNEL_H + +/* Copyright (C) 2003-2010 Jozsef Kadlecsik + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#ifdef __KERNEL__ + +/* Complete debug messages */ +#define pr_fmt(fmt) "%s %s[%i]: " fmt "\n", __FILE__, __func__, __LINE__ + +#include + +#endif /* __KERNEL__ */ + +#endif /*_IP_SET_H */ diff --git a/kernel/include/linux/netfilter/ip_set_list.h b/kernel/include/linux/netfilter/ip_set_list.h new file mode 100644 index 0000000..c40643e --- /dev/null +++ b/kernel/include/linux/netfilter/ip_set_list.h @@ -0,0 +1,21 @@ +#ifndef __IP_SET_LIST_H +#define __IP_SET_LIST_H + +/* List type specific error codes */ +enum { + IPSET_ERR_NAME = IPSET_ERR_TYPE_SPECIFIC, + IPSET_ERR_LOOP, + IPSET_ERR_BEFORE, + IPSET_ERR_NAMEREF, + IPSET_ERR_LIST_FULL, + IPSET_ERR_REF_EXIST, +}; + +#ifdef __KERNEL__ + +#define IP_SET_LIST_DEFAULT_SIZE 8 +#define IP_SET_LIST_MIN_SIZE 4 + +#endif /* __KERNEL__ */ + +#endif /* __IP_SET_LIST_H */ diff --git a/kernel/include/linux/netfilter/ip_set_slist.h b/kernel/include/linux/netfilter/ip_set_slist.h new file mode 100644 index 0000000..abc5afe --- /dev/null +++ b/kernel/include/linux/netfilter/ip_set_slist.h @@ -0,0 +1,86 @@ +#ifndef _IP_SET_SLIST_H +#define _IP_SET_SLIST_H + +#include +#include +#include + +/* + * Single linked lists with a single pointer. + * Mostly useful for hash tables where the two pointer list head + * and list node is too wasteful. + */ + +struct slist { + struct slist *next; +}; + +#define SLIST(name) struct slist name = { .next = NULL } +#define INIT_SLIST(ptr) ((ptr)->next = NULL) + +#define slist_entry(ptr, type, member) container_of(ptr,type,member) + +#define slist_for_each(pos, head) \ + for (pos = (head)->next; pos && ({ prefetch(pos->next); 1; }); \ + pos = pos->next) + +#define slist_for_each_prev(prev, pos, head) \ + for (prev = head, pos = (head)->next; pos && ({ prefetch(pos->next); 1; }); \ + prev = pos, pos = pos->next) + +#define slist_for_each_safe(pos, n, head) \ + for (pos = (head)->next; pos && ({ n = pos->next; 1; }); \ + pos = n) + +/** + * slist_for_each_entry - iterate over list of given type + * @tpos: the type * to use as a loop cursor. + * @pos: the &struct slist to use as a loop cursor. + * @head: the head for your list. + * @member: the name of the slist within the struct. + */ +#define slist_for_each_entry(tpos, pos, head, member) \ + for (pos = (head)->next; \ + pos && ({ prefetch(pos->next); 1;}) && \ + ({ tpos = slist_entry(pos, typeof(*tpos), member); 1;}); \ + pos = pos->next) + +/** + * slist_for_each_entry_continue - iterate over a hlist continuing after current point + * @tpos: the type * to use as a loop cursor. + * @pos: the &struct slist to use as a loop cursor. + * @member: the name of the slist within the struct. + */ +#define slist_for_each_entry_continue(tpos, pos, member) \ + for (pos = (pos)->next; \ + pos && ({ prefetch(pos->next); 1;}) && \ + ({ tpos = slist_entry(pos, typeof(*tpos), member); 1;}); \ + pos = pos->next) + +/** + * slist_for_each_entry_from - iterate over a hlist continuing from current point + * @tpos: the type * to use as a loop cursor. + * @pos: the &struct slist to use as a loop cursor. + * @member: the name of the slist within the struct. + */ +#define slist_for_each_entry_from(tpos, pos, member) \ + for (; pos && ({ prefetch(pos->next); 1;}) && \ + ({ tpos = slist_entry(pos, typeof(*tpos), member); 1;}); \ + pos = pos->next) + +/** + * slist_for_each_entry_safe - iterate over list of given type safe against + * removal of list entry + * @tpos: the type * to use as a loop cursor. + * @pos: the &struct slist to use as a loop cursor. + * @n: another &struct slist to use as temporary storage + * @head: the head for your list. + * @member: the name of the slist within the struct. + */ +#define slist_for_each_entry_safe(tpos, pos, n, head, member) \ + for (pos = (head)->next; \ + pos && ({ n = pos->next; 1; }) && \ + ({ tpos = slist_entry(pos, typeof(*tpos), member); 1;}); \ + pos = n) + +#endif /* _IP_SET_SLIST_H */ diff --git a/kernel/include/linux/netfilter/ip_set_timeout.h b/kernel/include/linux/netfilter/ip_set_timeout.h index da18875..bf1cbf6 100644 --- a/kernel/include/linux/netfilter/ip_set_timeout.h +++ b/kernel/include/linux/netfilter/ip_set_timeout.h @@ -10,21 +10,33 @@ #ifdef __KERNEL__ -/* How often should the gc be run at a minimum */ +/* How often should the gc be run by default */ #define IPSET_GC_TIME (3 * 60) /* Timeout period depending on the timeout value of the given set */ #define IPSET_GC_PERIOD(timeout) \ - max_t(uint32_t, (timeout)/10, IPSET_GC_TIME) + ((timeout/3) ? min_t(u32, (timeout)/3, IPSET_GC_TIME) : 1) -/* How much msec to sleep before retrying to destroy gc timer */ -#define IPSET_DESTROY_TIMER_SLEEP 10 +/* Set is defined without timeout support */ +#define IPSET_NO_TIMEOUT UINT_MAX -/* Timing out etries: unset and permanent */ +#define with_timeout(timeout) ((timeout) != IPSET_NO_TIMEOUT) + +static inline unsigned int +ip_set_timeout_uget(struct nlattr *tb) +{ + unsigned int timeout = ip_set_get_h32(tb); + + return timeout == IPSET_NO_TIMEOUT ? IPSET_NO_TIMEOUT - 1 : timeout; +} + +#ifdef IP_SET_BITMAP_TIMEOUT + +/* Bitmap entry is unset */ #define IPSET_ELEM_UNSET 0 +/* Bitmap entry is set with no timeout value */ #define IPSET_ELEM_PERMANENT UINT_MAX/2 -#ifdef IP_SET_BITMAP_TIMEOUT static inline bool ip_set_timeout_test(unsigned long timeout) { @@ -42,7 +54,7 @@ ip_set_timeout_expired(unsigned long timeout) } static inline unsigned long -ip_set_timeout_set(uint32_t timeout) +ip_set_timeout_set(u32 timeout) { unsigned long t; @@ -56,7 +68,7 @@ ip_set_timeout_set(uint32_t timeout) return t; } -static inline uint32_t +static inline u32 ip_set_timeout_get(unsigned long timeout) { return timeout == IPSET_ELEM_PERMANENT ? 0 : (timeout - jiffies)/HZ; @@ -64,6 +76,9 @@ ip_set_timeout_get(unsigned long timeout) #else +/* Hash entry is set with no timeout value */ +#define IPSET_ELEM_UNSET 0 + static inline bool ip_set_timeout_test(unsigned long timeout) { @@ -77,7 +92,7 @@ ip_set_timeout_expired(unsigned long timeout) } static inline unsigned long -ip_set_timeout_set(uint32_t timeout) +ip_set_timeout_set(u32 timeout) { unsigned long t; @@ -91,7 +106,7 @@ ip_set_timeout_set(uint32_t timeout) return t; } -static inline uint32_t +static inline u32 ip_set_timeout_get(unsigned long timeout) { return timeout == IPSET_ELEM_UNSET ? 0 : (timeout - jiffies)/HZ; diff --git a/kernel/include/linux/netfilter/ipt_set.h b/kernel/include/linux/netfilter/ipt_set.h deleted file mode 100644 index 2a18b93..0000000 --- a/kernel/include/linux/netfilter/ipt_set.h +++ /dev/null @@ -1,21 +0,0 @@ -#ifndef _IPT_SET_H -#define _IPT_SET_H - -#include - -struct ipt_set_info { - ip_set_id_t index; - u_int32_t flags[IP_SET_MAX_BINDINGS + 1]; -}; - -/* match info */ -struct ipt_set_info_match { - struct ipt_set_info match_set; -}; - -struct ipt_set_info_target { - struct ipt_set_info add_set; - struct ipt_set_info del_set; -}; - -#endif /*_IPT_SET_H*/ diff --git a/kernel/include/linux/netfilter/xt_set.h b/kernel/include/linux/netfilter/xt_set.h new file mode 100644 index 0000000..949fa59 --- /dev/null +++ b/kernel/include/linux/netfilter/xt_set.h @@ -0,0 +1,55 @@ +#ifndef _XT_SET_H +#define _XT_SET_H + +#include + +/* Revision 0 interface: backward compatible with netfilter/iptables */ + +/* + * Option flags for kernel operations (xt_set_info_v0) + */ +#define IPSET_SRC 0x01 /* Source match/add */ +#define IPSET_DST 0x02 /* Destination match/add */ +#define IPSET_MATCH_INV 0x04 /* Inverse matching */ + +struct xt_set_info_v0 { + ip_set_id_t index; + union { + u_int32_t flags[IPSET_DIM_MAX + 1]; + struct { + u_int32_t __flags[IPSET_DIM_MAX]; + u_int8_t dim; + u_int8_t flags; + } compat; + } u; +}; + +/* match and target infos */ +struct xt_set_info_match_v0 { + struct xt_set_info_v0 match_set; +}; + +struct xt_set_info_target_v0 { + struct xt_set_info_v0 add_set; + struct xt_set_info_v0 del_set; +}; + +/* Revision 1: current interface to netfilter/iptables */ + +struct xt_set_info { + ip_set_id_t index; + u_int8_t dim; + u_int8_t flags; +}; + +/* match and target infos */ +struct xt_set_info_match { + struct xt_set_info match_set; +}; + +struct xt_set_info_target { + struct xt_set_info add_set; + struct xt_set_info del_set; +}; + +#endif /*_XT_SET_H*/ diff --git a/kernel/ip_set.c b/kernel/ip_set.c index 3af8fce..5bf331e 100644 --- a/kernel/ip_set.c +++ b/kernel/ip_set.c @@ -9,25 +9,26 @@ /* Kernel module for IP set management */ +#include #include #include #include -#include #include #include #include #include +#include #include #include #include #include -#include -static struct list_head ip_set_type_list; /* all registered sets */ +static struct list_head ip_set_type_list; /* all registered set types */ +static DEFINE_MUTEX(ip_set_type_mutex); /* protects ip_set_type_list */ + static struct ip_set **ip_set_list; /* all individual sets */ -static DEFINE_MUTEX(ip_set_type_mutex); /* protects ip_set_type_lists */ -static ip_set_id_t ip_set_max = CONFIG_IP_SET_MAX; +static ip_set_id_t ip_set_max = CONFIG_IP_SET_MAX; /* max number of sets */ #define STREQ(a,b) (strncmp(a,b,IPSET_MAXNAMELEN) == 0) @@ -43,31 +44,146 @@ MODULE_ALIAS_NFNL_SUBSYS(NFNL_SUBSYS_IPSET); /* * The set types are implemented in modules and registered set types * can be found in ip_set_type_list. Adding/deleting types is - * serialized by ip_set_type_list_lock/ip_set_type_list_unlock. + * serialized by ip_set_type_mutex. */ static inline void -ip_set_type_list_lock(void) +ip_set_type_lock(void) { mutex_lock(&ip_set_type_mutex); } static inline void -ip_set_type_list_unlock(void) +ip_set_type_unlock(void) { mutex_unlock(&ip_set_type_mutex); } +/* Register and deregister settype */ + +static inline struct ip_set_type * +find_set_type(const char *name, u8 family, u8 revision) +{ + struct ip_set_type *type; + + list_for_each_entry_rcu(type, &ip_set_type_list, list) + if (STREQ(type->name, name) + && (type->family == family || type->family == AF_UNSPEC) + && type->revision == revision) + return type; + return NULL; +} + +/* Find a set type so that rcu_read_lock() is called by the function. + * If we succeeded, the RCU lock is NOT released and the caller + * must release it later. + */ +static struct ip_set_type * +find_set_type_rcu(const char *name, u8 family, u8 revision) +{ + struct ip_set_type *type; + + rcu_read_lock(); + type = find_set_type(name, family, revision); + if (type == NULL) + rcu_read_unlock(); + + return type; +} + +/* Find a given set type by name and family together + * with the supported minimal and maximum revisions. + */ +static bool +find_set_type_minmax(const char *name, u8 family, + u8 *min, u8 *max) +{ + struct ip_set_type *type; + bool ret = false; + + *min = *max = 0; + rcu_read_lock(); + list_for_each_entry_rcu(type, &ip_set_type_list, list) + if (STREQ(type->name, name) + && (type->family == family || type->family == AF_UNSPEC)) { + ret = true; + if (type->revision < *min) + *min = type->revision; + else if (type->revision > *max) + *max = type->revision; + } + rcu_read_unlock(); + + return ret; +} + +#define family_name(f) ((f) == AF_INET ? "inet" : \ + (f) == AF_INET6 ? "inet6" : "any") + +/* Register a set type structure. The type is identified by + * the unique triple of name, family and revision. + */ +int +ip_set_type_register(struct ip_set_type *type) +{ + int ret = 0; + + if (type->protocol != IPSET_PROTOCOL) { + pr_warning("ip_set type %s, family %s, revision %u uses " + "wrong protocol version %u (want %u)\n", + type->name, family_name(type->family), + type->revision, type->protocol, IPSET_PROTOCOL); + return -EINVAL; + } + + ip_set_type_lock(); + if (find_set_type(type->name, type->family, type->revision)) { + /* Duplicate! */ + pr_warning("ip_set type %s, family %s, revision %u " + "already registered!\n", type->name, + family_name(type->family), type->revision); + ret = -EINVAL; + goto unlock; + } + list_add_rcu(&type->list, &ip_set_type_list); + pr_debug("type %s, family %s, revision %u registered.", + type->name, family_name(type->family), type->revision); +unlock: + ip_set_type_unlock(); + return ret; +} +EXPORT_SYMBOL(ip_set_type_register); + +/* Unregister a set type. There's a small race with ip_set_create */ +void +ip_set_type_unregister(struct ip_set_type *type) +{ + ip_set_type_lock(); + if (!find_set_type(type->name, type->family, type->revision)) { + pr_warning("ip_set type %s, family %s, revision %u " + "not registered\n", type->name, + family_name(type->family), type->revision); + goto unlock; + } + list_del_rcu(&type->list); + pr_debug("type %s, family %s, revision %u unregistered.", + type->name, family_name(type->family), type->revision); +unlock: + ip_set_type_unlock(); + + synchronize_rcu(); +} +EXPORT_SYMBOL(ip_set_type_unregister); + /* * Creating/destroying/renaming/swapping affect the existence and - * integrity of a set. All of these can be executed from userspace only - * and serialized by nfnl_lock/nfnl_unlock indirectly from nfnetlink. + * the properties of a set. All of these can be executed from userspace + * only and serialized by the nfnl mutex indirectly from nfnetlink. * * Sets are identified by their index in ip_set_list and the index * is used by the external references (set/SET netfilter modules). * - * The set behind an index may change by swapping. - * + * The set behind an index may change by swapping only, from userspace. */ static inline void @@ -82,264 +198,306 @@ __ip_set_put(ip_set_id_t index) atomic_dec(&ip_set_list[index]->ref); } -/* Add, del and test set entries from kernel */ +/* + * Add, del and test set entries from kernel. + * + * The set behind the index must exist and must be referenced + * so it can't be destroyed (or changed) under our foot. + */ int ip_set_test(ip_set_id_t index, const struct sk_buff *skb, - uint8_t family, const uint8_t *flags) + u8 family, u8 dim, u8 flags) { - struct ip_set *set; + struct ip_set *set = ip_set_list[index]; int ret = 0; - rcu_read_lock(); - set = rcu_dereference(ip_set_list[index]); - D("set %s, index %u", set->name, index); + BUG_ON(set == NULL || atomic_read(&set->ref) == 0); + pr_debug("set %s, index %u", set->name, index); + + if (dim < set->type->dimension + || !(family == set->family || set->family == AF_UNSPEC)) + return 0; read_lock_bh(&set->lock); - ret = set->variant->kadt(set, skb, IPSET_TEST, family, flags); + ret = set->variant->kadt(set, skb, IPSET_TEST, family, dim, flags); read_unlock_bh(&set->lock); if (ret == -EAGAIN) { /* Type requests element to be re-added */ + pr_debug("element must be competed, ADD is triggered"); write_lock_bh(&set->lock); - set->variant->kadt(set, skb, IPSET_ADD, family, flags); + set->variant->kadt(set, skb, IPSET_ADD, family, dim, flags); write_unlock_bh(&set->lock); ret = 1; } - rcu_read_unlock(); - + /* Convert error codes to nomatch */ return (ret < 0 ? 0 : ret); } +EXPORT_SYMBOL(ip_set_test); int ip_set_add(ip_set_id_t index, const struct sk_buff *skb, - uint8_t family, const uint8_t *flags) + u8 family, u8 dim, u8 flags) { - struct ip_set *set; + struct ip_set *set = ip_set_list[index]; int ret = 0, retried = 0; -retry: - rcu_read_lock(); - set = rcu_dereference(ip_set_list[index]); - D("set %s, index %u", set->name, index); + BUG_ON(set == NULL || atomic_read(&set->ref) == 0); + pr_debug("set %s, index %u", set->name, index); + if (dim < set->type->dimension + || !(family == set->family || set->family == AF_UNSPEC)) + return 0; + +retry: write_lock_bh(&set->lock); - ret = set->variant->kadt(set, skb, IPSET_ADD, family, flags); + ret = set->variant->kadt(set, skb, IPSET_ADD, family, dim, flags); write_unlock_bh(&set->lock); - rcu_read_unlock(); /* Retry function must be called without holding any lock */ if (ret == -EAGAIN && set->variant->resize - && (ret = set->variant->resize(set, retried++)) == 0) + && (ret = set->variant->resize(set, GFP_ATOMIC, retried++)) == 0) goto retry; return ret; } +EXPORT_SYMBOL(ip_set_add); int ip_set_del(ip_set_id_t index, const struct sk_buff *skb, - uint8_t family, const uint8_t *flags) + u8 family, u8 dim, u8 flags) { - struct ip_set *set; + struct ip_set *set = ip_set_list[index]; int ret = 0; - rcu_read_lock(); - set = rcu_dereference(ip_set_list[index]); - D("set %s, index %u", set->name, index); + BUG_ON(set == NULL || atomic_read(&set->ref) == 0); + pr_debug("set %s, index %u", set->name, index); + + if (dim < set->type->dimension + || !(family == set->family || set->family == AF_UNSPEC)) + return 0; write_lock_bh(&set->lock); - ret = set->variant->kadt(set, skb, IPSET_DEL, family, flags); + ret = set->variant->kadt(set, skb, IPSET_DEL, family, dim, flags); write_unlock_bh(&set->lock); - - rcu_read_unlock(); return ret; } +EXPORT_SYMBOL(ip_set_del); -/* Register and deregister settype */ - -#define family_name(f) ((f) == AF_INET ? "inet" : \ - (f) == AF_INET6 ? "inet6" : "any") - -static inline struct ip_set_type * -find_set_type(const char *name, uint8_t family, uint8_t revision) +/* + * Find set by name, reference it once. The reference makes sure the + * thing pointed to, does not go away under our feet. + * + * The nfnl mutex must already be activated. + */ +ip_set_id_t +ip_set_get_byname(const char *name, struct ip_set **set) { - struct ip_set_type *type; - - list_for_each_entry(type, &ip_set_type_list, list) - if (STREQ(type->name, name) - && (type->family == family || type->family == AF_UNSPEC) - && type->revision == revision) - return type; - return NULL; -} + ip_set_id_t i, index = IPSET_INVALID_ID; + struct ip_set *s; -int -ip_set_type_register(struct ip_set_type *type) -{ - int ret = 0; - - if (type->protocol != IPSET_PROTOCOL) { - printk("set type %s, family %s, revision %u uses " - "wrong protocol version %u (want %u)\n", - type->name, family_name(type->family), type->revision, - type->protocol, IPSET_PROTOCOL); - return -EINVAL; + for (i = 0; i < ip_set_max; i++) { + s = ip_set_list[i]; + if (s != NULL && STREQ(s->name, name)) { + __ip_set_get(i); + index = i; + *set = s; + } } - ip_set_type_list_lock(); - if (find_set_type(type->name, type->family, type->revision)) { - /* Duplicate! */ - printk("type %s, family %s, revision %u already registered!\n", - type->name, family_name(type->family), type->revision); - ret = -EINVAL; - goto unlock; - } - list_add(&type->list, &ip_set_type_list); - D("type %s, family %s, revision %u registered.", - type->name, family_name(type->family), type->revision); -unlock: - ip_set_type_list_unlock(); - return ret; + return index; } +EXPORT_SYMBOL(ip_set_get_byname); +/* + * If the given set pointer points to a valid set, decrement + * reference count by 1. The caller shall not assume the index + * to be valid, after calling this function. + * + * The nfnl mutex must already be activated. + */ void -ip_set_type_unregister(struct ip_set_type *type) +ip_set_put_byindex(ip_set_id_t index) { - ip_set_type_list_lock(); - if (!find_set_type(type->name, type->family, type->revision)) { - printk("type %s, family %s, revision %u not registered\n", - type->name, family_name(type->family), type->revision); - goto unlock; + if (ip_set_list[index] != NULL) { + BUG_ON(atomic_read(&ip_set_list[index]->ref) == 0); + __ip_set_put(index); } - list_del(&type->list); - D("type %s, family %s, revision %u unregistered.", - type->name, family_name(type->family), type->revision); -unlock: - ip_set_type_list_unlock(); } +EXPORT_SYMBOL(ip_set_put_byindex); + +/* + * Get the name of a set behind a set index. + * We assume the set is referenced, so it does exist and + * can't be destroyed. The set cannot be renamed due to + * the referencing either. + * + * The nfnl mutex must already be activated. + */ +const char * +ip_set_name_byindex(ip_set_id_t index) +{ + struct ip_set *set = ip_set_list[index]; + + BUG_ON(set == NULL); + BUG_ON(atomic_read(&set->ref) == 0); + + /* Referenced, so it's safe */ + return set->name; +} +EXPORT_SYMBOL(ip_set_name_byindex); -/* Get/put a set with referencing */ +/* + * Routines to call by external subsystems, which do not + * call nfnl_lock for us. + */ /* * Find set by name, reference it once. The reference makes sure the - * thing pointed to, does not go away under our feet. Drop the reference - * later, using ip_set_put*(). + * thing pointed to, does not go away under our feet. + * + * The nfnl mutex is used in the function. */ ip_set_id_t -ip_set_get_byname(const char *name) +ip_set_nfnl_get(const char *name) { - ip_set_id_t i, index = IPSET_INVALID_ID; - - nfnl_lock(); - for (i = 0; index == IPSET_INVALID_ID && i < ip_set_max; i++) - if (STREQ(ip_set_list[i]->name, name)) { - __ip_set_get(i); - index = i; - } + struct ip_set *s; + ip_set_id_t index; + + nfnl_lock(); + index = ip_set_get_byname(name, &s); + nfnl_unlock(); + + return index; +} +EXPORT_SYMBOL(ip_set_nfnl_get); + +/* + * Find set by index, reference it once. The reference makes sure the + * thing pointed to, does not go away under our feet. + * + * The nfnl mutex is used in the function. + */ +ip_set_id_t +ip_set_nfnl_get_byindex(ip_set_id_t index) +{ + if (index > ip_set_max) + return IPSET_INVALID_ID; + + nfnl_lock(); + if (ip_set_list[index]) + __ip_set_get(index); + else + index = IPSET_INVALID_ID; nfnl_unlock(); return index; } +EXPORT_SYMBOL(ip_set_nfnl_get_byindex); /* * If the given set pointer points to a valid set, decrement * reference count by 1. The caller shall not assume the index * to be valid, after calling this function. + * + * The nfnl mutex is used in the function. */ void -ip_set_put_byindex(ip_set_id_t index) +ip_set_nfnl_put(ip_set_id_t index) { nfnl_lock(); - if (ip_set_list[index]) + if (ip_set_list[index] != NULL) { + BUG_ON(atomic_read(&ip_set_list[index]->ref) == 0); __ip_set_put(index); + } nfnl_unlock(); } +EXPORT_SYMBOL(ip_set_nfnl_put); -static ip_set_id_t -find_set_id(const char *name) +/* + * Communication protocol with userspace over netlink. + * + * We already locked by nfnl_lock. + */ + +static inline bool +protocol_failed(const struct nlattr * const tb[]) { - ip_set_id_t i, index = IPSET_INVALID_ID; - - for (i = 0; index == IPSET_INVALID_ID && i < ip_set_max; i++) { - if (ip_set_list[i] != NULL - && STREQ(ip_set_list[i]->name, name)) - index = i; - } - return index; + return !tb[IPSET_ATTR_PROTOCOL] + || nla_get_u8(tb[IPSET_ATTR_PROTOCOL]) != IPSET_PROTOCOL; } -static ip_set_id_t -find_set_id_rcu(const char *name) +static inline u32 +flag_exist(const struct nlmsghdr *nlh) { - ip_set_id_t i, index = IPSET_INVALID_ID; - struct ip_set *set; - - for (i = 0; index == IPSET_INVALID_ID && i < ip_set_max; i++) { - set = rcu_dereference(ip_set_list[i]); - if (set != NULL && STREQ(set->name, name)) - index = i; - } - return index; + return nlh->nlmsg_flags & NLM_F_EXCL ? 0 : IPSET_FLAG_EXIST; } -static struct ip_set * -find_set(const char *name) +static inline bool +flag_nested(const struct nlattr *nla) { - ip_set_id_t index = find_set_id(name); - - return index == IPSET_INVALID_ID ? NULL : ip_set_list[index]; + return nla->nla_type & NLA_F_NESTED; } -/* Communication protocol with userspace over netlink */ +static struct nlmsghdr * +start_msg(struct sk_buff *skb, u32 pid, u32 seq, unsigned int flags, + enum ipset_cmd cmd) +{ + struct nlmsghdr *nlh; + struct nfgenmsg *nfmsg; + + nlh = nlmsg_put(skb, pid, seq, cmd | (NFNL_SUBSYS_IPSET << 8), + sizeof(*nfmsg), flags); + if (nlh == NULL) + return NULL; + + nfmsg = nlmsg_data(nlh); + nfmsg->nfgen_family = AF_INET; + nfmsg->version = NFNETLINK_V0; + nfmsg->res_id = 0; + + return nlh; +} /* Create a set */ static const struct nla_policy ip_set_create_policy[IPSET_ATTR_CMD_MAX + 1] __read_mostly = { [IPSET_ATTR_PROTOCOL] = { .type = NLA_U8 }, - [IPSET_ATTR_SETNAME] = { .type = NLA_STRING, - .len = IPSET_MAXNAMELEN }, - [IPSET_ATTR_TYPENAME] = { .type = NLA_STRING, - .len = IPSET_MAXNAMELEN }, + [IPSET_ATTR_SETNAME] = { .type = NLA_NUL_STRING, + .len = IPSET_MAXNAMELEN -1 }, + [IPSET_ATTR_TYPENAME] = { .type = NLA_NUL_STRING, + .len = IPSET_MAXNAMELEN - 1}, [IPSET_ATTR_REVISION] = { .type = NLA_U8 }, [IPSET_ATTR_FAMILY] = { .type = NLA_U8 }, [IPSET_ATTR_LINENO] = { .type = NLA_U32 }, [IPSET_ATTR_DATA] = { .type = NLA_NESTED }, }; -static inline bool -protocol_failed(const struct nlattr * const tb[]) -{ - return !tb[IPSET_ATTR_PROTOCOL] - || nla_get_u8(tb[IPSET_ATTR_PROTOCOL]) != IPSET_PROTOCOL; -} - -static inline uint32_t -flag_exist(const struct nlmsghdr *nlh) -{ - return nlh->nlmsg_flags & NLM_F_EXCL ? 0 : IPSET_FLAG_EXIST; -} - -static inline bool -flag_nested(const struct nlattr *nla) +static ip_set_id_t +find_set_id(const char *name) { - return nla->nla_type & NLA_F_NESTED; + ip_set_id_t i, index = IPSET_INVALID_ID; + struct ip_set *set; + + for (i = 0; index == IPSET_INVALID_ID && i < ip_set_max; i++) { + set = ip_set_list[i]; + if (set != NULL && STREQ(set->name, name)) + index = i; + } + return index; } -static struct ip_set_type * -find_set_type_lock(const char *name, uint8_t family, uint8_t revision) +static inline struct ip_set * +find_set(const char *name) { - struct ip_set_type *type; - - ip_set_type_list_lock(); - type = find_set_type(name, family, revision); - if (type == NULL) - ip_set_type_list_unlock(); + ip_set_id_t index = find_set_id(name); - return type; + return index == IPSET_INVALID_ID ? NULL : ip_set_list[index]; } static int @@ -364,30 +522,10 @@ find_free_id(const char *name, ip_set_id_t *index, struct ip_set **set) return 0; } -static struct nlmsghdr * -start_msg(struct sk_buff *skb, u32 pid, u32 seq, unsigned int flags, - enum ipset_cmd cmd) -{ - struct nlmsghdr *nlh; - struct nfgenmsg *nfmsg; - - nlh = nlmsg_put(skb, pid, seq, cmd | (NFNL_SUBSYS_IPSET << 8), - sizeof(*nfmsg), flags); - if (nlh == NULL) - return NULL; - - nfmsg = nlmsg_data(nlh); - nfmsg->nfgen_family = AF_INET; - nfmsg->version = NFNETLINK_V0; - nfmsg->res_id = 0; - - return nlh; -} - static inline void load_type_module(const char *typename) { - D("try to load ip_set_%s", typename); + pr_debug("try to load ip_set_%s", typename); request_module("ip_set_%s", typename); } @@ -399,8 +537,8 @@ ip_set_create(struct sock *ctnl, struct sk_buff *skb, struct ip_set *set, *clash; ip_set_id_t index = IPSET_INVALID_ID; const char *name, *typename; - uint8_t family, revision; - uint32_t flags = flag_exist(nlh); + u8 family, revision; + u32 flags = flag_exist(nlh); int ret = 0, len; if (unlikely(protocol_failed(attr) @@ -416,8 +554,8 @@ ip_set_create(struct sock *ctnl, struct sk_buff *skb, typename = nla_data(attr[IPSET_ATTR_TYPENAME]); family = nla_get_u8(attr[IPSET_ATTR_FAMILY]); revision = nla_get_u8(attr[IPSET_ATTR_REVISION]); - D("setname: %s, typename: %s, family: %s, revision: %u", - name, typename, family_name(family), revision); + pr_debug("setname: %s, typename: %s, family: %s, revision: %u", + name, typename, family_name(family), revision); /* * First, and without any locks, allocate and initialize @@ -429,6 +567,7 @@ ip_set_create(struct sock *ctnl, struct sk_buff *skb, rwlock_init(&set->lock); strncpy(set->name, name, IPSET_MAXNAMELEN); atomic_set(&set->ref, 0); + set->family = family; /* * Next, check that we know the type, and take @@ -438,31 +577,32 @@ ip_set_create(struct sock *ctnl, struct sk_buff *skb, * After referencing the type, we try to create the type * specific part of the set without holding any locks. */ - set->type = find_set_type_lock(typename, family, revision); + set->type = find_set_type_rcu(typename, family, revision); if (set->type == NULL) { /* Try loading the module */ load_type_module(typename); - set->type = find_set_type_lock(typename, family, revision); + set->type = find_set_type_rcu(typename, family, revision); if (set->type == NULL) { - printk("Can't find type %s, family %s, revision %u:" - " set '%s' not created", - typename, family_name(family), revision, name); + pr_warning("Can't find ip_set type %s, family %s, " + "revision %u: set '%s' not created", + typename, family_name(family), revision, + name); ret = -IPSET_ERR_FIND_TYPE; goto out; } } if (!try_module_get(set->type->me)) { - ip_set_type_list_unlock(); + rcu_read_unlock(); ret = -EFAULT; goto out; } - ip_set_type_list_unlock(); + rcu_read_unlock(); /* * Without holding any locks, create private part. */ len = attr[IPSET_ATTR_DATA] ? nla_len(attr[IPSET_ATTR_DATA]) : 0; - D("data len: %u", len); + pr_debug("data len: %u", len); ret = set->type->create(set, attr[IPSET_ATTR_DATA] ? nla_data(attr[IPSET_ATTR_DATA]) : NULL, len, flags); @@ -482,7 +622,8 @@ ip_set_create(struct sock *ctnl, struct sk_buff *skb, && (flags & IPSET_FLAG_EXIST) && STREQ(set->type->name, clash->type->name) && set->type->family == clash->type->family - && set->type->revision == clash->type->revision) + && set->type->revision == clash->type->revision + && set->variant->same_set(set, clash)) ret = 0; goto cleanup; } @@ -490,7 +631,7 @@ ip_set_create(struct sock *ctnl, struct sk_buff *skb, /* * Finally! Add our shiny new set to the list, and be done. */ - D("create: '%s' created with index %u!", set->name, index); + pr_debug("create: '%s' created with index %u!", set->name, index); ip_set_list[index] = set; return ret; @@ -509,8 +650,8 @@ out: static const struct nla_policy ip_set_setname_policy[IPSET_ATTR_CMD_MAX + 1] __read_mostly = { [IPSET_ATTR_PROTOCOL] = { .type = NLA_U8 }, - [IPSET_ATTR_SETNAME] = { .type = NLA_STRING, - .len = IPSET_MAXNAMELEN }, + [IPSET_ATTR_SETNAME] = { .type = NLA_NUL_STRING, + .len = IPSET_MAXNAMELEN - 1 }, }; static inline void @@ -518,7 +659,7 @@ ip_set_destroy_set(ip_set_id_t index) { struct ip_set *set = ip_set_list[index]; - D("set: %s", set->name); + pr_debug("set: %s", set->name); ip_set_list[index] = NULL; /* Must call it without holding any lock */ @@ -565,7 +706,7 @@ ip_set_destroy(struct sock *ctnl, struct sk_buff *skb, static inline void ip_set_flush_set(struct ip_set *set) { - D("set: %s", set->name); + pr_debug("set: %s", set->name); write_lock_bh(&set->lock); set->variant->flush(set); @@ -602,10 +743,10 @@ ip_set_flush(struct sock *ctnl, struct sk_buff *skb, static const struct nla_policy ip_set_setname2_policy[IPSET_ATTR_CMD_MAX + 1] __read_mostly = { [IPSET_ATTR_PROTOCOL] = { .type = NLA_U8 }, - [IPSET_ATTR_SETNAME] = { .type = NLA_STRING, - .len = IPSET_MAXNAMELEN }, - [IPSET_ATTR_SETNAME2] = { .type = NLA_STRING, - .len = IPSET_MAXNAMELEN }, + [IPSET_ATTR_SETNAME] = { .type = NLA_NUL_STRING, + .len = IPSET_MAXNAMELEN - 1 }, + [IPSET_ATTR_SETNAME2] = { .type = NLA_NUL_STRING, + .len = IPSET_MAXNAMELEN - 1 }, }; static int @@ -625,6 +766,8 @@ ip_set_rename(struct sock *ctnl, struct sk_buff *skb, set = find_set(nla_data(attr[IPSET_ATTR_SETNAME])); if (set == NULL) return -EEXIST; + if (atomic_read(&set->ref) != 0) + return -IPSET_ERR_REFERENCED; name2 = nla_data(attr[IPSET_ATTR_SETNAME2]); for (i = 0; i < ip_set_max; i++) { @@ -638,7 +781,13 @@ ip_set_rename(struct sock *ctnl, struct sk_buff *skb, } /* Swap two sets so that name/index points to the other. - * References are also swapped. */ + * References and set names are also swapped. + * + * We are protected by the nfnl mutex and references are + * manipulated only by holding the mutex. The kernel interfaces + * do not hold the mutex but the pointer settings are atomic + * so the ip_set_list always contains valid pointers to the sets. + */ static int ip_set_swap(struct sock *ctnl, struct sk_buff *skb, @@ -648,7 +797,7 @@ ip_set_swap(struct sock *ctnl, struct sk_buff *skb, struct ip_set *from, *to; ip_set_id_t from_id, to_id; char from_name[IPSET_MAXNAMELEN]; - uint32_t from_ref; + u32 from_ref; if (unlikely(protocol_failed(attr) || attr[IPSET_ATTR_SETNAME] == NULL @@ -673,7 +822,7 @@ ip_set_swap(struct sock *ctnl, struct sk_buff *skb, && from->type->family == to->type->family)) return -IPSET_ERR_TYPE_MISMATCH; - /* No magic here: ref munging protected by the mutex */ + /* No magic here: ref munging protected by the nfnl_lock */ strncpy(from_name, from->name, IPSET_MAXNAMELEN); from_ref = atomic_read(&from->ref); @@ -682,20 +831,29 @@ ip_set_swap(struct sock *ctnl, struct sk_buff *skb, strncpy(to->name, from_name, IPSET_MAXNAMELEN); atomic_set(&to->ref, from_ref); - rcu_assign_pointer(ip_set_list[from_id], to); - rcu_assign_pointer(ip_set_list[to_id], from); - synchronize_rcu(); + ip_set_list[from_id] = to; + ip_set_list[to_id] = from; + + /* Avoid possible race between ongoing slow add/del in kernel space + * and next destroy command. */ + synchronize_net(); return 0; } /* List/save set data */ +#define DUMP_ALL 0L +#define DUMP_ONE 1L +#define DUMP_LAST 2L + static int ip_set_dump_done(struct netlink_callback *cb) { - if (cb->args[2]) + if (cb->args[2]) { + pr_debug("release set %s", ip_set_list[cb->args[1]]->name); __ip_set_put((ip_set_id_t) cb->args[1]); + } return 0; } @@ -705,9 +863,9 @@ dump_attrs(struct nlmsghdr *nlh) struct nlattr *attr; int rem; - D("dump nlmsg"); + pr_debug("dump nlmsg"); nlmsg_for_each_attr(attr, nlh, sizeof(struct nfgenmsg), rem) { - D("type: %u, len %u", nla_type(attr), attr->nla_len); + pr_debug("type: %u, len %u", nla_type(attr), attr->nla_len); } } @@ -720,22 +878,32 @@ ip_set_dump_start(struct sk_buff *skb, struct netlink_callback *cb) unsigned int flags = NETLINK_CB(cb->skb).pid ? NLM_F_MULTI : 0; int ret = 0; - max = cb->args[0] ? cb->args[1] + 1 : ip_set_max; - rcu_read_lock(); + if (cb->args[1] >= ip_set_max) + goto out; + + pr_debug("args[0]: %ld args[1]: %ld\n", cb->args[0], cb->args[1]); + max = cb->args[0] == DUMP_ONE ? cb->args[1] + 1 : ip_set_max; for (; cb->args[1] < max; cb->args[1]++) { index = (ip_set_id_t) cb->args[1]; - set = rcu_dereference(ip_set_list[index]); + set = ip_set_list[index]; if (set == NULL) { - if (cb->args[0]) { + if (cb->args[0] == DUMP_ONE) { ret = -EEXIST; - goto unlock; + goto out; } continue; } - D("List set: %s", set->name); + /* When dumping all sets, we must dump "sorted" + * so that lists (unions of sets) are dumped last. + */ + if (cb->args[0] != DUMP_ONE + && !((cb->args[0] == DUMP_ALL) + ^ (set->type->features & IPSET_DUMP_LAST))) + continue; + pr_debug("List set: %s", set->name); if (!cb->args[2]) { /* Start listing: make sure set won't be destroyed */ - D("reference set"); + pr_debug("reference set"); __ip_set_get(index); } nlh = start_msg(skb, NETLINK_CB(cb->skb).pid, @@ -753,7 +921,7 @@ ip_set_dump_start(struct sk_buff *skb, struct netlink_callback *cb) NLA_PUT_STRING(skb, IPSET_ATTR_TYPENAME, set->type->name); NLA_PUT_U8(skb, IPSET_ATTR_FAMILY, - set->type->family); + set->family); NLA_PUT_U8(skb, IPSET_ATTR_REVISION, set->type->revision); ret = set->variant->head(set, skb); @@ -764,28 +932,35 @@ ip_set_dump_start(struct sk_buff *skb, struct netlink_callback *cb) read_lock_bh(&set->lock); ret = set->variant->list(set, skb, cb); read_unlock_bh(&set->lock); - if (!cb->args[2]) + if (!cb->args[2]) { /* Set is done, proceed with next one */ - cb->args[1]++; + if (cb->args[0] == DUMP_ONE) + cb->args[1] = IPSET_INVALID_ID; + else + cb->args[1]++; + } goto release_refcount; } } - goto unlock; + goto out; nla_put_failure: ret = -EFAULT; release_refcount: /* If there was an error or set is done, release set */ if (ret || !cb->args[2]) { - D("release set"); + pr_debug("release set %s", ip_set_list[index]->name); __ip_set_put(index); } -unlock: - rcu_read_unlock(); + /* If we dump all sets, continue with dumping last ones */ + if (cb->args[0] == DUMP_ALL && cb->args[1] >= max && !cb->args[2]) + cb->args[0] = DUMP_LAST; + +out: if (nlh) { nlmsg_end(skb, nlh); - D("nlmsg_len: %u", nlh->nlmsg_len); + pr_debug("nlmsg_len: %u", nlh->nlmsg_len); dump_attrs(nlh); } @@ -807,23 +982,18 @@ ip_set_dump(struct sock *ctnl, struct sk_buff *skb, ip_set_dump_start, ip_set_dump_done); - rcu_read_lock(); - index = find_set_id_rcu(nla_data(attr[IPSET_ATTR_SETNAME])); - if (index == IPSET_INVALID_ID) { - rcu_read_unlock(); + index = find_set_id(nla_data(attr[IPSET_ATTR_SETNAME])); + if (index == IPSET_INVALID_ID) return -EEXIST; - } - rcu_read_unlock(); - /* cb->args[0] : 1 => dump single set, - * : 0 => dump all sets + /* cb->args[0] : dump single set/all sets * [1] : set index * [..]: type specific */ return netlink_dump_init(ctnl, skb, nlh, ip_set_dump_start, ip_set_dump_done, - 2, 1, index); + 2, DUMP_ONE, index); } /* Add, del and test */ @@ -831,8 +1001,8 @@ ip_set_dump(struct sock *ctnl, struct sk_buff *skb, static const struct nla_policy ip_set_adt_policy[IPSET_ATTR_CMD_MAX + 1] __read_mostly = { [IPSET_ATTR_PROTOCOL] = { .type = NLA_U8 }, - [IPSET_ATTR_SETNAME] = { .type = NLA_STRING, - .len = IPSET_MAXNAMELEN }, + [IPSET_ATTR_SETNAME] = { .type = NLA_NUL_STRING, + .len = IPSET_MAXNAMELEN - 1 }, [IPSET_ATTR_LINENO] = { .type = NLA_U32 }, [IPSET_ATTR_DATA] = { .type = NLA_NESTED }, [IPSET_ATTR_ADT] = { .type = NLA_NESTED }, @@ -842,11 +1012,11 @@ static int call_ad(struct sock *ctnl, struct sk_buff *skb, const struct nlattr * const attr[], struct ip_set *set, const struct nlattr *nla, - enum ipset_adt adt, uint32_t flags) + enum ipset_adt adt, u32 flags) { struct nlattr *head = nla_data(nla); int ret, len = nla_len(nla), retried = 0; - uint32_t lineno = 0; + u32 lineno = 0; bool eexist = flags & IPSET_FLAG_EXIST; do { @@ -856,13 +1026,13 @@ call_ad(struct sock *ctnl, struct sk_buff *skb, write_unlock_bh(&set->lock); } while (ret == -EAGAIN && set->variant->resize - && (ret = set->variant->resize(set, retried++)) == 0); + && (ret = set->variant->resize(set, GFP_KERNEL, retried++)) == 0); if (!ret || (ret == -IPSET_ERR_EXIST && eexist)) return 0; if (lineno && attr[IPSET_ATTR_LINENO]) { /* Error in restore/batch mode: send back lineno */ - uint32_t *errline = nla_data(attr[IPSET_ATTR_LINENO]); + u32 *errline = nla_data(attr[IPSET_ATTR_LINENO]); *errline = lineno; } @@ -877,7 +1047,7 @@ ip_set_uadd(struct sock *ctnl, struct sk_buff *skb, { struct ip_set *set; const struct nlattr *nla; - uint32_t flags = flag_exist(nlh); + u32 flags = flag_exist(nlh); int ret = 0; if (unlikely(protocol_failed(attr) @@ -921,7 +1091,7 @@ ip_set_udel(struct sock *ctnl, struct sk_buff *skb, { struct ip_set *set; const struct nlattr *nla; - uint32_t flags = flag_exist(nlh); + u32 flags = flag_exist(nlh); int ret = 0; if (unlikely(protocol_failed(attr) @@ -1022,7 +1192,7 @@ ip_set_header(struct sock *ctnl, struct sk_buff *skb, NLA_PUT_U8(skb2, IPSET_ATTR_PROTOCOL, IPSET_PROTOCOL); NLA_PUT_STRING(skb2, IPSET_ATTR_SETNAME, set->name); NLA_PUT_STRING(skb2, IPSET_ATTR_TYPENAME, set->type->name); - NLA_PUT_U8(skb2, IPSET_ATTR_FAMILY, set->type->family); + NLA_PUT_U8(skb2, IPSET_ATTR_FAMILY, set->family); NLA_PUT_U8(skb2, IPSET_ATTR_REVISION, set->type->revision); nlmsg_end(skb2, nlh2); @@ -1044,34 +1214,11 @@ nlmsg_failure: static const struct nla_policy ip_set_type_policy[IPSET_ATTR_CMD_MAX + 1] __read_mostly = { [IPSET_ATTR_PROTOCOL] = { .type = NLA_U8 }, - [IPSET_ATTR_TYPENAME] = { .type = NLA_STRING, - .len = IPSET_MAXNAMELEN }, + [IPSET_ATTR_TYPENAME] = { .type = NLA_NUL_STRING, + .len = IPSET_MAXNAMELEN - 1 }, [IPSET_ATTR_FAMILY] = { .type = NLA_U8 }, }; -static bool -find_set_type_minmax(const char *name, uint8_t family, - uint8_t *min, uint8_t *max) -{ - struct ip_set_type *type; - bool ret = false; - - *min = *max = 0; - ip_set_type_list_lock(); - list_for_each_entry(type, &ip_set_type_list, list) - if (STREQ(type->name, name) - && (type->family == family || type->family == AF_UNSPEC)) { - ret = true; - if (type->revision < *min) - *min = type->revision; - else if (type->revision > *max) - *max = type->revision; - } - ip_set_type_list_unlock(); - - return ret; -} - static int ip_set_type(struct sock *ctnl, struct sk_buff *skb, const struct nlmsghdr *nlh, @@ -1079,7 +1226,7 @@ ip_set_type(struct sock *ctnl, struct sk_buff *skb, { struct sk_buff *skb2; struct nlmsghdr *nlh2; - uint8_t family, min, max; + u8 family, min, max; const char *typename; int ret = 0; @@ -1094,7 +1241,7 @@ ip_set_type(struct sock *ctnl, struct sk_buff *skb, /* Try to load in the type module */ load_type_module(typename); if (!find_set_type_minmax(typename, family, &min, &max)) { - D("can't find: %s, family: %u", typename, family); + pr_debug("can't find: %s, family: %u", typename, family); return -EEXIST; } } @@ -1114,7 +1261,7 @@ ip_set_type(struct sock *ctnl, struct sk_buff *skb, NLA_PUT_U8(skb2, IPSET_ATTR_REVISION_MIN, min); nlmsg_end(skb2, nlh2); - D("Send TYPE, nlmsg_len: %u", nlh2->nlmsg_len); + pr_debug("Send TYPE, nlmsg_len: %u", nlh2->nlmsg_len); ret = netlink_unicast(ctnl, skb2, NETLINK_CB(skb).pid, MSG_DONTWAIT); if (ret < 0) return -EFAULT; @@ -1171,7 +1318,8 @@ nlmsg_failure: return -EFAULT; } -static const struct nfnl_callback ip_set_netlink_subsys_cb[IPSET_MSG_MAX] = { +static const struct nfnl_callback __read_mostly +ip_set_netlink_subsys_cb[IPSET_MSG_MAX] = { [IPSET_CMD_CREATE] = { .call = ip_set_create, .attr_count = IPSET_ATTR_CMD_MAX, @@ -1246,6 +1394,106 @@ static struct nfnetlink_subsystem ip_set_netlink_subsys = { .cb = ip_set_netlink_subsys_cb, }; +/* Interface to iptables/ip6tables */ + +static int +ip_set_sockfn_get(struct sock *sk, int optval, void *user, int *len) +{ + unsigned *op; + void *data; + int copylen = *len, ret = 0; + + if (!capable(CAP_NET_ADMIN)) + return -EPERM; + if (optval != SO_IP_SET) + return -EBADF; + if (*len < sizeof(unsigned)) { + return -EINVAL; + } + data = vmalloc(*len); + if (!data) + return -ENOMEM; + if (copy_from_user(data, user, *len) != 0) { + ret = -EFAULT; + goto done; + } + op = (unsigned *) data; + + if (*op < IP_SET_OP_VERSION) { + /* Check the version at the beginning of operations */ + struct ip_set_req_version *req_version = data; + if (req_version->version != IPSET_PROTOCOL) { + ret = -EPROTO; + goto done; + } + } + + switch (*op) { + case IP_SET_OP_VERSION: { + struct ip_set_req_version *req_version = data; + + if (*len != sizeof(struct ip_set_req_version)) { + ret = -EINVAL; + goto done; + } + + req_version->version = IPSET_PROTOCOL; + ret = copy_to_user(user, req_version, + sizeof(struct ip_set_req_version)); + goto done; + } + case IP_SET_OP_GET_BYNAME: { + struct ip_set_req_get_set *req_get = data; + + if (*len != sizeof(struct ip_set_req_get_set)) { + ret = -EINVAL; + goto done; + } + req_get->set.name[IPSET_MAXNAMELEN - 1] = '\0'; + nfnl_lock(); + req_get->set.index = find_set_id(req_get->set.name); + nfnl_unlock(); + goto copy; + } + case IP_SET_OP_GET_BYINDEX: { + struct ip_set_req_get_set *req_get = data; + + if (*len != sizeof(struct ip_set_req_get_set) + || req_get->set.index >= ip_set_max) { + ret = -EINVAL; + goto done; + } + nfnl_lock(); + strncpy(req_get->set.name, + ip_set_list[req_get->set.index] + ? ip_set_list[req_get->set.index]->name : "", + IPSET_MAXNAMELEN); + nfnl_unlock(); + goto copy; + } + default: + ret = -EBADMSG; + goto done; + } /* end of switch(op) */ + + copy: + ret = copy_to_user(user, data, copylen); + + done: + vfree(data); + if (ret > 0) + ret = 0; + return ret; +} + +static struct nf_sockopt_ops so_set = { + .pf = PF_INET, + .get_optmin = SO_IP_SET, + .get_optmax = SO_IP_SET + 1, + .get = &ip_set_sockfn_get, + .owner = THIS_MODULE, +}; + static int __init ip_set_init(void) { @@ -1258,7 +1506,7 @@ ip_set_init(void) ip_set_list = kzalloc(sizeof(struct ip_set *) * ip_set_max, GFP_KERNEL); if (!ip_set_list) { - printk(KERN_ERR "Unable to create ip_set_list\n"); + pr_err("ip_set: Unable to create ip_set_list"); return -ENOMEM; } @@ -1266,12 +1514,19 @@ ip_set_init(void) ret = nfnetlink_subsys_register(&ip_set_netlink_subsys); if (ret != 0) { - printk("ip_set_init: cannot register with nfnetlink.\n"); + pr_err("ip_set: cannot register with nfnetlink."); + kfree(ip_set_list); + return ret; + } + ret = nf_register_sockopt(&so_set); + if (ret != 0) { + pr_err("SO_SET registry failed: %d", ret); + nfnetlink_subsys_unregister(&ip_set_netlink_subsys); kfree(ip_set_list); return ret; } - printk("ip_set with protocol version %u loaded\n", IPSET_PROTOCOL); + pr_notice("ip_set with protocol version %u loaded", IPSET_PROTOCOL); return 0; } @@ -1279,20 +1534,11 @@ static void __exit ip_set_fini(void) { /* There can't be any existing set */ + nf_unregister_sockopt(&so_set); nfnetlink_subsys_unregister(&ip_set_netlink_subsys); kfree(ip_set_list); - D("these are the famous last words"); + pr_debug("these are the famous last words"); } -EXPORT_SYMBOL(ip_set_type_register); -EXPORT_SYMBOL(ip_set_type_unregister); - -EXPORT_SYMBOL(ip_set_get_byname); -EXPORT_SYMBOL(ip_set_put_byindex); - -EXPORT_SYMBOL(ip_set_add); -EXPORT_SYMBOL(ip_set_del); -EXPORT_SYMBOL(ip_set_test); - module_init(ip_set_init); module_exit(ip_set_fini); diff --git a/kernel/ip_set_bitmap_ip.c b/kernel/ip_set_bitmap_ip.c index ccb5473..66d3979 100644 --- a/kernel/ip_set_bitmap_ip.c +++ b/kernel/ip_set_bitmap_ip.c @@ -9,6 +9,7 @@ /* Kernel module implementing an IP set type: the bitmap:ip type */ +#include #include #include #include @@ -17,7 +18,6 @@ #include #include #include -#include #include #include #include @@ -38,28 +38,28 @@ MODULE_ALIAS("ip_set_bitmap:ip"); struct bitmap_ip { void *members; /* the set members */ - uint32_t first_ip; /* host byte order, included in range */ - uint32_t last_ip; /* host byte order, included in range */ - uint32_t elements; /* number of max elements in the set */ - uint32_t hosts; /* number of hosts in a subnet */ + u32 first_ip; /* host byte order, included in range */ + u32 last_ip; /* host byte order, included in range */ + u32 elements; /* number of max elements in the set */ + u32 hosts; /* number of hosts in a subnet */ size_t memsize; /* members size */ - uint8_t netmask; /* subnet netmask */ + u8 netmask; /* subnet netmask */ }; -static inline uint32_t -ip_to_id(const struct bitmap_ip *map, uint32_t ip) +static inline u32 +ip_to_id(const struct bitmap_ip *map, u32 ip) { return ((ip & HOSTMASK(map->netmask)) - map->first_ip)/map->hosts; } static inline int -bitmap_ip_test(const struct bitmap_ip *map, uint32_t id) +bitmap_ip_test(const struct bitmap_ip *map, u32 id) { return !!test_bit(id, map->members); } static inline int -bitmap_ip_add(struct bitmap_ip *map, uint32_t id) +bitmap_ip_add(struct bitmap_ip *map, u32 id) { if (test_and_set_bit(id, map->members)) return -IPSET_ERR_EXIST; @@ -68,7 +68,7 @@ bitmap_ip_add(struct bitmap_ip *map, uint32_t id) } static inline int -bitmap_ip_del(struct bitmap_ip *map, uint32_t id) +bitmap_ip_del(struct bitmap_ip *map, u32 id) { if (!test_and_clear_bit(id, map->members)) return -IPSET_ERR_EXIST; @@ -78,14 +78,12 @@ bitmap_ip_del(struct bitmap_ip *map, uint32_t id) static int bitmap_ip_kadt(struct ip_set *set, const struct sk_buff *skb, - enum ipset_adt adt, uint8_t pf, const uint8_t *flags) + enum ipset_adt adt, u8 pf, u8 dim, u8 flags) { struct bitmap_ip *map = set->data; - uint32_t ip = ntohl(ip4addr(skb, flags)); + u32 ip; - if (pf != AF_INET) - return -EINVAL; - + ip = ntohl(ip4addr(skb, flags & IPSET_DIM_ONE_SRC)); if (ip < map->first_ip || ip > map->last_ip) return -IPSET_ERR_BITMAP_RANGE; @@ -113,12 +111,12 @@ bitmap_ip_adt_policy[IPSET_ATTR_ADT_MAX+1] __read_mostly = { static int bitmap_ip_uadt(struct ip_set *set, struct nlattr *head, int len, - enum ipset_adt adt, uint32_t *lineno, uint32_t flags) + enum ipset_adt adt, u32 *lineno, u32 flags) { struct bitmap_ip *map = set->data; struct nlattr *tb[IPSET_ATTR_ADT_MAX]; bool eexist = flags & IPSET_FLAG_EXIST; - uint32_t ip, ip_to, id; + u32 ip, ip_to, id; int ret = 0; if (nla_parse(tb, IPSET_ATTR_ADT_MAX, head, len, @@ -133,6 +131,8 @@ bitmap_ip_uadt(struct ip_set *set, struct nlattr *head, int len, if (ip < map->first_ip || ip > map->last_ip) return -IPSET_ERR_BITMAP_RANGE; + /* Set was defined without timeout support, + * don't ignore attribute silently */ if (tb[IPSET_ATTR_TIMEOUT]) return -IPSET_ERR_TIMEOUT; @@ -147,10 +147,11 @@ bitmap_ip_uadt(struct ip_set *set, struct nlattr *head, int len, return -IPSET_ERR_BITMAP_RANGE; } } else if (tb[IPSET_ATTR_CIDR]) { - uint8_t cidr = nla_get_u8(tb[IPSET_ATTR_CIDR]); + u8 cidr = nla_get_u8(tb[IPSET_ATTR_CIDR]); if (cidr > 32) return -IPSET_ERR_INVALID_CIDR; + ip &= HOSTMASK(cidr); ip_to = ip | ~HOSTMASK(cidr); } else ip_to = ip; @@ -168,7 +169,7 @@ bitmap_ip_uadt(struct ip_set *set, struct nlattr *head, int len, *lineno = nla_get_u32(tb[IPSET_ATTR_LINENO]); return ret; } - }; + } return ret; } @@ -196,11 +197,6 @@ bitmap_ip_head(struct ip_set *set, struct sk_buff *skb) { const struct bitmap_ip *map = set->data; struct nlattr *nested; - uint32_t id, elements; - - for (id = 0, elements = 0; id < map->elements; id++) - if (bitmap_ip_test(map, id)) - elements++; nested = ipset_nest_start(skb, IPSET_ATTR_DATA); if (!nested) @@ -209,10 +205,10 @@ bitmap_ip_head(struct ip_set *set, struct sk_buff *skb) NLA_PUT_NET32(skb, IPSET_ATTR_IP_TO, htonl(map->last_ip)); if (map->netmask != 32) NLA_PUT_U8(skb, IPSET_ATTR_NETMASK, map->netmask); - NLA_PUT_NET32(skb, IPSET_ATTR_ELEMENTS, htonl(elements)); NLA_PUT_NET32(skb, IPSET_ATTR_REFERENCES, htonl(atomic_read(&set->ref) - 1)); - NLA_PUT_NET32(skb, IPSET_ATTR_MEMSIZE, htonl(map->memsize)); + NLA_PUT_NET32(skb, IPSET_ATTR_MEMSIZE, + htonl(sizeof(*map) + map->memsize)); ipset_nest_end(skb, nested); return 0; @@ -226,7 +222,7 @@ bitmap_ip_list(struct ip_set *set, { const struct bitmap_ip *map = set->data; struct nlattr *atd, *nested; - uint32_t id, first = cb->args[2]; + u32 id, first = cb->args[2]; atd = ipset_nest_start(skb, IPSET_ATTR_ADT); if (!atd) @@ -245,8 +241,6 @@ bitmap_ip_list(struct ip_set *set, } NLA_PUT_NET32(skb, IPSET_ATTR_IP, htonl(map->first_ip + id * map->hosts)); - if (map->netmask != 32) - NLA_PUT_U8(skb, IPSET_ATTR_CIDR, map->netmask); ipset_nest_end(skb, nested); } ipset_nest_end(skb, atd); @@ -260,6 +254,17 @@ nla_put_failure: return 0; } +static bool +bitmap_ip_same_set(const struct ip_set *a, const struct ip_set *b) +{ + struct bitmap_ip *x = a->data; + struct bitmap_ip *y = b->data; + + return x->first_ip == y->first_ip + && x->last_ip == y->last_ip + && x->netmask == y->netmask; +} + static const struct ip_set_type_variant bitmap_ip __read_mostly = { .kadt = bitmap_ip_kadt, .uadt = bitmap_ip_uadt, @@ -267,34 +272,35 @@ static const struct ip_set_type_variant bitmap_ip __read_mostly = { .flush = bitmap_ip_flush, .head = bitmap_ip_head, .list = bitmap_ip_list, + .same_set = bitmap_ip_same_set, }; /* Timeout variant */ struct bitmap_ip_timeout { void *members; /* the set members */ - uint32_t first_ip; /* host byte order, included in range */ - uint32_t last_ip; /* host byte order, included in range */ - uint32_t elements; /* number of max elements in the set */ - uint32_t hosts; /* number of hosts in a subnet */ + u32 first_ip; /* host byte order, included in range */ + u32 last_ip; /* host byte order, included in range */ + u32 elements; /* number of max elements in the set */ + u32 hosts; /* number of hosts in a subnet */ size_t memsize; /* members size */ - uint8_t netmask; /* subnet netmask */ + u8 netmask; /* subnet netmask */ - uint32_t timeout; /* timeout parameter */ + u32 timeout; /* timeout parameter */ struct timer_list gc; /* garbage collection */ }; static inline bool -bitmap_ip_timeout_test(const struct bitmap_ip_timeout *map, uint32_t id) +bitmap_ip_timeout_test(const struct bitmap_ip_timeout *map, u32 id) { unsigned long *table = map->members; return ip_set_timeout_test(table[id]); } -static int +static inline int bitmap_ip_timeout_add(struct bitmap_ip_timeout *map, - uint32_t id, uint32_t timeout) + u32 id, u32 timeout) { unsigned long *table = map->members; @@ -306,8 +312,8 @@ bitmap_ip_timeout_add(struct bitmap_ip_timeout *map, return 0; } -static int -bitmap_ip_timeout_del(struct bitmap_ip_timeout *map, uint32_t id) +static inline int +bitmap_ip_timeout_del(struct bitmap_ip_timeout *map, u32 id) { unsigned long *table = map->members; int ret = -IPSET_ERR_EXIST; @@ -321,14 +327,12 @@ bitmap_ip_timeout_del(struct bitmap_ip_timeout *map, uint32_t id) static int bitmap_ip_timeout_kadt(struct ip_set *set, const struct sk_buff *skb, - enum ipset_adt adt, uint8_t pf, const uint8_t *flags) + enum ipset_adt adt, u8 pf, u8 dim, u8 flags) { struct bitmap_ip_timeout *map = set->data; - uint32_t ip = ntohl(ip4addr(skb, flags)); - - if (pf != AF_INET) - return -EINVAL; + u32 ip; + ip = ntohl(ip4addr(skb, flags & IPSET_DIM_ONE_SRC)); if (ip < map->first_ip || ip > map->last_ip) return -IPSET_ERR_BITMAP_RANGE; @@ -348,12 +352,12 @@ bitmap_ip_timeout_kadt(struct ip_set *set, const struct sk_buff *skb, static int bitmap_ip_timeout_uadt(struct ip_set *set, struct nlattr *head, int len, - enum ipset_adt adt, uint32_t *lineno, uint32_t flags) + enum ipset_adt adt, u32 *lineno, u32 flags) { struct bitmap_ip_timeout *map = set->data; struct nlattr *tb[IPSET_ATTR_ADT_MAX]; bool eexist = flags & IPSET_FLAG_EXIST; - uint32_t ip, ip_to, id, timeout = map->timeout; + u32 ip, ip_to, id, timeout = map->timeout; int ret = 0; if (nla_parse(tb, IPSET_ATTR_ADT_MAX, head, len, @@ -380,10 +384,11 @@ bitmap_ip_timeout_uadt(struct ip_set *set, struct nlattr *head, int len, return -IPSET_ERR_BITMAP_RANGE; } } else if (tb[IPSET_ATTR_CIDR]) { - uint8_t cidr = nla_get_u8(tb[IPSET_ATTR_CIDR]); + u8 cidr = nla_get_u8(tb[IPSET_ATTR_CIDR]); if (cidr > 32) return -IPSET_ERR_INVALID_CIDR; + ip &= HOSTMASK(cidr); ip_to = ip | ~HOSTMASK(cidr); } else ip_to = ip; @@ -391,9 +396,8 @@ bitmap_ip_timeout_uadt(struct ip_set *set, struct nlattr *head, int len, if (ip_to > map->last_ip) return -IPSET_ERR_BITMAP_RANGE; - if (tb[IPSET_ATTR_TIMEOUT]) { - timeout = ip_set_get_h32(tb[IPSET_ATTR_TIMEOUT]); - } + if (tb[IPSET_ATTR_TIMEOUT]) + timeout = ip_set_timeout_uget(tb[IPSET_ATTR_TIMEOUT]); for (; !before(ip_to, ip); ip += map->hosts) { id = ip_to_id((const struct bitmap_ip *)map, ip); @@ -414,11 +418,8 @@ static void bitmap_ip_timeout_destroy(struct ip_set *set) { struct bitmap_ip_timeout *map = set->data; - - /* gc might be running: del_timer_sync can't be used */ - while (!del_timer(&map->gc)) - msleep(IPSET_DESTROY_TIMER_SLEEP); - + + del_timer_sync(&map->gc); ip_set_free(map->members, set->flags); kfree(map); @@ -438,11 +439,6 @@ bitmap_ip_timeout_head(struct ip_set *set, struct sk_buff *skb) { const struct bitmap_ip_timeout *map = set->data; struct nlattr *nested; - uint32_t id, elements; - - for (id = 0, elements = 0; id < map->elements; id++) - if (bitmap_ip_timeout_test(map, id)) - elements++; nested = ipset_nest_start(skb, IPSET_ATTR_DATA); if (!nested) @@ -452,10 +448,10 @@ bitmap_ip_timeout_head(struct ip_set *set, struct sk_buff *skb) if (map->netmask != 32) NLA_PUT_U8(skb, IPSET_ATTR_NETMASK, map->netmask); NLA_PUT_NET32(skb, IPSET_ATTR_TIMEOUT , htonl(map->timeout)); - NLA_PUT_NET32(skb, IPSET_ATTR_ELEMENTS, htonl(elements)); NLA_PUT_NET32(skb, IPSET_ATTR_REFERENCES, htonl(atomic_read(&set->ref) - 1)); - NLA_PUT_NET32(skb, IPSET_ATTR_MEMSIZE, htonl(map->memsize)); + NLA_PUT_NET32(skb, IPSET_ATTR_MEMSIZE, + htonl(sizeof(*map) + map->memsize)); ipset_nest_end(skb, nested); return 0; @@ -469,7 +465,7 @@ bitmap_ip_timeout_list(struct ip_set *set, { const struct bitmap_ip_timeout *map = set->data; struct nlattr *adt, *nested; - uint32_t id, first = cb->args[2]; + u32 id, first = cb->args[2]; unsigned long *table = map->members; adt = ipset_nest_start(skb, IPSET_ATTR_ADT); @@ -489,8 +485,6 @@ bitmap_ip_timeout_list(struct ip_set *set, } NLA_PUT_NET32(skb, IPSET_ATTR_IP, htonl(map->first_ip + id * map->hosts)); - if (map->netmask != 32) - NLA_PUT_U8(skb, IPSET_ATTR_CIDR, map->netmask); NLA_PUT_NET32(skb, IPSET_ATTR_TIMEOUT, htonl(ip_set_timeout_get(table[id]))); ipset_nest_end(skb, nested); @@ -508,6 +502,18 @@ nla_put_failure: return 0; } +static bool +bitmap_ip_timeout_same_set(const struct ip_set *a, const struct ip_set *b) +{ + struct bitmap_ip_timeout *x = a->data; + struct bitmap_ip_timeout *y = b->data; + + return x->first_ip == y->first_ip + && x->last_ip == y->last_ip + && x->netmask == y->netmask + && x->timeout == y->timeout; +} + static const struct ip_set_type_variant bitmap_ip_timeout __read_mostly = { .kadt = bitmap_ip_timeout_kadt, .uadt = bitmap_ip_timeout_uadt, @@ -515,15 +521,16 @@ static const struct ip_set_type_variant bitmap_ip_timeout __read_mostly = { .flush = bitmap_ip_timeout_flush, .head = bitmap_ip_timeout_head, .list = bitmap_ip_timeout_list, + .same_set = bitmap_ip_timeout_same_set, }; static void -bitmap_ip_timeout_gc(unsigned long ul_set) +bitmap_ip_gc(unsigned long ul_set) { struct ip_set *set = (struct ip_set *) ul_set; struct bitmap_ip_timeout *map = set->data; unsigned long *table = map->members; - uint32_t id; + u32 id; /* We run parallel with other readers (test element) * but adding/deleting new entries is locked out */ @@ -544,7 +551,7 @@ bitmap_ip_gc_init(struct ip_set *set) init_timer(&map->gc); map->gc.data = (unsigned long) set; - map->gc.function = bitmap_ip_timeout_gc; + map->gc.function = bitmap_ip_gc; map->gc.expires = jiffies + IPSET_GC_PERIOD(map->timeout) * HZ; add_timer(&map->gc); } @@ -562,8 +569,8 @@ bitmap_ip_create_policy[IPSET_ATTR_CREATE_MAX+1] __read_mostly = { static bool init_map_ip(struct ip_set *set, struct bitmap_ip *map, - uint32_t first_ip, uint32_t last_ip, - uint32_t elements, uint32_t hosts, uint8_t netmask) + u32 first_ip, u32 last_ip, + u32 elements, u32 hosts, u8 netmask) { map->members = ip_set_alloc(map->memsize, GFP_KERNEL, &set->flags); if (!map->members) @@ -582,11 +589,11 @@ init_map_ip(struct ip_set *set, struct bitmap_ip *map, static int bitmap_ip_create(struct ip_set *set, struct nlattr *head, int len, - uint32_t flags) + u32 flags) { struct nlattr *tb[IPSET_ATTR_CREATE_MAX]; - uint32_t first_ip, last_ip, hosts, elements; - uint8_t netmask = 32; + u32 first_ip, last_ip, hosts, elements; + u8 netmask = 32; if (nla_parse(tb, IPSET_ATTR_CREATE_MAX, head, len, bitmap_ip_create_policy)) @@ -600,13 +607,13 @@ bitmap_ip_create(struct ip_set *set, struct nlattr *head, int len, if (tb[IPSET_ATTR_IP_TO]) { last_ip = ip_set_get_h32(tb[IPSET_ATTR_IP_TO]); if (first_ip > last_ip) { - uint32_t tmp = first_ip; + u32 tmp = first_ip; first_ip = last_ip; last_ip = tmp; } } else if (tb[IPSET_ATTR_CIDR]) { - uint8_t cidr = nla_get_u8(tb[IPSET_ATTR_CIDR]); + u8 cidr = nla_get_u8(tb[IPSET_ATTR_CIDR]); if (cidr >= 32) return -IPSET_ERR_INVALID_CIDR; @@ -628,8 +635,8 @@ bitmap_ip_create(struct ip_set *set, struct nlattr *head, int len, hosts = 1; elements = last_ip - first_ip + 1; } else { - uint8_t mask_bits; - uint32_t mask; + u8 mask_bits; + u32 mask; mask = range_to_mask(first_ip, last_ip, &mask_bits); @@ -637,14 +644,14 @@ bitmap_ip_create(struct ip_set *set, struct nlattr *head, int len, || netmask <= mask_bits) return -IPSET_ERR_BITMAP_RANGE; - D("mask_bits %u, netmask %u", mask_bits, netmask); + pr_debug("mask_bits %u, netmask %u", mask_bits, netmask); hosts = 2 << (32 - netmask - 1); elements = 2 << (netmask - mask_bits - 1); } if (elements > IPSET_BITMAP_MAX_RANGE + 1) { return -IPSET_ERR_BITMAP_RANGE_SIZE; } - D("hosts %u, elements %u", hosts, elements); + pr_debug("hosts %u, elements %u", hosts, elements); if (tb[IPSET_ATTR_TIMEOUT]) { struct bitmap_ip_timeout *map; @@ -662,8 +669,7 @@ bitmap_ip_create(struct ip_set *set, struct nlattr *head, int len, return -ENOMEM; } - map->timeout = ip_set_get_h32(tb[IPSET_ATTR_TIMEOUT]); - set->flags |= IP_SET_FLAG_TIMEOUT; + map->timeout = ip_set_timeout_uget(tb[IPSET_ATTR_TIMEOUT]); set->variant = &bitmap_ip_timeout; bitmap_ip_gc_init(set); @@ -692,6 +698,7 @@ static struct ip_set_type bitmap_ip_type = { .name = "bitmap:ip", .protocol = IPSET_PROTOCOL, .features = IPSET_TYPE_IP, + .dimension = IPSET_DIM_ONE, .family = AF_INET, .revision = 0, .create = bitmap_ip_create, diff --git a/kernel/ip_set_bitmap_ipmac.c b/kernel/ip_set_bitmap_ipmac.c index 45335dd..d036862 100644 --- a/kernel/ip_set_bitmap_ipmac.c +++ b/kernel/ip_set_bitmap_ipmac.c @@ -10,8 +10,10 @@ /* Kernel module implementing an IP set type: the bitmap:ip,mac type */ +#include #include #include +#include #include #include #include @@ -19,7 +21,6 @@ #include #include #include -#include #include #include #include @@ -40,119 +41,235 @@ enum { MAC_UNSET, /* element is set, without MAC */ }; -/* Member element without and with timeout */ +/* Type structure */ +struct bitmap_ipmac { + void *members; /* the set members */ + u32 first_ip; /* host byte order, included in range */ + u32 last_ip; /* host byte order, included in range */ + u32 timeout; /* timeout value */ + struct timer_list gc; /* garbage collector */ + size_t dsize; /* size of element */ +}; struct ipmac { + u32 id; /* id in array */ + unsigned char *ether; /* ethernet address */ +}; + +/* Member element without and with timeout */ + +struct ipmac_elem { unsigned char ether[ETH_ALEN]; unsigned char match; }; -struct ipmac_timeout { +struct ipmac_telem { unsigned char ether[ETH_ALEN]; unsigned char match; unsigned long timeout; }; -struct bitmap_ipmac { - void *members; /* the set members */ - uint32_t first_ip; /* host byte order, included in range */ - uint32_t last_ip; /* host byte order, included in range */ - uint32_t timeout; /* timeout value */ - struct timer_list gc; /* garbage collector */ - size_t elem_size; /* size of element */ -}; - static inline void * -bitmap_ipmac_elem(const struct bitmap_ipmac *map, uint32_t id) +bitmap_ipmac_elem(const struct bitmap_ipmac *map, u32 id) { - return (void *)((char *)map->members + id * map->elem_size); + return (void *)((char *)map->members + id * map->dsize); } static inline bool -bitmap_timeout(const struct bitmap_ipmac *map, uint32_t id) +bitmap_timeout(const struct bitmap_ipmac *map, u32 id) { - const struct ipmac_timeout *elem = bitmap_ipmac_elem(map, id); + const struct ipmac_telem *elem = bitmap_ipmac_elem(map, id); return ip_set_timeout_test(elem->timeout); } static inline bool -bitmap_expired(const struct bitmap_ipmac *map, uint32_t id) +bitmap_expired(const struct bitmap_ipmac *map, u32 id) { - const struct ipmac_timeout *elem = bitmap_ipmac_elem(map, id); + const struct ipmac_telem *elem = bitmap_ipmac_elem(map, id); return ip_set_timeout_expired(elem->timeout); } static inline int -bitmap_ipmac_exist(const struct ipmac *elem, bool with_timeout) +bitmap_ipmac_exist(const struct ipmac_telem *elem) { - const struct ipmac_timeout *e = (const struct ipmac_timeout *) elem; - return elem->match == MAC_UNSET || (elem->match == MAC_FILLED - && !(with_timeout && ip_set_timeout_expired(e->timeout))); + && !ip_set_timeout_expired(elem->timeout)); } -static inline int -bitmap_ipmac_test(const struct bitmap_ipmac *map, bool with_timeout, - uint32_t id, const unsigned char *ether) +/* Base variant */ + +static int +bitmap_ipmac_test(struct ip_set *set, void *value, + gfp_t gfp_flags, u32 timeout) { - const struct ipmac *elem = bitmap_ipmac_elem(map, id); + const struct bitmap_ipmac *map = set->data; + const struct ipmac *data = value; + const struct ipmac_elem *elem = bitmap_ipmac_elem(map, data->id); switch (elem->match) { case MAC_UNSET: /* Trigger kernel to fill out the ethernet address */ return -EAGAIN; case MAC_FILLED: - return (ether == NULL - || memcmp(ether, elem->ether, ETH_ALEN) == 0) - && (!with_timeout || bitmap_timeout(map, id)); + return data->ether == NULL + || compare_ether_addr(data->ether, elem->ether) == 0; } return 0; } static int -bitmap_ipmac_add(struct bitmap_ipmac *map, bool with_timeout, - uint32_t id, const unsigned char *ether, - uint32_t timeout) +bitmap_ipmac_add(struct ip_set *set, void *value, + gfp_t gfp_flags, u32 timeout) { - struct ipmac *elem = bitmap_ipmac_elem(map, id); - struct ipmac_timeout *e = (struct ipmac_timeout *) elem; + struct bitmap_ipmac *map = set->data; + const struct ipmac *data = value; + struct ipmac_elem *elem = bitmap_ipmac_elem(map, data->id); switch (elem->match) { case MAC_UNSET: - if (!ether) + if (!data->ether) /* Already added without ethernet address */ return -IPSET_ERR_EXIST; /* Fill the MAC address and activate the timer */ - memcpy(elem->ether, ether, ETH_ALEN); + memcpy(elem->ether, data->ether, ETH_ALEN); elem->match = MAC_FILLED; - if (with_timeout) { - if (timeout == map->timeout) - /* Timeout was not specified, get stored one */ - timeout = e->timeout; - e->timeout = ip_set_timeout_set(timeout); + break; + case MAC_FILLED: + return -IPSET_ERR_EXIST; + case MAC_EMPTY: + if (data->ether) { + memcpy(elem->ether, data->ether, ETH_ALEN); + elem->match = MAC_FILLED; + } else + elem->match = MAC_UNSET; + } + + return 0; +} + +static int +bitmap_ipmac_del(struct ip_set *set, void *value, + gfp_t gfp_flags, u32 timeout) +{ + struct bitmap_ipmac *map = set->data; + const struct ipmac *data = value; + struct ipmac_elem *elem = bitmap_ipmac_elem(map, data->id); + + if (elem->match == MAC_EMPTY) + return -IPSET_ERR_EXIST; + + elem->match = MAC_EMPTY; + + return 0; +} + +static int +bitmap_ipmac_list(struct ip_set *set, + struct sk_buff *skb, struct netlink_callback *cb) +{ + const struct bitmap_ipmac *map = set->data; + const struct ipmac_elem *elem; + struct nlattr *atd, *nested; + u32 id, first = cb->args[2]; + u32 last = map->last_ip - map->first_ip; + + atd = ipset_nest_start(skb, IPSET_ATTR_ADT); + if (!atd) + return -EFAULT; + for (; cb->args[2] <= last; cb->args[2]++) { + id = cb->args[2]; + elem = bitmap_ipmac_elem(map, id); + if (elem->match == MAC_EMPTY) + continue; + nested = ipset_nest_start(skb, IPSET_ATTR_DATA); + if (!nested) { + if (id == first) { + nla_nest_cancel(skb, atd); + return -EFAULT; + } else + goto nla_put_failure; } + NLA_PUT_NET32(skb, IPSET_ATTR_IP, + htonl(map->first_ip + id)); + if (elem->match == MAC_FILLED) + NLA_PUT(skb, IPSET_ATTR_ETHER, ETH_ALEN, + elem->ether); + ipset_nest_end(skb, nested); + } + ipset_nest_end(skb, atd); + /* Set listing finished */ + cb->args[2] = 0; + + return 0; + +nla_put_failure: + nla_nest_cancel(skb, nested); + ipset_nest_end(skb, atd); + return 0; +} + +/* Timeout variant */ + +static int +bitmap_ipmac_ttest(struct ip_set *set, void *value, + gfp_t gfp_flags, u32 timeout) +{ + const struct bitmap_ipmac *map = set->data; + const struct ipmac *data = value; + const struct ipmac_elem *elem = bitmap_ipmac_elem(map, data->id); + + switch (elem->match) { + case MAC_UNSET: + /* Trigger kernel to fill out the ethernet address */ + return -EAGAIN; + case MAC_FILLED: + return (data->ether == NULL + || compare_ether_addr(data->ether, elem->ether) == 0) + && !bitmap_expired(map, data->id); + } + return 0; +} + +static int +bitmap_ipmac_tadd(struct ip_set *set, void *value, + gfp_t gfp_flags, u32 timeout) +{ + struct bitmap_ipmac *map = set->data; + const struct ipmac *data = value; + struct ipmac_telem *elem = bitmap_ipmac_elem(map, data->id); + + switch (elem->match) { + case MAC_UNSET: + if (!data->ether) + /* Already added without ethernet address */ + return -IPSET_ERR_EXIST; + /* Fill the MAC address and activate the timer */ + memcpy(elem->ether, data->ether, ETH_ALEN); + elem->match = MAC_FILLED; + if (timeout == map->timeout) + /* Timeout was not specified, get stored one */ + timeout = elem->timeout; + elem->timeout = ip_set_timeout_set(timeout); break; case MAC_FILLED: - if (!(with_timeout && bitmap_expired(map, id))) + if (bitmap_expired(map, data->id)) return -IPSET_ERR_EXIST; /* Fall through */ case MAC_EMPTY: - if (ether) { - memcpy(elem->ether, ether, ETH_ALEN); + if (data->ether) { + memcpy(elem->ether, data->ether, ETH_ALEN); elem->match = MAC_FILLED; } else elem->match = MAC_UNSET; - if (with_timeout) { - /* If MAC is unset yet, we store plain timeout - * because the timer is not activated yet - * and we can reuse it later when MAC is filled out, - * possibly by the kernel */ - e->timeout = ether ? ip_set_timeout_set(timeout) - : timeout; - } + /* If MAC is unset yet, we store plain timeout + * because the timer is not activated yet + * and we can reuse it later when MAC is filled out, + * possibly by the kernel */ + elem->timeout = data->ether ? ip_set_timeout_set(timeout) + : timeout; break; } @@ -160,13 +277,14 @@ bitmap_ipmac_add(struct bitmap_ipmac *map, bool with_timeout, } static int -bitmap_ipmac_del(struct bitmap_ipmac *map, bool with_timeout, - uint32_t id) +bitmap_ipmac_tdel(struct ip_set *set, void *value, + gfp_t gfp_flags, u32 timeout) { - struct ipmac *elem = bitmap_ipmac_elem(map, id); + struct bitmap_ipmac *map = set->data; + const struct ipmac *data = value; + struct ipmac_telem *elem = bitmap_ipmac_elem(map, data->id); - if (elem->match == MAC_EMPTY - || (with_timeout && bitmap_expired(map, id))) + if (elem->match == MAC_EMPTY || bitmap_expired(map, data->id)) return -IPSET_ERR_EXIST; elem->match = MAC_EMPTY; @@ -174,39 +292,75 @@ bitmap_ipmac_del(struct bitmap_ipmac *map, bool with_timeout, return 0; } +static int +bitmap_ipmac_tlist(struct ip_set *set, + struct sk_buff *skb, struct netlink_callback *cb) +{ + const struct bitmap_ipmac *map = set->data; + const struct ipmac_telem *elem; + struct nlattr *atd, *nested; + u32 id, first = cb->args[2]; + u32 timeout, last = map->last_ip - map->first_ip; + + atd = ipset_nest_start(skb, IPSET_ATTR_ADT); + if (!atd) + return -EFAULT; + for (; cb->args[2] <= last; cb->args[2]++) { + id = cb->args[2]; + elem = bitmap_ipmac_elem(map, id); + if (!bitmap_ipmac_exist(elem)) + continue; + nested = ipset_nest_start(skb, IPSET_ATTR_DATA); + if (!nested) { + if (id == first) { + nla_nest_cancel(skb, atd); + return -EFAULT; + } else + goto nla_put_failure; + } + NLA_PUT_NET32(skb, IPSET_ATTR_IP, + htonl(map->first_ip + id)); + if (elem->match == MAC_FILLED) + NLA_PUT(skb, IPSET_ATTR_ETHER, ETH_ALEN, + elem->ether); + timeout = elem->match == MAC_UNSET ? elem->timeout + : ip_set_timeout_get(elem->timeout); + NLA_PUT_NET32(skb, IPSET_ATTR_TIMEOUT, htonl(timeout)); + ipset_nest_end(skb, nested); + } + ipset_nest_end(skb, atd); + /* Set listing finished */ + cb->args[2] = 0; + + return 0; + +nla_put_failure: + nla_nest_cancel(skb, nested); + ipset_nest_end(skb, atd); + return 0; +} + static int bitmap_ipmac_kadt(struct ip_set *set, const struct sk_buff *skb, - enum ipset_adt adt, uint8_t pf, const uint8_t *flags) + enum ipset_adt adt, u8 pf, u8 dim, u8 flags) { struct bitmap_ipmac *map = set->data; - uint32_t ip = ntohl(ip4addr(skb, flags)); - bool with_timeout = set->flags & IP_SET_FLAG_TIMEOUT; - - if (pf != AF_INET) - return -EINVAL; + ipset_adtfn adtfn = set->variant->adt[adt]; + struct ipmac data; - if (ip < map->first_ip || ip > map->last_ip) + data.id = ntohl(ip4addr(skb, flags & IPSET_DIM_ONE_SRC)); + if (data.id < map->first_ip || data.id > map->last_ip) return -IPSET_ERR_BITMAP_RANGE; + /* Backward compatibility: we don't check the second flag */ if (skb_mac_header(skb) < skb->head || (skb_mac_header(skb) + ETH_HLEN) > skb->data) return -EINVAL; - ip -= map->first_ip; - - switch (adt) { - case IPSET_TEST: - return bitmap_ipmac_test(map, with_timeout, - ip, eth_hdr(skb)->h_source); - case IPSET_ADD: - return bitmap_ipmac_add(map, with_timeout, - ip, eth_hdr(skb)->h_source, - map->timeout); - case IPSET_DEL: - return bitmap_ipmac_del(map, with_timeout, ip); - default: - return -EINVAL; - } + data.id -= map->first_ip; + data.ether = eth_hdr(skb)->h_source; + + return adtfn(set, &data, GFP_ATOMIC, map->timeout); } static const struct nla_policy @@ -218,14 +372,14 @@ bitmap_ipmac_adt_policy[IPSET_ATTR_ADT_MAX + 1] __read_mostly = { static int bitmap_ipmac_uadt(struct ip_set *set, struct nlattr *head, int len, - enum ipset_adt adt, uint32_t *lineno, uint32_t flags) + enum ipset_adt adt, u32 *lineno, u32 flags) { struct bitmap_ipmac *map = set->data; struct nlattr *tb[IPSET_ATTR_ADT_MAX]; + ipset_adtfn adtfn = set->variant->adt[adt]; bool eexist = flags & IPSET_FLAG_EXIST; - bool with_timeout = set->flags & IP_SET_FLAG_TIMEOUT; - uint32_t ip, timeout = map->timeout; - unsigned char *ether = NULL; + struct ipmac data; + u32 timeout = map->timeout; int ret = 0; if (nla_parse(tb, IPSET_ATTR_ADT_MAX, head, len, @@ -233,35 +387,31 @@ bitmap_ipmac_uadt(struct ip_set *set, struct nlattr *head, int len, return -IPSET_ERR_PROTOCOL; if (tb[IPSET_ATTR_IP]) - ip = ip_set_get_h32(tb[IPSET_ATTR_IP]); + data.id = ip_set_get_h32(tb[IPSET_ATTR_IP]); else return -IPSET_ERR_PROTOCOL; - if (ip < map->first_ip || ip > map->last_ip) + if (data.id < map->first_ip || data.id > map->last_ip) return -IPSET_ERR_BITMAP_RANGE; if (tb[IPSET_ATTR_ETHER]) - ether = nla_data(tb[IPSET_ATTR_ETHER]); + data.ether = nla_data(tb[IPSET_ATTR_ETHER]); + else + data.ether = NULL; if (tb[IPSET_ATTR_TIMEOUT]) { - if (!with_timeout) + if (!with_timeout(map->timeout)) return -IPSET_ERR_TIMEOUT; - timeout = ip_set_get_h32(tb[IPSET_ATTR_TIMEOUT]); + timeout = ip_set_timeout_uget(tb[IPSET_ATTR_TIMEOUT]); } - ip -= map->first_ip; - - if (adt == IPSET_TEST) - return bitmap_ipmac_test(map, with_timeout, ip, ether); + data.id -= map->first_ip; - ret = adt == IPSET_ADD ? bitmap_ipmac_add(map, with_timeout, - ip, ether, timeout) - : bitmap_ipmac_del(map, with_timeout, ip); + ret = adtfn(set, &data, GFP_KERNEL, timeout); if (ret && !(ret == -IPSET_ERR_EXIST && eexist)) { if (tb[IPSET_ATTR_LINENO]) *lineno = nla_get_u32(tb[IPSET_ATTR_LINENO]); - return ret; } return ret; } @@ -271,10 +421,8 @@ bitmap_ipmac_destroy(struct ip_set *set) { struct bitmap_ipmac *map = set->data; - /* gc might be running: del_timer_sync can't be used */ - if (set->flags & IP_SET_FLAG_TIMEOUT) - while (!del_timer(&map->gc)) - msleep(IPSET_DESTROY_TIMER_SLEEP); + if (with_timeout(map->timeout)) + del_timer_sync(&map->gc); ip_set_free(map->members, set->flags); kfree(map); @@ -288,7 +436,7 @@ bitmap_ipmac_flush(struct ip_set *set) struct bitmap_ipmac *map = set->data; memset(map->members, 0, - (map->last_ip - map->first_ip + 1) * map->elem_size); + (map->last_ip - map->first_ip + 1) * map->dsize); } static int @@ -296,28 +444,18 @@ bitmap_ipmac_head(struct ip_set *set, struct sk_buff *skb) { const struct bitmap_ipmac *map = set->data; struct nlattr *nested; - const struct ipmac *elem; - uint32_t id, elements = 0, last = map->last_ip - map->first_ip; - bool with_timeout = set->flags & IP_SET_FLAG_TIMEOUT; - - for (id = 0; id <= last; id++) { - elem = bitmap_ipmac_elem(map, id); - if (bitmap_ipmac_exist(elem, with_timeout)) - elements++; - } nested = ipset_nest_start(skb, IPSET_ATTR_DATA); if (!nested) goto nla_put_failure; NLA_PUT_NET32(skb, IPSET_ATTR_IP, htonl(map->first_ip)); NLA_PUT_NET32(skb, IPSET_ATTR_IP_TO, htonl(map->last_ip)); - NLA_PUT_NET32(skb, IPSET_ATTR_ELEMENTS, htonl(elements)); NLA_PUT_NET32(skb, IPSET_ATTR_REFERENCES, htonl(atomic_read(&set->ref) - 1)); NLA_PUT_NET32(skb, IPSET_ATTR_MEMSIZE, - htonl((map->last_ip - map->first_ip + 1) - * map->elem_size)); - if (with_timeout) + htonl(sizeof(*map) + + (map->last_ip - map->first_ip + 1) * map->dsize)); + if (with_timeout(map->timeout)) NLA_PUT_NET32(skb, IPSET_ATTR_TIMEOUT, htonl(map->timeout)); ipset_nest_end(skb, nested); @@ -326,77 +464,54 @@ nla_put_failure: return -EFAULT; } -static int -bitmap_ipmac_list(struct ip_set *set, - struct sk_buff *skb, struct netlink_callback *cb) +static bool +bitmap_ipmac_same_set(const struct ip_set *a, const struct ip_set *b) { - const struct bitmap_ipmac *map = set->data; - const struct ipmac *elem; - struct nlattr *atd, *nested; - uint32_t id, first = cb->args[2]; - uint32_t last = map->last_ip - map->first_ip; - bool with_timeout = set->flags & IP_SET_FLAG_TIMEOUT; - - atd = ipset_nest_start(skb, IPSET_ATTR_ADT); - if (!atd) - return -EFAULT; - for (; cb->args[2] <= last; cb->args[2]++) { - id = cb->args[2]; - elem = bitmap_ipmac_elem(map, id); - if (!bitmap_ipmac_exist(elem, with_timeout)) - continue; - nested = ipset_nest_start(skb, IPSET_ATTR_DATA); - if (!nested) { - if (id == first) { - nla_nest_cancel(skb, atd); - return -EFAULT; - } else - goto nla_put_failure; - } - NLA_PUT_NET32(skb, IPSET_ATTR_IP, - htonl(map->first_ip + id)); - if (elem->match == MAC_FILLED) - NLA_PUT(skb, IPSET_ATTR_ETHER, ETH_ALEN, - elem->ether); - if (with_timeout) { - const struct ipmac_timeout *e = - (const struct ipmac_timeout *)elem; - uint32_t timeout = e->match == MAC_UNSET ? e->timeout - : ip_set_timeout_get(e->timeout); + struct bitmap_ipmac *x = a->data; + struct bitmap_ipmac *y = b->data; - NLA_PUT_NET32(skb, IPSET_ATTR_TIMEOUT, - htonl(timeout)); - } - ipset_nest_end(skb, nested); - } - ipset_nest_end(skb, atd); - /* Set listing finished */ - cb->args[2] = 0; - - return 0; - -nla_put_failure: - nla_nest_cancel(skb, nested); - ipset_nest_end(skb, atd); - return 0; + return x->first_ip == y->first_ip + && x->last_ip == y->last_ip + && x->timeout == y->timeout; } const struct ip_set_type_variant bitmap_ipmac __read_mostly = { .kadt = bitmap_ipmac_kadt, .uadt = bitmap_ipmac_uadt, + .adt = { + [IPSET_ADD] = bitmap_ipmac_add, + [IPSET_DEL] = bitmap_ipmac_del, + [IPSET_TEST] = bitmap_ipmac_test, + }, .destroy = bitmap_ipmac_destroy, .flush = bitmap_ipmac_flush, .head = bitmap_ipmac_head, .list = bitmap_ipmac_list, + .same_set = bitmap_ipmac_same_set, +}; + +const struct ip_set_type_variant bitmap_tipmac __read_mostly = { + .kadt = bitmap_ipmac_kadt, + .uadt = bitmap_ipmac_uadt, + .adt = { + [IPSET_ADD] = bitmap_ipmac_tadd, + [IPSET_DEL] = bitmap_ipmac_tdel, + [IPSET_TEST] = bitmap_ipmac_ttest, + }, + .destroy = bitmap_ipmac_destroy, + .flush = bitmap_ipmac_flush, + .head = bitmap_ipmac_head, + .list = bitmap_ipmac_tlist, + .same_set = bitmap_ipmac_same_set, }; static void -bitmap_ipmac_timeout_gc(unsigned long ul_set) +bitmap_ipmac_gc(unsigned long ul_set) { struct ip_set *set = (struct ip_set *) ul_set; struct bitmap_ipmac *map = set->data; - struct ipmac_timeout *elem; - uint32_t id, last = map->last_ip - map->first_ip; + struct ipmac_telem *elem; + u32 id, last = map->last_ip - map->first_ip; /* We run parallel with other readers (test element) * but adding/deleting new entries is locked out */ @@ -414,13 +529,13 @@ bitmap_ipmac_timeout_gc(unsigned long ul_set) } static inline void -bitmap_ipmac_timeout_gc_init(struct ip_set *set) +bitmap_ipmac_gc_init(struct ip_set *set) { struct bitmap_ipmac *map = set->data; init_timer(&map->gc); map->gc.data = (unsigned long) set; - map->gc.function = bitmap_ipmac_timeout_gc; + map->gc.function = bitmap_ipmac_gc; map->gc.expires = jiffies + IPSET_GC_PERIOD(map->timeout) * HZ; add_timer(&map->gc); } @@ -436,14 +551,15 @@ bitmap_ipmac_create_policy[IPSET_ATTR_CREATE_MAX+1] __read_mostly = { static bool init_map_ipmac(struct ip_set *set, struct bitmap_ipmac *map, - uint32_t first_ip, uint32_t last_ip) + u32 first_ip, u32 last_ip) { - map->members = ip_set_alloc((last_ip - first_ip + 1) * map->elem_size, + map->members = ip_set_alloc((last_ip - first_ip + 1) * map->dsize, GFP_KERNEL, &set->flags); if (!map->members) return false; map->first_ip = first_ip; map->last_ip = last_ip; + map->timeout = IPSET_NO_TIMEOUT; set->data = map; set->family = AF_INET; @@ -453,10 +569,10 @@ init_map_ipmac(struct ip_set *set, struct bitmap_ipmac *map, static int bitmap_ipmac_create(struct ip_set *set, struct nlattr *head, int len, - uint32_t flags) + u32 flags) { struct nlattr *tb[IPSET_ATTR_CREATE_MAX]; - uint32_t first_ip, last_ip, elements; + u32 first_ip, last_ip, elements; struct bitmap_ipmac *map; if (nla_parse(tb, IPSET_ATTR_CREATE_MAX, head, len, @@ -471,13 +587,13 @@ bitmap_ipmac_create(struct ip_set *set, struct nlattr *head, int len, if (tb[IPSET_ATTR_IP_TO]) { last_ip = ip_set_get_h32(tb[IPSET_ATTR_IP_TO]); if (first_ip > last_ip) { - uint32_t tmp = first_ip; + u32 tmp = first_ip; first_ip = last_ip; last_ip = tmp; } } else if (tb[IPSET_ATTR_CIDR]) { - uint8_t cidr = nla_get_u8(tb[IPSET_ATTR_CIDR]); + u8 cidr = nla_get_u8(tb[IPSET_ATTR_CIDR]); if (cidr >= 32) return -IPSET_ERR_INVALID_CIDR; @@ -490,31 +606,32 @@ bitmap_ipmac_create(struct ip_set *set, struct nlattr *head, int len, if (elements > IPSET_BITMAP_MAX_RANGE + 1) return -IPSET_ERR_BITMAP_RANGE_SIZE; - set->variant = &bitmap_ipmac; - map = kzalloc(sizeof(*map), GFP_KERNEL); if (!map) return -ENOMEM; if (tb[IPSET_ATTR_TIMEOUT]) { - map->elem_size = sizeof(struct ipmac_timeout); + map->dsize = sizeof(struct ipmac_telem); if (!init_map_ipmac(set, map, first_ip, last_ip)) { kfree(map); return -ENOMEM; } - map->timeout = ip_set_get_h32(tb[IPSET_ATTR_TIMEOUT]); - set->flags |= IP_SET_FLAG_TIMEOUT; + map->timeout = ip_set_timeout_uget(tb[IPSET_ATTR_TIMEOUT]); - bitmap_ipmac_timeout_gc_init(set); + set->variant = &bitmap_tipmac; + + bitmap_ipmac_gc_init(set); } else { - map->elem_size = sizeof(struct ipmac); + map->dsize = sizeof(struct ipmac_elem); if (!init_map_ipmac(set, map, first_ip, last_ip)) { kfree(map); return -ENOMEM; } + set->variant = &bitmap_ipmac; + } return 0; } @@ -522,7 +639,8 @@ bitmap_ipmac_create(struct ip_set *set, struct nlattr *head, int len, struct ip_set_type bitmap_ipmac_type = { .name = "bitmap:ip,mac", .protocol = IPSET_PROTOCOL, - .features = IPSET_TYPE_IP, + .features = IPSET_TYPE_IP | IPSET_TYPE_MAC, + .dimension = IPSET_DIM_TWO, .family = AF_INET, .revision = 0, .create = bitmap_ipmac_create, diff --git a/kernel/ip_set_bitmap_port.c b/kernel/ip_set_bitmap_port.c index 3afd031..f3e498a 100644 --- a/kernel/ip_set_bitmap_port.c +++ b/kernel/ip_set_bitmap_port.c @@ -7,6 +7,7 @@ /* Kernel module implementing an IP set type: the bitmap:port type */ +#include #include #include #include @@ -17,7 +18,6 @@ #include #include #include -#include #include #include #include @@ -38,19 +38,19 @@ MODULE_ALIAS("ip_set_bitmap:port"); struct bitmap_port { void *members; /* the set members */ - uint16_t first_port; /* host byte order, included in range */ - uint16_t last_port; /* host byte order, included in range */ + u16 first_port; /* host byte order, included in range */ + u16 last_port; /* host byte order, included in range */ size_t memsize; /* members size */ }; static inline int -bitmap_port_test(const struct bitmap_port *map, uint16_t id) +bitmap_port_test(const struct bitmap_port *map, u16 id) { return !!test_bit(id, map->members); } static inline int -bitmap_port_add(struct bitmap_port *map, uint16_t id) +bitmap_port_add(struct bitmap_port *map, u16 id) { if (test_and_set_bit(id, map->members)) return -IPSET_ERR_EXIST; @@ -59,7 +59,7 @@ bitmap_port_add(struct bitmap_port *map, uint16_t id) } static int -bitmap_port_del(struct bitmap_port *map, uint16_t id) +bitmap_port_del(struct bitmap_port *map, u16 id) { if (!test_and_clear_bit(id, map->members)) return -IPSET_ERR_EXIST; @@ -69,13 +69,13 @@ bitmap_port_del(struct bitmap_port *map, uint16_t id) static int bitmap_port_kadt(struct ip_set *set, const struct sk_buff *skb, - enum ipset_adt adt, uint8_t pf, const uint8_t *flags) + enum ipset_adt adt, u8 pf, u8 dim, u8 flags) { struct bitmap_port *map = set->data; - uint32_t port = get_port(pf, skb, flags); - - if (port == IPSET_INVALID_PORT) - return 0; + u16 port; + + if (!get_port(pf, skb, flags & IPSET_DIM_ONE_SRC, &port)) + return -EINVAL; port = ntohs(port); @@ -105,13 +105,13 @@ bitmap_port_adt_policy[IPSET_ATTR_ADT_MAX+1] __read_mostly = { static int bitmap_port_uadt(struct ip_set *set, struct nlattr *head, int len, - enum ipset_adt adt, uint32_t *lineno, uint32_t flags) + enum ipset_adt adt, u32 *lineno, u32 flags) { struct bitmap_port *map = set->data; struct nlattr *tb[IPSET_ATTR_ADT_MAX]; bool eexist = flags & IPSET_FLAG_EXIST; - uint32_t port; - uint16_t id, port_to; + u32 port; /* wraparound */ + u16 id, port_to; int ret = 0; if (nla_parse(tb, IPSET_ATTR_ADT_MAX, head, len, @@ -183,22 +183,16 @@ bitmap_port_head(struct ip_set *set, struct sk_buff *skb) { struct bitmap_port *map = set->data; struct nlattr *nested; - uint32_t id; - uint16_t elements, last = map->last_port - map->first_port; - - for (id = 0, elements = 0; id <= last; id++) - if (test_bit(id, map->members)) - elements++; nested = ipset_nest_start(skb, IPSET_ATTR_DATA); if (!nested) goto nla_put_failure; NLA_PUT_NET16(skb, IPSET_ATTR_PORT, htons(map->first_port)); NLA_PUT_NET16(skb, IPSET_ATTR_PORT_TO, htons(map->last_port)); - NLA_PUT_NET32(skb, IPSET_ATTR_ELEMENTS, htonl(elements)); NLA_PUT_NET32(skb, IPSET_ATTR_REFERENCES, htonl(atomic_read(&set->ref) - 1)); - NLA_PUT_NET32(skb, IPSET_ATTR_MEMSIZE, htonl(map->memsize)); + NLA_PUT_NET32(skb, IPSET_ATTR_MEMSIZE, + htonl(sizeof(*map) + map->memsize)); ipset_nest_end(skb, nested); return 0; @@ -212,8 +206,8 @@ bitmap_port_list(struct ip_set *set, { struct bitmap_port *map = set->data; struct nlattr *atd, *nested; - uint16_t id, first = cb->args[2]; - uint16_t last = map->last_port - map->first_port; + u16 id, first = cb->args[2]; + u16 last = map->last_port - map->first_port; atd = ipset_nest_start(skb, IPSET_ATTR_ADT); if (!atd) @@ -246,6 +240,16 @@ nla_put_failure: return 0; } +static bool +bitmap_port_same_set(const struct ip_set *a, const struct ip_set *b) +{ + struct bitmap_port *x = a->data; + struct bitmap_port *y = b->data; + + return x->first_port == y->first_port + && x->last_port == y->last_port; +} + const struct ip_set_type_variant bitmap_port __read_mostly = { .kadt = bitmap_port_kadt, .uadt = bitmap_port_uadt, @@ -253,22 +257,23 @@ const struct ip_set_type_variant bitmap_port __read_mostly = { .flush = bitmap_port_flush, .head = bitmap_port_head, .list = bitmap_port_list, + .same_set = bitmap_port_same_set, }; /* Timeout variant */ struct bitmap_port_timeout { void *members; /* the set members */ - uint16_t first_port; /* host byte order, included in range */ - uint16_t last_port; /* host byte order, included in range */ + u16 first_port; /* host byte order, included in range */ + u16 last_port; /* host byte order, included in range */ size_t memsize; /* members size */ - uint32_t timeout; /* timeout parameter */ + u32 timeout; /* timeout parameter */ struct timer_list gc; /* garbage collection */ }; static inline bool -bitmap_port_timeout_test(const struct bitmap_port_timeout *map, uint16_t id) +bitmap_port_timeout_test(const struct bitmap_port_timeout *map, u16 id) { unsigned long *timeout = map->members; @@ -277,7 +282,7 @@ bitmap_port_timeout_test(const struct bitmap_port_timeout *map, uint16_t id) static int bitmap_port_timeout_add(const struct bitmap_port_timeout *map, - uint16_t id, uint32_t timeout) + u16 id, u32 timeout) { unsigned long *table = map->members; @@ -291,7 +296,7 @@ bitmap_port_timeout_add(const struct bitmap_port_timeout *map, static int bitmap_port_timeout_del(const struct bitmap_port_timeout *map, - uint16_t id) + u16 id) { unsigned long *table = map->members; int ret = -IPSET_ERR_EXIST; @@ -305,13 +310,13 @@ bitmap_port_timeout_del(const struct bitmap_port_timeout *map, static int bitmap_port_timeout_kadt(struct ip_set *set, const struct sk_buff *skb, - enum ipset_adt adt, uint8_t pf, const uint8_t *flags) + enum ipset_adt adt, u8 pf, u8 dim, u8 flags) { struct bitmap_port_timeout *map = set->data; - uint32_t port = get_port(pf, skb, flags); - - if (port == IPSET_INVALID_PORT) - return 0; + u16 port; + + if (!get_port(pf, skb, flags & IPSET_DIM_ONE_SRC, &port)) + return -EINVAL; port = ntohs(port); @@ -334,13 +339,13 @@ bitmap_port_timeout_kadt(struct ip_set *set, const struct sk_buff *skb, static int bitmap_port_timeout_uadt(struct ip_set *set, struct nlattr *head, int len, - enum ipset_adt adt, uint32_t *lineno, uint32_t flags) + enum ipset_adt adt, u32 *lineno, u32 flags) { const struct bitmap_port_timeout *map = set->data; struct nlattr *tb[IPSET_ATTR_ADT_MAX]; bool eexist = flags & IPSET_FLAG_EXIST; - uint16_t port_to, id; - uint32_t port, timeout = map->timeout; + u16 id, port_to; + u32 port, timeout = map->timeout; /* wraparound */ int ret = 0; if (nla_parse(tb, IPSET_ATTR_ADT_MAX, head, len, @@ -372,7 +377,7 @@ bitmap_port_timeout_uadt(struct ip_set *set, struct nlattr *head, int len, return -IPSET_ERR_BITMAP_RANGE; if (tb[IPSET_ATTR_TIMEOUT]) - timeout = ip_set_get_h32(tb[IPSET_ATTR_TIMEOUT]); + timeout = ip_set_timeout_uget(tb[IPSET_ATTR_TIMEOUT]); for (; port <= port_to; port++) { id = port - map->first_port; @@ -394,10 +399,7 @@ bitmap_port_timeout_destroy(struct ip_set *set) { struct bitmap_port_timeout *map = set->data; - /* gc might be running: del_timer_sync can't be used */ - while (!del_timer(&map->gc)) - msleep(IPSET_DESTROY_TIMER_SLEEP); - + del_timer_sync(&map->gc); ip_set_free(map->members, set->flags); kfree(map); @@ -417,23 +419,17 @@ bitmap_port_timeout_head(struct ip_set *set, struct sk_buff *skb) { struct bitmap_port_timeout *map = set->data; struct nlattr *nested; - uint32_t id; - uint16_t elements, last = map->last_port - map->first_port; - - for (id = 0, elements = 0; id <= last; id++) - if (bitmap_port_timeout_test(map, id)) - elements++; - + nested = ipset_nest_start(skb, IPSET_ATTR_DATA); if (!nested) goto nla_put_failure; NLA_PUT_NET16(skb, IPSET_ATTR_PORT, htons(map->first_port)); NLA_PUT_NET16(skb, IPSET_ATTR_PORT_TO, htons(map->last_port)); NLA_PUT_NET32(skb, IPSET_ATTR_TIMEOUT , htonl(map->timeout)); - NLA_PUT_NET32(skb, IPSET_ATTR_ELEMENTS, htonl(elements)); NLA_PUT_NET32(skb, IPSET_ATTR_REFERENCES, htonl(atomic_read(&set->ref) - 1)); - NLA_PUT_NET32(skb, IPSET_ATTR_MEMSIZE, htonl(map->memsize)); + NLA_PUT_NET32(skb, IPSET_ATTR_MEMSIZE, + htonl(sizeof(*map) + map->memsize)); ipset_nest_end(skb, nested); return 0; @@ -447,8 +443,8 @@ bitmap_port_timeout_list(struct ip_set *set, { struct bitmap_port_timeout *map = set->data; struct nlattr *adt, *nested; - uint16_t id, first = cb->args[2]; - uint16_t last = map->last_port - map->first_port; + u16 id, first = cb->args[2]; + u16 last = map->last_port - map->first_port; unsigned long *table = map->members; adt = ipset_nest_start(skb, IPSET_ATTR_ADT); @@ -485,6 +481,17 @@ nla_put_failure: return 0; } +static bool +bitmap_port_timeout_same_set(const struct ip_set *a, const struct ip_set *b) +{ + struct bitmap_port_timeout *x = a->data; + struct bitmap_port_timeout *y = b->data; + + return x->first_port == y->first_port + && x->last_port == y->last_port + && x->timeout == y->timeout; +} + const struct ip_set_type_variant bitmap_port_timeout __read_mostly = { .kadt = bitmap_port_timeout_kadt, .uadt = bitmap_port_timeout_uadt, @@ -492,15 +499,17 @@ const struct ip_set_type_variant bitmap_port_timeout __read_mostly = { .flush = bitmap_port_timeout_flush, .head = bitmap_port_timeout_head, .list = bitmap_port_timeout_list, + .same_set = bitmap_port_timeout_same_set, }; static void -bitmap_port_timeout_gc(unsigned long ul_set) +bitmap_port_gc(unsigned long ul_set) { struct ip_set *set = (struct ip_set *) ul_set; struct bitmap_port_timeout *map = set->data; unsigned long *table = map->members; - uint16_t id, last = map->last_port - map->first_port; + u32 id; /* wraparound */ + u16 last = map->last_port - map->first_port; /* We run parallel with other readers (test element) * but adding/deleting new entries is locked out */ @@ -515,13 +524,13 @@ bitmap_port_timeout_gc(unsigned long ul_set) } static inline void -bitmap_port_timeout_gc_init(struct ip_set *set) +bitmap_port_gc_init(struct ip_set *set) { struct bitmap_port_timeout *map = set->data; init_timer(&map->gc); map->gc.data = (unsigned long) set; - map->gc.function = bitmap_port_timeout_gc; + map->gc.function = bitmap_port_gc; map->gc.expires = jiffies + IPSET_GC_PERIOD(map->timeout) * HZ; add_timer(&map->gc); } @@ -537,7 +546,7 @@ bitmap_port_create_policy[IPSET_ATTR_CREATE_MAX+1] __read_mostly = { static bool init_map_port(struct ip_set *set, struct bitmap_port *map, - uint16_t first_port, uint16_t last_port) + u16 first_port, u16 last_port) { map->members = ip_set_alloc(map->memsize, GFP_KERNEL, &set->flags); if (!map->members) @@ -553,10 +562,10 @@ init_map_port(struct ip_set *set, struct bitmap_port *map, static int bitmap_port_create(struct ip_set *set, struct nlattr *head, int len, - uint32_t flags) + u32 flags) { struct nlattr *tb[IPSET_ATTR_CREATE_MAX]; - uint16_t first_port, last_port; + u16 first_port, last_port; if (nla_parse(tb, IPSET_ATTR_CREATE_MAX, head, len, bitmap_port_create_policy)) @@ -570,7 +579,7 @@ bitmap_port_create(struct ip_set *set, struct nlattr *head, int len, if (tb[IPSET_ATTR_PORT_TO]) { last_port = ip_set_get_h16(tb[IPSET_ATTR_PORT_TO]); if (first_port > last_port) { - uint16_t tmp = first_port; + u16 tmp = first_port; first_port = last_port; last_port = tmp; @@ -594,11 +603,10 @@ bitmap_port_create(struct ip_set *set, struct nlattr *head, int len, return -ENOMEM; } - map->timeout = ip_set_get_h32(tb[IPSET_ATTR_TIMEOUT]); - set->flags |= IP_SET_FLAG_TIMEOUT; + map->timeout = ip_set_timeout_uget(tb[IPSET_ATTR_TIMEOUT]); set->variant = &bitmap_port_timeout; - bitmap_port_timeout_gc_init(set); + bitmap_port_gc_init(set); } else { struct bitmap_port *map; @@ -607,7 +615,7 @@ bitmap_port_create(struct ip_set *set, struct nlattr *head, int len, return -ENOMEM; map->memsize = bitmap_bytes(0, last_port - first_port); - D("memsize: %zu", map->memsize); + pr_debug("memsize: %zu", map->memsize); if (!init_map_port(set, map, first_port, last_port)) { kfree(map); return -ENOMEM; @@ -622,6 +630,7 @@ struct ip_set_type bitmap_port_type = { .name = "bitmap:port", .protocol = IPSET_PROTOCOL, .features = IPSET_TYPE_PORT, + .dimension = IPSET_DIM_ONE, .family = AF_UNSPEC, .revision = 0, .create = bitmap_port_create, diff --git a/kernel/ip_set_hash_ip.c b/kernel/ip_set_hash_ip.c index d99c99b..e5ce6a4 100644 --- a/kernel/ip_set_hash_ip.c +++ b/kernel/ip_set_hash_ip.c @@ -7,6 +7,8 @@ /* Kernel module implementing an IP set type: the hash:ip type */ +#include +#include #include #include #include @@ -19,6 +21,7 @@ #include #include #include +#include #include #include @@ -30,213 +33,125 @@ MODULE_AUTHOR("Jozsef Kadlecsik "); MODULE_DESCRIPTION("hash:ip type of IP sets"); MODULE_ALIAS("ip_set_hash:ip"); -/* Member elements without timeout */ -struct ip4_elem { - uint32_t ip; -}; +/* Type specific function prefix */ +#define TYPE hash_ip -struct ip6_elem { - union nf_inet_addr ip; -}; +static bool +hash_ip_same_set(const struct ip_set *a, const struct ip_set *b); -/* Member elements with timeout support */ -struct ip4_elem_timeout { - uint32_t ip; - unsigned long timeout; -}; +#define hash_ip4_same_set hash_ip_same_set +#define hash_ip6_same_set hash_ip_same_set -struct ip6_elem_timeout { - union nf_inet_addr ip; - unsigned long timeout; -}; +/* The type variant functions: IPv4 */ -/* The hash:ip type structure */ -struct hash_ip { - void *members; /* the set members */ - uint32_t hashsize; /* hash size */ - uint32_t maxelem; /* max number of elements/hashsize */ - uint8_t probes; /* max number of probes */ - uint8_t resize; /* resize factor in percent */ - uint8_t netmask; /* netmask */ - uint32_t timeout; /* timeout value */ - uint32_t elements; /* number of elements */ - struct timer_list gc; /* garbage collector */ - size_t elem_size; /* size of element */ - initval_t initval[0]; /* initvals for jhash_1word */ +/* Member elements without timeout */ +struct hash_ip4_elem { + u32 ip; }; -static inline void * -hash_ip_elem(const struct hash_ip *map, uint32_t id) -{ - return (void *)((char *)map->members + id * map->elem_size); -} - -static inline unsigned long -get_ip4_elem_timeout(const struct ip4_elem *elem) -{ - return ((const struct ip4_elem_timeout *)elem)->timeout; -} - -static inline unsigned long -get_ip6_elem_timeout(const struct ip6_elem *elem) -{ - return ((const struct ip6_elem_timeout *)elem)->timeout; -} - -static inline uint32_t -ip4_hash(struct ip4_elem *elem, initval_t initval, uint32_t hashsize) -{ - return jhash_1word(elem->ip, initval) % hashsize; -} - -static inline uint32_t -ip6_hash(struct ip6_elem *elem, initval_t initval, uint32_t hashsize) -{ - return jhash2((u32 *)&elem->ip, 4, initval) % hashsize; -} +/* Member elements with timeout support */ +struct hash_ip4_telem { + u32 ip; + unsigned long timeout; +}; static inline bool -ip4_cmp(struct ip4_elem *ip1, struct ip4_elem *ip2) +hash_ip4_data_equal(const struct hash_ip4_elem *ip1, + const struct hash_ip4_elem *ip2) { return ip1->ip == ip2->ip; } static inline bool -ip6_cmp(struct ip6_elem *ip1, struct ip6_elem *ip2) -{ - return ipv6_addr_cmp(&ip1->ip.in6, &ip2->ip.in6); -} - -static inline bool -ip4_null(struct ip4_elem *elem) +hash_ip4_data_isnull(const struct hash_ip4_elem *elem) { return elem->ip == 0; } -static inline bool -ip6_null(struct ip6_elem *elem) -{ - return ipv6_addr_any(&elem->ip.in6); -} - static inline void -ip4_cpy(struct ip4_elem *dst, const struct ip4_elem *src) +hash_ip4_data_copy(struct hash_ip4_elem *dst, const struct hash_ip4_elem *src) { dst->ip = src->ip; } static inline void -ip6_cpy(struct ip6_elem *dst, const struct ip6_elem *src) +hash_ip4_data_swap(struct hash_ip4_elem *dst, struct hash_ip4_elem *src) { - ipv6_addr_copy(&dst->ip.in6, &src->ip.in6); + swap(dst->ip, src->ip); } -/* Zero valued IP addresses (network order) cannot be stored */ +/* Zero valued IP addresses cannot be stored */ static inline void -ip4_zero_out(struct ip4_elem *elem) +hash_ip4_data_zero_out(struct hash_ip4_elem *elem) { elem->ip = 0; } -static inline void -ip6_zero_out(struct ip6_elem *elem) +static inline bool +hash_ip4_data_list(struct sk_buff *skb, const struct hash_ip4_elem *data) { - ipv6_addr_set(&elem->ip.in6, 0, 0, 0, 0); -} + NLA_PUT_NET32(skb, IPSET_ATTR_IP, data->ip); + return 0; -static inline void -ip6_netmask(union nf_inet_addr *ip, uint8_t prefix) -{ - ip->ip6[0] &= NETMASK6(prefix)[0]; - ip->ip6[1] &= NETMASK6(prefix)[1]; - ip->ip6[2] &= NETMASK6(prefix)[2]; - ip->ip6[3] &= NETMASK6(prefix)[3]; +nla_put_failure: + return 1; } -/* The type variant functions: generic ones */ - -static void -hash_ip_destroy(struct ip_set *set) +static inline bool +hash_ip4_data_tlist(struct sk_buff *skb, const struct hash_ip4_elem *data) { - struct hash_ip *map = set->data; + const struct hash_ip4_telem *tdata = + (const struct hash_ip4_telem *)data; - /* gc might be running: del_timer_sync can't be used */ - if (set->flags & IP_SET_FLAG_TIMEOUT) - while (!del_timer(&map->gc)) - msleep(IPSET_DESTROY_TIMER_SLEEP); - - ip_set_free(map->members, set->flags); - kfree(map); - - set->data = NULL; -} + NLA_PUT_NET32(skb, IPSET_ATTR_IP, tdata->ip); + NLA_PUT_NET32(skb, IPSET_ATTR_TIMEOUT, + htonl(ip_set_timeout_get(tdata->timeout))); -#define hash_ip4_destroy hash_ip_destroy -#define hash_ip6_destroy hash_ip_destroy + return 0; -static void -hash_ip_flush(struct ip_set *set) -{ - struct hash_ip *map = set->data; - - memset(map->members, 0, map->hashsize * map->elem_size); - map->elements = 0; +nla_put_failure: + return 1; } -#define hash_ip4_flush hash_ip_flush -#define hash_ip6_flush hash_ip_flush - -/* IPv4 variant */ - -#define PF 4 -#include "ip_set_hash_ip_src.c" -#undef PF +#define IP_SET_HASH_WITH_NETMASK +#define PF 4 +#define HOST_MASK 32 +#include static int hash_ip4_kadt(struct ip_set *set, const struct sk_buff *skb, - enum ipset_adt adt, uint8_t pf, const uint8_t *flags) + enum ipset_adt adt, u8 pf, u8 dim, u8 flags) { - struct hash_ip *map = set->data; - bool with_timeout = set->flags & IP_SET_FLAG_TIMEOUT; - uint32_t ip; + struct chash *h = set->data; + ipset_adtfn adtfn = set->variant->adt[adt]; + u32 ip; - if (pf != AF_INET) - return -EINVAL; - - ip4addrptr(skb, flags, &ip); - ip &= NETMASK(map->netmask); + ip4addrptr(skb, flags & IPSET_DIM_ONE_SRC, &ip); + ip &= NETMASK(h->netmask); if (ip == 0) return -EINVAL; - switch (adt) { - case IPSET_TEST: - return hash_ip4_test(map, with_timeout, - (struct ip4_elem *)&ip); - case IPSET_ADD: - return hash_ip4_add(map, with_timeout, - (struct ip4_elem *)&ip, map->timeout); - case IPSET_DEL: - return hash_ip4_del(map, with_timeout, (struct ip4_elem *)&ip); - default: - BUG(); - } - return 0; + return adtfn(set, &ip, GFP_ATOMIC, h->timeout); } static const struct nla_policy hash_ip4_adt_policy[IPSET_ATTR_ADT_MAX + 1] __read_mostly = { [IPSET_ATTR_IP] = { .type = NLA_U32 }, + [IPSET_ATTR_IP_TO] = { .type = NLA_U32 }, + [IPSET_ATTR_CIDR] = { .type = NLA_U8 }, [IPSET_ATTR_TIMEOUT] = { .type = NLA_U32 }, }; static int hash_ip4_uadt(struct ip_set *set, struct nlattr *head, int len, - enum ipset_adt adt, uint32_t *lineno, uint32_t flags) + enum ipset_adt adt, u32 *lineno, u32 flags) { - struct hash_ip *map = set->data; + struct chash *h = set->data; struct nlattr *tb[IPSET_ATTR_ADT_MAX]; - bool with_timeout = set->flags & IP_SET_FLAG_TIMEOUT; - uint32_t ip, timeout = map->timeout; + bool eexist = flags & IPSET_FLAG_EXIST; + ipset_adtfn adtfn = set->variant->adt[adt]; + u32 ip, nip, ip_to, hosts, timeout = h->timeout; + int ret = 0; if (nla_parse(tb, IPSET_ATTR_ADT_MAX, head, len, hash_ip4_adt_policy)) @@ -247,69 +162,164 @@ hash_ip4_uadt(struct ip_set *set, struct nlattr *head, int len, else return -IPSET_ERR_PROTOCOL; - ip &= NETMASK(map->netmask); + ip &= NETMASK(h->netmask); if (ip == 0) return -IPSET_ERR_HASH_ELEM; if (tb[IPSET_ATTR_TIMEOUT]) { - if (!with_timeout) + if (!with_timeout(h->timeout)) return -IPSET_ERR_TIMEOUT; - timeout = ip_set_get_h32(tb[IPSET_ATTR_TIMEOUT]); + timeout = ip_set_timeout_uget(tb[IPSET_ATTR_TIMEOUT]); } - switch (adt) { - case IPSET_TEST: - return hash_ip4_test(map, with_timeout, - (struct ip4_elem *)&ip); - case IPSET_ADD: - return hash_ip4_add(map, with_timeout, - (struct ip4_elem *)&ip, timeout); - case IPSET_DEL: - return hash_ip4_del(map, with_timeout, - (struct ip4_elem *)&ip); - default: - BUG(); + if (adt == IPSET_TEST) + return adtfn(set, &ip, GFP_KERNEL, timeout); + + ip = ntohl(ip); + if (tb[IPSET_ATTR_IP_TO]) { + ip_to = ip_set_get_h32(tb[IPSET_ATTR_IP_TO]); + if (ip > ip_to) + swap(ip, ip_to); + } else if (tb[IPSET_ATTR_CIDR]) { + u8 cidr = nla_get_u8(tb[IPSET_ATTR_CIDR]); + + if (cidr > 32) + return -IPSET_ERR_INVALID_CIDR; + ip &= HOSTMASK(cidr); + ip_to = ip | ~HOSTMASK(cidr); + } else + ip_to = ip; + + hosts = h->netmask == 32 ? 1 : 2 << (32 - h->netmask - 1); + + for (; !before(ip_to, ip); ip += hosts) { + nip = htonl(ip); + ret = adtfn(set, &nip, GFP_KERNEL, timeout); + + if (ret && !(ret == -IPSET_ERR_EXIST && eexist)) { + if (tb[IPSET_ATTR_LINENO]) + *lineno = nla_get_u32(tb[IPSET_ATTR_LINENO]); + return ret; + } } + return ret; +} + +static bool +hash_ip_same_set(const struct ip_set *a, const struct ip_set *b) +{ + struct chash *x = a->data; + struct chash *y = b->data; + + return x->maxelem == y->maxelem + && x->timeout == y->timeout + && x->htable_bits == y->htable_bits /* resizing ? */ + && x->array_size == y->array_size + && x->chain_limit == y->chain_limit + && x->netmask == y->netmask; +} + +/* The type variant functions: IPv6 */ + +struct hash_ip6_elem { + union nf_inet_addr ip; +}; +struct hash_ip6_telem { + union nf_inet_addr ip; + unsigned long timeout; +}; + +static inline bool +hash_ip6_data_equal(const struct hash_ip6_elem *ip1, + const struct hash_ip6_elem *ip2) +{ + return ipv6_addr_cmp(&ip1->ip.in6, &ip2->ip.in6) == 0; +} + +static inline bool +hash_ip6_data_isnull(const struct hash_ip6_elem *elem) +{ + return ipv6_addr_any(&elem->ip.in6); +} + +static inline void +hash_ip6_data_copy(struct hash_ip6_elem *dst, const struct hash_ip6_elem *src) +{ + ipv6_addr_copy(&dst->ip.in6, &src->ip.in6); +} + +static inline void +hash_ip6_data_swap(struct hash_ip6_elem *dst, struct hash_ip6_elem *src) +{ + struct in6_addr tmp; + + ipv6_addr_copy(&tmp, &dst->ip.in6); + ipv6_addr_copy(&dst->ip.in6, &src->ip.in6); + ipv6_addr_copy(&src->ip.in6, &tmp); +} + +static inline void +hash_ip6_data_zero_out(struct hash_ip6_elem *elem) +{ + ipv6_addr_set(&elem->ip.in6, 0, 0, 0, 0); +} + +static inline void +ip6_netmask(union nf_inet_addr *ip, u8 prefix) +{ + ip->ip6[0] &= NETMASK6(prefix)[0]; + ip->ip6[1] &= NETMASK6(prefix)[1]; + ip->ip6[2] &= NETMASK6(prefix)[2]; + ip->ip6[3] &= NETMASK6(prefix)[3]; +} + +static inline bool +hash_ip6_data_list(struct sk_buff *skb, const struct hash_ip6_elem *data) +{ + NLA_PUT(skb, IPSET_ATTR_IP, sizeof(struct in6_addr), &data->ip); return 0; + +nla_put_failure: + return 1; } -/* IPv6 variants */ +static inline bool +hash_ip6_data_tlist(struct sk_buff *skb, const struct hash_ip6_elem *data) +{ + const struct hash_ip6_telem *e = + (const struct hash_ip6_telem *)data; + + NLA_PUT(skb, IPSET_ATTR_IP, sizeof(struct in6_addr), &e->ip); + NLA_PUT_NET32(skb, IPSET_ATTR_TIMEOUT, + htonl(ip_set_timeout_get(e->timeout))); + return 0; + +nla_put_failure: + return 1; +} -#define PF 6 -#include "ip_set_hash_ip_src.c" #undef PF +#undef HOST_MASK + +#define PF 6 +#define HOST_MASK 128 +#include static int hash_ip6_kadt(struct ip_set *set, const struct sk_buff *skb, - enum ipset_adt adt, uint8_t pf, const uint8_t *flags) + enum ipset_adt adt, u8 pf, u8 dim, u8 flags) { - struct hash_ip *map = set->data; - bool with_timeout = set->flags & IP_SET_FLAG_TIMEOUT; + struct chash *h = set->data; + ipset_adtfn adtfn = set->variant->adt[adt]; union nf_inet_addr ip; - if (pf != AF_INET6) - return -EINVAL; - - ip6addrptr(skb, flags, &ip.in6); - ip6_netmask(&ip, map->netmask); + ip6addrptr(skb, flags & IPSET_DIM_ONE_SRC, &ip.in6); + ip6_netmask(&ip, h->netmask); if (ipv6_addr_any(&ip.in6)) return -EINVAL; - switch (adt) { - case IPSET_TEST: - return hash_ip6_test(map, with_timeout, - (struct ip6_elem *)&ip); - case IPSET_ADD: - return hash_ip6_add(map, with_timeout, - (struct ip6_elem *)&ip, map->timeout); - case IPSET_DEL: - return hash_ip6_del(map, with_timeout, - (struct ip6_elem *)&ip); - default: - BUG(); - } - return 0; + return adtfn(set, &ip, GFP_ATOMIC, h->timeout); } static const struct nla_policy @@ -321,13 +331,13 @@ hash_ip6_adt_policy[IPSET_ATTR_ADT_MAX + 1] __read_mostly = { static int hash_ip6_uadt(struct ip_set *set, struct nlattr *head, int len, - enum ipset_adt adt, uint32_t *lineno, uint32_t flags) + enum ipset_adt adt, u32 *lineno, u32 flags) { - struct hash_ip *map = set->data; + struct chash *h = set->data; struct nlattr *tb[IPSET_ATTR_ADT_MAX]; + ipset_adtfn adtfn = set->variant->adt[adt]; union nf_inet_addr *ip; - bool with_timeout = set->flags & IP_SET_FLAG_TIMEOUT; - uint32_t timeout = map->timeout; + u32 timeout = h->timeout; if (nla_parse(tb, IPSET_ATTR_ADT_MAX, head, len, hash_ip6_adt_policy)) @@ -338,31 +348,17 @@ hash_ip6_uadt(struct ip_set *set, struct nlattr *head, int len, else return -IPSET_ERR_PROTOCOL; - ip6_netmask(ip, map->netmask); + ip6_netmask(ip, h->netmask); if (ipv6_addr_any(&ip->in6)) return -IPSET_ERR_HASH_ELEM; if (tb[IPSET_ATTR_TIMEOUT]) { - if (!with_timeout) + if (!with_timeout(h->timeout)) return -IPSET_ERR_TIMEOUT; - timeout = ip_set_get_h32(tb[IPSET_ATTR_TIMEOUT]); + timeout = ip_set_timeout_uget(tb[IPSET_ATTR_TIMEOUT]); } - switch (adt) { - case IPSET_TEST: - return hash_ip6_test(map, with_timeout, - (struct ip6_elem *)ip); - case IPSET_ADD: - return hash_ip6_add(map, with_timeout, - (struct ip6_elem *)ip, timeout); - case IPSET_DEL: - return hash_ip6_del(map, with_timeout, - (struct ip6_elem *)ip); - default: - BUG(); - } - - return 0; + return adtfn(set, ip, GFP_KERNEL, timeout); } /* Create hash:ip type of sets */ @@ -374,114 +370,84 @@ hash_ip_create_policy[IPSET_ATTR_CREATE_MAX+1] __read_mostly = { [IPSET_ATTR_PROBES] = { .type = NLA_U8 }, [IPSET_ATTR_RESIZE] = { .type = NLA_U8 }, [IPSET_ATTR_TIMEOUT] = { .type = NLA_U32 }, + [IPSET_ATTR_NETMASK] = { .type = NLA_U8 }, }; -static bool -init_map_ip(struct ip_set *set, struct hash_ip *map, uint32_t maxelem, - uint32_t probes, uint32_t resize, uint8_t netmask, uint8_t family) -{ - map->members = ip_set_alloc(map->hashsize * map->elem_size, - GFP_KERNEL, &set->flags); - if (!map->members) - return false; - - map->maxelem = maxelem; - map->probes = probes; - map->resize = resize; - map->netmask = netmask; - - set->data = map; - set->family = family; - - return true; -} - static int -hash_ip_create(struct ip_set *set, struct nlattr *head, int len, - uint32_t flags) +hash_ip_create(struct ip_set *set, struct nlattr *head, int len, u32 flags) { struct nlattr *tb[IPSET_ATTR_CREATE_MAX]; - uint32_t hashsize, maxelem; - uint8_t probes, resize, netmask, family, i; - struct hash_ip *map; + u32 hashsize = IPSET_DEFAULT_HASHSIZE, maxelem = IPSET_DEFAULT_MAXELEM; + u8 netmask; + struct chash *h; + + if (!(set->family == AF_INET || set->family == AF_INET6)) + return -IPSET_ERR_INVALID_FAMILY; + netmask = set->family == AF_INET ? 32 : 128; + pr_debug("Create set %s with family %s", + set->name, set->family == AF_INET ? "inet" : "inet6"); if (nla_parse(tb, IPSET_ATTR_CREATE_MAX, head, len, hash_ip_create_policy)) return -IPSET_ERR_PROTOCOL; - hashsize = IPSET_DEFAULT_HASHSIZE; - maxelem = IPSET_DEFAULT_MAXELEM; - probes = IPSET_DEFAULT_PROBES; - resize = IPSET_DEFAULT_RESIZE; - family = AF_INET; - - if (tb[IPSET_ATTR_HASHSIZE]) + if (tb[IPSET_ATTR_HASHSIZE]) { hashsize = ip_set_get_h32(tb[IPSET_ATTR_HASHSIZE]); + if (hashsize < IPSET_MIMINAL_HASHSIZE) + hashsize = IPSET_MIMINAL_HASHSIZE; + } if (tb[IPSET_ATTR_MAXELEM]) maxelem = ip_set_get_h32(tb[IPSET_ATTR_MAXELEM]); - if (tb[IPSET_ATTR_PROBES]) - probes = nla_get_u8(tb[IPSET_ATTR_PROBES]); - - if (tb[IPSET_ATTR_RESIZE]) - resize = nla_get_u8(tb[IPSET_ATTR_RESIZE]); - - if (tb[IPSET_ATTR_FAMILY]) - family = nla_get_u8(tb[IPSET_ATTR_FAMILY]); - if (!(family == AF_INET || family == AF_INET6)) - return -IPSET_ERR_INVALID_FAMILY; - netmask = family == AF_INET ? 32 : 128; - if (tb[IPSET_ATTR_NETMASK]) { netmask = nla_get_u8(tb[IPSET_ATTR_NETMASK]); - if ((family == AF_INET && netmask > 32) - || (family == AF_INET6 && netmask > 128)) + if ((set->family == AF_INET && netmask > 32) + || (set->family == AF_INET6 && netmask > 128) + || netmask == 0) return -IPSET_ERR_INVALID_NETMASK; } - map = kzalloc(sizeof(*map) + probes * sizeof(initval_t), GFP_KERNEL); - if (!map) + h = kzalloc(sizeof(*h), GFP_KERNEL); + if (!h) return -ENOMEM; - - map->hashsize = hashsize; - if (tb[IPSET_ATTR_TIMEOUT]) { - map->elem_size = family == AF_INET - ? sizeof(struct ip4_elem_timeout) - : sizeof(struct ip6_elem_timeout); - - if (!init_map_ip(set, map, maxelem, probes, resize, netmask, - family)) { - kfree(map); - return -ENOMEM; - } - map->timeout = ip_set_get_h32(tb[IPSET_ATTR_TIMEOUT]); - set->flags |= IP_SET_FLAG_TIMEOUT; + h->maxelem = maxelem; + h->netmask = netmask; + h->htable_bits = htable_bits(hashsize); + h->array_size = CHASH_DEFAULT_ARRAY_SIZE; + h->chain_limit = CHASH_DEFAULT_CHAIN_LIMIT; + get_random_bytes(&h->initval, sizeof(h->initval)); + h->timeout = IPSET_NO_TIMEOUT; + + h->htable = ip_set_alloc(jhash_size(h->htable_bits) * sizeof(struct slist), + GFP_KERNEL, &set->flags); + if (!h->htable) { + kfree(h); + return -ENOMEM; + } + + set->data = h; + + if (tb[IPSET_ATTR_TIMEOUT]) { + h->timeout = ip_set_timeout_uget(tb[IPSET_ATTR_TIMEOUT]); - if (family == AF_INET) + set->variant = set->family == AF_INET + ? &hash_ip4_tvariant : &hash_ip6_tvariant; + + if (set->family == AF_INET) hash_ip4_gc_init(set); else hash_ip6_gc_init(set); } else { - map->elem_size = family == AF_INET - ? sizeof(struct ip4_elem) - : sizeof(struct ip6_elem); - - if (!init_map_ip(set, map, maxelem, probes, resize, netmask, - family)) { - kfree(map); - return -ENOMEM; - } + set->variant = set->family == AF_INET + ? &hash_ip4_variant : &hash_ip6_variant; } - for (i = 0; i < map->probes; i++) - get_random_bytes(((initval_t *) map->initval)+i, - sizeof(initval_t)); - set->variant = family == AF_INET ? &hash_ip4 : &hash_ip6; - D("create %s hashsize %u maxelem %u probes %u resize %u", - set->name, map->hashsize, map->maxelem, map->probes, map->resize); + pr_debug("create %s hashsize %u (%u) maxelem %u: %p(%p)", + set->name, jhash_size(h->htable_bits), + h->htable_bits, h->maxelem, set->data, h->htable); return 0; } @@ -490,6 +456,7 @@ static struct ip_set_type hash_ip_type = { .name = "hash:ip", .protocol = IPSET_PROTOCOL, .features = IPSET_TYPE_IP, + .dimension = IPSET_DIM_ONE, .family = AF_UNSPEC, .revision = 0, .create = hash_ip_create, diff --git a/kernel/ip_set_hash_ip_src.c b/kernel/ip_set_hash_ip_src.c deleted file mode 100644 index ef0a8ec..0000000 --- a/kernel/ip_set_hash_ip_src.c +++ /dev/null @@ -1,473 +0,0 @@ -/* Copyright (C) 2003-2010 Jozsef Kadlecsik - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - */ - -#define CONCAT(a, b, c) a##b##c -#define TOKEN(a, b, c) CONCAT(a, b, c) - -/* IPv4/IPv6 dependent function prototypes for hash:ip */ - -#if PF == 4 -#define HOST_MASK 32 -#define NLA_PUT_ADDR(skb, ip) \ - NLA_PUT_NET32(skb, IPSET_ATTR_IP, *(ip)); -#else -#define HOST_MASK 128 -#define NLA_PUT_ADDR(skb, ip) \ - NLA_PUT(skb, IPSET_ATTR_IP, sizeof(struct in6_addr), ip); -#endif - -#define hash_ip_pf_timeout TOKEN(hash_ip, PF, _timeout) -#define hash_ip_pf_expired TOKEN(hash_ip, PF, _expired) -#define hash_ip_pf_elem_test TOKEN(hash_ip, PF, _elem_test) -#define hash_ip_pf_elem_exist TOKEN(hash_ip, PF, _elem_exist) -#define hash_ip_pf_elem_expired TOKEN(hash_ip, PF, _elem_expired) -#define hash_ip_pf_test TOKEN(hash_ip, PF, _test) -#define hash_ip_pf_add TOKEN(hash_ip, PF, _add) -#define hash_ip_pf_readd TOKEN(hash_ip, PF, _readd) -#define hash_ip_pf_del TOKEN(hash_ip, PF, _del) -#define hash_ip_pf_map_expired TOKEN(hash_ip, PF, _map_expired) -#define hash_ip_pf_set_expired TOKEN(hash_ip, PF, _set_expired) -#define hash_ip_pf_head TOKEN(hash_ip, PF, _head) -#define hash_ip_pf_list TOKEN(hash_ip, PF, _list) -#define hash_ip_pf_resize TOKEN(hash_ip, PF, _resize) -#define hash_ip_pf TOKEN(hash_ip, PF , ) -#define hash_ip_pf_kadt TOKEN(hash_ip, PF, _kadt) -#define hash_ip_pf_uadt TOKEN(hash_ip, PF, _uadt) -#define hash_ip_pf_destroy TOKEN(hash_ip, PF, _destroy) -#define hash_ip_pf_flush TOKEN(hash_ip, PF, _flush) -#define hash_ip_pf_timeout_gc TOKEN(hash_ip, PF, _timeout_gc) -#define hash_ip_pf_gc_init TOKEN(hash_ip, PF, _gc_init) -#define ip_pf_hash TOKEN(ip, PF, _hash) -#define ip_pf_cmp TOKEN(ip, PF, _cmp) -#define ip_pf_null TOKEN(ip, PF, _null) -#define ip_pf_cpy TOKEN(ip, PF, _cpy) -#define ip_pf_zero_out TOKEN(ip, PF, _zero_out) -#define ip_pf_elem TOKEN(ip, PF, _elem) -#define ip_pf_elem_timeout TOKEN(ip, PF, _elem_timeout) -#define ip_pf_get_elem_timeout TOKEN(get_ip, PF, _elem_timeout) - -static inline bool -hash_ip_pf_timeout(const struct hash_ip *map, uint32_t id) -{ - struct ip_pf_elem_timeout *elem = hash_ip_elem(map, id); - - return ip_set_timeout_test(elem->timeout); -} - -static inline bool -hash_ip_pf_expired(const struct hash_ip *map, uint32_t id) -{ - struct ip_pf_elem_timeout *elem = hash_ip_elem(map, id); - - return ip_set_timeout_expired(elem->timeout); -} - -static inline bool -hash_ip_pf_elem_test(const struct hash_ip *map, bool with_timeout, - uint32_t id, struct ip_pf_elem * ip) -{ - struct ip_pf_elem *elem = hash_ip_elem(map, id); - - return ip_pf_cmp(elem, ip) - && (!with_timeout || hash_ip_pf_timeout(map, id)); -} - -static inline bool -hash_ip_pf_elem_exist(const struct hash_ip *map, bool with_timeout, - uint32_t id) -{ - struct ip_pf_elem *elem = hash_ip_elem(map, id); - - return !(ip_pf_null(elem) - || (with_timeout && hash_ip_pf_expired(map, id))); -} - -static inline bool -hash_ip_pf_elem_expired(const struct hash_ip *map, bool with_timeout, - uint32_t id) -{ - struct ip_pf_elem *elem = hash_ip_elem(map, id); - - return ip_pf_null(elem) - || (with_timeout && hash_ip_pf_expired(map, id)); -} - -static inline uint32_t -hash_ip_pf_test(const struct hash_ip *map, bool with_timeout, - struct ip_pf_elem * ip) -{ - uint32_t id; - uint8_t i; - - for (i = 0; i < map->probes; i++) { - id = ip_pf_hash(ip, *(map->initval + i), map->hashsize); - if (hash_ip_pf_elem_test(map, with_timeout, id, ip)) - return id + 1; - /* No shortcut - there can be deleted entries. */ - } - return 0; -} - -static void -hash_ip_pf_map_expired(struct hash_ip *map) -{ - struct ip_pf_elem_timeout *table = map->members; - uint32_t i; - - /* We run parallel with other readers (test element) - * but adding/deleting new entries is locked out */ - for (i = 0; i < map->hashsize; i++) - if (ip_set_timeout_expired(table[i].timeout)) { - ip_pf_zero_out((struct ip_pf_elem *)&table[i]); - table[i].timeout = IPSET_ELEM_UNSET; - map->elements--; - } -} - -static inline void -hash_ip_pf_set_expired(struct ip_set *set) -{ - /* We run parallel with other readers (test element) - * but adding/deleting new entries is locked out */ - read_lock_bh(&set->lock); - hash_ip_pf_map_expired(set->data); - read_unlock_bh(&set->lock); -} - -static int -hash_ip_pf_add(struct hash_ip *map, bool with_timeout, - struct ip_pf_elem *ip, uint32_t timeout) -{ - uint32_t id, empty = 0; - uint8_t i; - - if (map->elements >= map->maxelem) { - if (with_timeout) { - hash_ip_pf_map_expired(map); - if (map->elements < map->maxelem) - goto doit; - } - return -IPSET_ERR_HASH_FULL; - } - -doit: - for (i = 0; i < map->probes; i++) { - id = ip_pf_hash(ip, *(map->initval + i), map->hashsize); - if (hash_ip_pf_elem_test(map, with_timeout, id, ip)) - return -IPSET_ERR_EXIST; - if (empty == 0 - && hash_ip_pf_elem_expired(map, with_timeout, id)) - empty = id + 1; - /* There can be deleted entries, must check all slots */ - } - if (!empty) - /* Trigger rehashing */ - return -EAGAIN; - - if (with_timeout) { - struct ip_pf_elem_timeout *e = hash_ip_elem(map, empty - 1); - e->timeout = ip_set_timeout_set(timeout); - D("add with timeout: %u (%lu)", timeout, e->timeout); - ip_pf_cpy((struct ip_pf_elem *)e, ip); - } else { - struct ip_pf_elem *e = hash_ip_elem(map, empty - 1); - ip_pf_cpy(e, ip); - } - map->elements++; - return 0; -} - -static int -hash_ip_pf_readd(struct hash_ip *map, bool with_timeout, struct ip_pf_elem *ip) -{ - uint32_t id, empty = 0; - uint8_t i; - - for (i = 0; empty == 0 && i < map->probes; i++) { - id = ip_pf_hash(ip, *(map->initval + i), map->hashsize); - if (ip_pf_null(hash_ip_elem(map, id))) - empty = id + 1; - } - if (!empty) - /* Trigger rehashing */ - return -EAGAIN; - - if (with_timeout) { - struct ip_pf_elem_timeout *e = hash_ip_elem(map, empty - 1); - e->timeout = ip_pf_get_elem_timeout(ip); - ip_pf_cpy((struct ip_pf_elem *)e, ip); - } else { - struct ip_pf_elem *e = hash_ip_elem(map, empty - 1); - ip_pf_cpy(e, ip); - } - map->elements++; - return 0; -} - -static int -hash_ip_pf_del(struct hash_ip *map, bool with_timeout, struct ip_pf_elem *ip) -{ - struct ip_pf_elem *e; - uint32_t id, found = 0; - uint8_t i; - - for (i = 0; i < map->probes; i++) { - id = ip_pf_hash(ip, *(map->initval + i), map->hashsize); - if (hash_ip_pf_elem_test(map, with_timeout, id, ip)) { - found = id + 1; - break; - } - } - if (!found) - return -IPSET_ERR_EXIST; - - e = hash_ip_elem(map, found - 1); - ip_pf_zero_out(e); - if (with_timeout) - ((struct ip_pf_elem_timeout *)e)->timeout = IPSET_ELEM_UNSET; - - map->elements--; - - return 0; -} - -static int -hash_ip_pf_head(struct ip_set *set, struct sk_buff *skb) -{ - const struct hash_ip *map = set->data; - struct nlattr *nested; - - if (set->flags & IP_SET_FLAG_TIMEOUT) - hash_ip_pf_set_expired(set); - - nested = ipset_nest_start(skb, IPSET_ATTR_DATA); - if (!nested) - goto nla_put_failure; - NLA_PUT_NET32(skb, IPSET_ATTR_HASHSIZE, htonl(map->hashsize)); - NLA_PUT_NET32(skb, IPSET_ATTR_MAXELEM, htonl(map->maxelem)); - if (map->netmask != HOST_MASK) - NLA_PUT_U8(skb, IPSET_ATTR_NETMASK, map->netmask); - NLA_PUT_U8(skb, IPSET_ATTR_PROBES, map->probes); - NLA_PUT_U8(skb, IPSET_ATTR_RESIZE, map->resize); - NLA_PUT_NET32(skb, IPSET_ATTR_ELEMENTS, htonl(map->elements)); - NLA_PUT_NET32(skb, IPSET_ATTR_REFERENCES, - htonl(atomic_read(&set->ref) - 1)); - NLA_PUT_NET32(skb, IPSET_ATTR_MEMSIZE, - htonl(map->hashsize * map->elem_size)); - if (set->flags & IP_SET_FLAG_TIMEOUT) - NLA_PUT_NET32(skb, IPSET_ATTR_TIMEOUT, htonl(map->timeout)); - ipset_nest_end(skb, nested); - - return 0; -nla_put_failure: - return -EFAULT; -} - -static int -hash_ip_pf_list(struct ip_set *set, - struct sk_buff *skb, struct netlink_callback *cb) -{ - const struct hash_ip *map = set->data; - struct nlattr *atd, *nested; - struct ip_pf_elem *elem; - uint32_t id, first = cb->args[2]; - bool with_timeout = set->flags & IP_SET_FLAG_TIMEOUT; - - atd = ipset_nest_start(skb, IPSET_ATTR_ADT); - if (!atd) - return -EFAULT; - for (; cb->args[2] < map->hashsize; cb->args[2]++) { - id = cb->args[2]; - if (hash_ip_pf_elem_expired(map, with_timeout, id)) - continue; - nested = ipset_nest_start(skb, IPSET_ATTR_DATA); - if (!nested) { - if (id == first) { - nla_nest_cancel(skb, atd); - return -EFAULT; - } else - goto nla_put_failure; - } - elem = hash_ip_elem(map, id); - NLA_PUT_ADDR(skb, &elem->ip); - if (map->netmask != HOST_MASK) - NLA_PUT_U8(skb, IPSET_ATTR_CIDR, map->netmask); - if (with_timeout) { - unsigned long timeout = ip_pf_get_elem_timeout(elem); - D("list with timeout: %u (%lu)", - ip_set_timeout_get(timeout), timeout); - NLA_PUT_NET32(skb, IPSET_ATTR_TIMEOUT, - htonl(ip_set_timeout_get(timeout))); - } - ipset_nest_end(skb, nested); - } - ipset_nest_end(skb, atd); - /* Set listing finished */ - cb->args[2] = 0; - - return 0; - -nla_put_failure: - nla_nest_cancel(skb, nested); - ipset_nest_end(skb, atd); - return 0; -} - -static int -hash_ip_pf_resize(struct ip_set *set, uint8_t retried) -{ - struct hash_ip *map = set->data, *tmp; - void *members; - uint32_t i, hashsize = map->hashsize; - uint8_t oflags, flags = set->flags; - bool with_timeout = flags & IP_SET_FLAG_TIMEOUT; - int ret; - - if (map->resize == 0) - return -IPSET_ERR_HASH_FULL; - - /* Try to cleanup first */ - if (retried == 0 && with_timeout) { - i = map->elements; - hash_ip_pf_set_expired(set); - if (map->elements < i) - return 0; - } - -again: - ret = 0; - - /* Calculate new hash size */ - hashsize += (hashsize * map->resize)/100; - if (hashsize == map->hashsize) - hashsize++; - if (hashsize >= map->maxelem) - return -IPSET_ERR_HASH_FULL; - - printk("Rehashing of set %s triggered: hash grows from %lu to %lu\n", - set->name, - (long unsigned)map->hashsize, - (long unsigned)hashsize); - - tmp = kmalloc(sizeof(struct hash_ip) - + map->probes * sizeof(initval_t), GFP_ATOMIC); - if (!tmp) - return -ENOMEM; - - memcpy(tmp, map, sizeof(*map) + map->probes * sizeof(initval_t)); - tmp->elements = 0; - tmp->hashsize = hashsize; - tmp->members = ip_set_alloc(hashsize * map->elem_size, - GFP_ATOMIC, &flags); - if (!tmp->members) { - kfree(tmp); - return -ENOMEM; - } - - write_lock_bh(&set->lock); - map = set->data; /* Play safe */ - for (i = 0; i < map->hashsize && ret == 0; i++) { - if (hash_ip_pf_elem_exist(map, with_timeout, i)) - ret = hash_ip_pf_readd(tmp, with_timeout, - hash_ip_elem(map, i)); - } - if (ret) { - /* Failure, try again */ - write_unlock_bh(&set->lock); - ip_set_free(tmp->members, flags); - kfree(tmp); - goto again; - } - - /* Success at resizing! */ - members = map->members; - oflags = set->flags; - - map->hashsize = tmp->hashsize; - map->members = tmp->members; - map->elements = tmp->elements; - set->flags = flags; - write_unlock_bh(&set->lock); - - ip_set_free(members, oflags); - kfree(tmp); - - return 0; -} - -static int -hash_ip_pf_kadt(struct ip_set *set, const struct sk_buff * skb, - enum ipset_adt adt, uint8_t pf, const uint8_t *flags); -static int -hash_ip_pf_uadt(struct ip_set *set, struct nlattr *head, int len, - enum ipset_adt adt, uint32_t *lineno, uint32_t flags); - -static const struct ip_set_type_variant hash_ip_pf __read_mostly = { - .kadt = hash_ip_pf_kadt, - .uadt = hash_ip_pf_uadt, - .destroy = hash_ip_pf_destroy, - .flush = hash_ip_pf_flush, - .head = hash_ip_pf_head, - .list = hash_ip_pf_list, - .resize = hash_ip_pf_resize, -}; - -static void -hash_ip_pf_timeout_gc(unsigned long ul_set) -{ - struct ip_set *set = (struct ip_set *) ul_set; - struct hash_ip *map = set->data; - - hash_ip_pf_set_expired(set); - - map->gc.expires = jiffies + IPSET_GC_PERIOD(map->timeout) * HZ; - add_timer(&map->gc); -} - -static inline void -hash_ip_pf_gc_init(struct ip_set *set) -{ - struct hash_ip *map = set->data; - - init_timer(&map->gc); - map->gc.data = (unsigned long) set; - map->gc.function = hash_ip_pf_timeout_gc; - map->gc.expires = jiffies + IPSET_GC_PERIOD(map->timeout) * HZ; - add_timer(&map->gc); -} - -#undef HOST_MASK -#undef NLA_PUT_ADDR -#undef hash_ip_pf_timeout -#undef hash_ip_pf_expired -#undef hash_ip_pf_elem_test -#undef hash_ip_pf_elem_exist -#undef hash_ip_pf_elem_expired -#undef hash_ip_pf_test -#undef hash_ip_pf_add -#undef hash_ip_pf_readd -#undef hash_ip_pf_del -#undef hash_ip_pf_map_expired -#undef hash_ip_pf_set_expired -#undef hash_ip_pf_head -#undef hash_ip_pf_list -#undef hash_ip_pf_resize -#undef hash_ip_pf -#undef hash_ip_pf_kadt -#undef hash_ip_pf_uadt -#undef hash_ip_pf_destroy -#undef hash_ip_pf_flush -#undef hash_ip_pf_timeout_gc -#undef hash_ip_pf_gc_init -#undef ip_pf_hash -#undef ip_pf_cmp -#undef ip_pf_null -#undef ip_pf_cpy -#undef ip_pf_zero_out -#undef ip_pf_elem -#undef ip_pf_elem_timeout -#undef ip_pf_get_elem_timeout diff --git a/kernel/ip_set_hash_ipport.c b/kernel/ip_set_hash_ipport.c index 36e68b0..8210f67 100644 --- a/kernel/ip_set_hash_ipport.c +++ b/kernel/ip_set_hash_ipport.c @@ -1,197 +1,455 @@ -/* Copyright (C) 2003-2008 Jozsef Kadlecsik +/* Copyright (C) 2003-2010 Jozsef Kadlecsik * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as * published by the Free Software Foundation. */ -/* Kernel module implementing an ip+port hash set */ +/* Kernel module implementing an IP set type: the hash:ip,port type */ +#include +#include #include -#include #include -#include -#include #include -#include #include #include #include #include #include - #include +#include +#include +#include -#include -#include +#include +#include +#include +#include +#include -static int limit = MAX_RANGE; +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Jozsef Kadlecsik "); +MODULE_DESCRIPTION("hash:ip,port type of IP sets"); +MODULE_ALIAS("ip_set_hash:ip,port"); + +/* Type specific function prefix */ +#define TYPE hash_ipport + +static bool +hash_ipport_same_set(const struct ip_set *a, const struct ip_set *b); + +#define hash_ipport4_same_set hash_ipport_same_set +#define hash_ipport6_same_set hash_ipport_same_set + +/* The type variant functions: IPv4 */ + +/* Member elements without timeout */ +struct hash_ipport4_elem { + u32 ip; + u16 port; + u16 match; +}; + +/* Member elements with timeout support */ +struct hash_ipport4_telem { + u32 ip; + u16 port; + u16 match; + unsigned long timeout; +}; + +static inline bool +hash_ipport4_data_equal(const struct hash_ipport4_elem *ip1, + const struct hash_ipport4_elem *ip2) +{ + return ip1->ip == ip2->ip && ip1->port == ip2->port; +} -static inline __u32 -ipporthash_id(struct ip_set *set, ip_set_ip_t ip, ip_set_ip_t port) +static inline bool +hash_ipport4_data_isnull(const struct hash_ipport4_elem *elem) { - struct ip_set_ipporthash *map = set->data; - __u32 id; - u_int16_t i; - ip_set_ip_t *elem; + return elem->match == 0; +} - ip = pack_ip_port(map, ip, port); - - if (!ip) - return UINT_MAX; - - for (i = 0; i < map->probes; i++) { - id = jhash_ip(map, i, ip) % map->hashsize; - DP("hash key: %u", id); - elem = HARRAY_ELEM(map->members, ip_set_ip_t *, id); - if (*elem == ip) - return id; - /* No shortcut - there can be deleted entries. */ - } - return UINT_MAX; +static inline void +hash_ipport4_data_copy(struct hash_ipport4_elem *dst, + const struct hash_ipport4_elem *src) +{ + dst->ip = src->ip; + dst->port = src->port; + dst->match = 1; } -static inline int -ipporthash_test(struct ip_set *set, ip_set_ip_t ip, ip_set_ip_t port) +static inline void +hash_ipport4_data_swap(struct hash_ipport4_elem *dst, + struct hash_ipport4_elem *src) { - struct ip_set_ipporthash *map = set->data; - - if (ip < map->first_ip || ip > map->last_ip) - return -ERANGE; - - return (ipporthash_id(set, ip, port) != UINT_MAX); -} - -#define KADT_CONDITION \ - ip_set_ip_t port; \ - \ - if (flags[1] == 0) \ - return 0; \ - \ - port = get_port(skb, flags++); \ - \ - if (port == INVALID_PORT) \ - return 0; - -UADT(ipporthash, test, req->port) -KADT(ipporthash, test, ipaddr, port) - -static inline int -__ipporthash_add(struct ip_set_ipporthash *map, ip_set_ip_t *ip) -{ - __u32 probe; - u_int16_t i; - ip_set_ip_t *elem, *slot = NULL; - - for (i = 0; i < map->probes; i++) { - probe = jhash_ip(map, i, *ip) % map->hashsize; - elem = HARRAY_ELEM(map->members, ip_set_ip_t *, probe); - if (*elem == *ip) - return -EEXIST; - if (!(slot || *elem)) - slot = elem; - /* There can be deleted entries, must check all slots */ + swap(dst->ip, src->ip); + swap(dst->port, src->port); +} + +static inline void +hash_ipport4_data_zero_out(struct hash_ipport4_elem *elem) +{ + elem->match = 0; +} + +static inline bool +hash_ipport4_data_list(struct sk_buff *skb, + const struct hash_ipport4_elem *data) +{ + NLA_PUT_NET32(skb, IPSET_ATTR_IP, data->ip); + NLA_PUT_NET16(skb, IPSET_ATTR_PORT, data->port); + return 0; + +nla_put_failure: + return 1; +} + +static inline bool +hash_ipport4_data_tlist(struct sk_buff *skb, + const struct hash_ipport4_elem *data) +{ + const struct hash_ipport4_telem *tdata = + (const struct hash_ipport4_telem *)data; + + NLA_PUT_NET32(skb, IPSET_ATTR_IP, tdata->ip); + NLA_PUT_NET16(skb, IPSET_ATTR_PORT, tdata->port); + NLA_PUT_NET32(skb, IPSET_ATTR_TIMEOUT, + htonl(ip_set_timeout_get(tdata->timeout))); + + return 0; + +nla_put_failure: + return 1; +} + +#define PF 4 +#define HOST_MASK 32 +#include + +static int +hash_ipport4_kadt(struct ip_set *set, const struct sk_buff *skb, + enum ipset_adt adt, u8 pf, u8 dim, u8 flags) +{ + struct chash *h = set->data; + ipset_adtfn adtfn = set->variant->adt[adt]; + struct hash_ipport4_elem data = {}; + + ip4addrptr(skb, flags & IPSET_DIM_ONE_SRC, &data.ip); + if (!get_port(AF_INET, skb, flags & IPSET_DIM_TWO_SRC, &data.port)) + return -EINVAL; + + return adtfn(set, &data, GFP_ATOMIC, h->timeout); +} + +static const struct nla_policy +hash_ipport4_adt_policy[IPSET_ATTR_ADT_MAX + 1] __read_mostly = { + [IPSET_ATTR_IP] = { .type = NLA_U32 }, + [IPSET_ATTR_PORT] = { .type = NLA_U16 }, + [IPSET_ATTR_TIMEOUT] = { .type = NLA_U32 }, +}; + +static int +hash_ipport4_uadt(struct ip_set *set, struct nlattr *head, int len, + enum ipset_adt adt, u32 *lineno, u32 flags) +{ + struct chash *h = set->data; + struct nlattr *tb[IPSET_ATTR_ADT_MAX]; + bool eexist = flags & IPSET_FLAG_EXIST; + ipset_adtfn adtfn = set->variant->adt[adt]; + struct hash_ipport4_elem data = {}; + u32 timeout = h->timeout; + int ret; + + if (nla_parse(tb, IPSET_ATTR_ADT_MAX, head, len, + hash_ipport4_adt_policy)) + return -IPSET_ERR_PROTOCOL; + + if (tb[IPSET_ATTR_IP]) + data.ip = ip_set_get_n32(tb[IPSET_ATTR_IP]); + else + return -IPSET_ERR_PROTOCOL; + + if (tb[IPSET_ATTR_PORT]) + data.port = ip_set_get_n16(tb[IPSET_ATTR_PORT]); + else + return -IPSET_ERR_PROTOCOL; + + if (tb[IPSET_ATTR_TIMEOUT]) { + if (!with_timeout(h->timeout)) + return -IPSET_ERR_TIMEOUT; + timeout = ip_set_timeout_uget(tb[IPSET_ATTR_TIMEOUT]); } - if (slot) { - *slot = *ip; - map->elements++; - return 0; + + ret = adtfn(set, &data, GFP_KERNEL, timeout); + + if (ret && !(ret == -IPSET_ERR_EXIST && eexist)) { + if (tb[IPSET_ATTR_LINENO]) + *lineno = nla_get_u32(tb[IPSET_ATTR_LINENO]); } - /* Trigger rehashing */ - return -EAGAIN; + return ret; } -static inline int -ipporthash_add(struct ip_set *set, ip_set_ip_t ip, ip_set_ip_t port) +static bool +hash_ipport_same_set(const struct ip_set *a, const struct ip_set *b) { - struct ip_set_ipporthash *map = set->data; - if (map->elements > limit) - return -ERANGE; - if (ip < map->first_ip || ip > map->last_ip) - return -ERANGE; + struct chash *x = a->data; + struct chash *y = b->data; + + return x->maxelem == y->maxelem + && x->timeout == y->timeout + && x->htable_bits == y->htable_bits /* resizing ? */ + && x->array_size == y->array_size + && x->chain_limit == y->chain_limit; +} - ip = pack_ip_port(map, ip, port); +/* The type variant functions: IPv6 */ - if (!ip) - return -ERANGE; - - return __ipporthash_add(map, &ip); +struct hash_ipport6_elem { + union nf_inet_addr ip; + u16 port; + u16 match; +}; + +struct hash_ipport6_telem { + union nf_inet_addr ip; + u16 port; + u16 match; + unsigned long timeout; +}; + +static inline bool +hash_ipport6_data_equal(const struct hash_ipport6_elem *ip1, + const struct hash_ipport6_elem *ip2) +{ + return ipv6_addr_cmp(&ip1->ip.in6, &ip2->ip.in6) == 0 + && ip1->port == ip2->port; } -UADT(ipporthash, add, req->port) -KADT(ipporthash, add, ipaddr, port) +static inline bool +hash_ipport6_data_isnull(const struct hash_ipport6_elem *elem) +{ + return elem->match == 0; +} static inline void -__ipporthash_retry(struct ip_set_ipporthash *tmp, - struct ip_set_ipporthash *map) +hash_ipport6_data_copy(struct hash_ipport6_elem *dst, + const struct hash_ipport6_elem *src) { - tmp->first_ip = map->first_ip; - tmp->last_ip = map->last_ip; + memcpy(dst, src, sizeof(*dst)); + dst->match = 1; } -HASH_RETRY(ipporthash, ip_set_ip_t) - -static inline int -ipporthash_del(struct ip_set *set, ip_set_ip_t ip, ip_set_ip_t port) +static inline void +hash_ipport6_data_swap(struct hash_ipport6_elem *dst, + struct hash_ipport6_elem *src) { - struct ip_set_ipporthash *map = set->data; - ip_set_ip_t id; - ip_set_ip_t *elem; + struct hash_ipport6_elem tmp; - if (ip < map->first_ip || ip > map->last_ip) - return -ERANGE; + memcpy(&tmp, dst, sizeof(tmp)); + memcpy(dst, src, sizeof(tmp)); + memcpy(src, &tmp, sizeof(tmp)); +} - id = ipporthash_id(set, ip, port); +static inline void +hash_ipport6_data_zero_out(struct hash_ipport6_elem *elem) +{ + elem->match = 0; +} - if (id == UINT_MAX) - return -EEXIST; - - elem = HARRAY_ELEM(map->members, ip_set_ip_t *, id); - *elem = 0; - map->elements--; +static inline bool +hash_ipport6_data_list(struct sk_buff *skb, + const struct hash_ipport6_elem *data) +{ + NLA_PUT(skb, IPSET_ATTR_IP, sizeof(struct in6_addr), &data->ip); + NLA_PUT_NET16(skb, IPSET_ATTR_PORT, data->port); + return 0; +nla_put_failure: + return 1; +} + +static inline bool +hash_ipport6_data_tlist(struct sk_buff *skb, + const struct hash_ipport6_elem *data) +{ + const struct hash_ipport6_telem *e = + (const struct hash_ipport6_telem *)data; + + NLA_PUT(skb, IPSET_ATTR_IP, sizeof(struct in6_addr), &e->ip); + NLA_PUT_NET16(skb, IPSET_ATTR_PORT, data->port); + NLA_PUT_NET32(skb, IPSET_ATTR_TIMEOUT, + htonl(ip_set_timeout_get(e->timeout))); return 0; + +nla_put_failure: + return 1; } -UADT(ipporthash, del, req->port) -KADT(ipporthash, del, ipaddr, port) +#undef PF +#undef HOST_MASK + +#define PF 6 +#define HOST_MASK 128 +#include + +static int +hash_ipport6_kadt(struct ip_set *set, const struct sk_buff *skb, + enum ipset_adt adt, u8 pf, u8 dim, u8 flags) +{ + struct chash *h = set->data; + ipset_adtfn adtfn = set->variant->adt[adt]; + struct hash_ipport6_elem data = {}; + + ip6addrptr(skb, flags & IPSET_DIM_ONE_SRC, &data.ip.in6); + if (!get_port(AF_INET, skb, flags & IPSET_DIM_TWO_SRC, &data.port)) + return -EINVAL; + + return adtfn(set, &data, GFP_ATOMIC, h->timeout); +} -static inline int -__ipporthash_create(const struct ip_set_req_ipporthash_create *req, - struct ip_set_ipporthash *map) +static const struct nla_policy +hash_ipport6_adt_policy[IPSET_ATTR_ADT_MAX + 1] __read_mostly = { + [IPSET_ATTR_IP] = { .type = NLA_BINARY, + .len = sizeof(struct in6_addr) }, + [IPSET_ATTR_PORT] = { .type = NLA_U16 }, + [IPSET_ATTR_TIMEOUT] = { .type = NLA_U32 }, +}; + +static int +hash_ipport6_uadt(struct ip_set *set, struct nlattr *head, int len, + enum ipset_adt adt, u32 *lineno, u32 flags) { - if (req->to - req->from > MAX_RANGE) { - ip_set_printk("range too big, %d elements (max %d)", - req->to - req->from + 1, MAX_RANGE+1); - return -ENOEXEC; + struct chash *h = set->data; + struct nlattr *tb[IPSET_ATTR_ADT_MAX]; + ipset_adtfn adtfn = set->variant->adt[adt]; + struct hash_ipport6_elem data = {}; + u32 timeout = h->timeout; + + if (nla_parse(tb, IPSET_ATTR_ADT_MAX, head, len, + hash_ipport6_adt_policy)) + return -IPSET_ERR_PROTOCOL; + + if (tb[IPSET_ATTR_IP]) + memcpy(&data.ip, nla_data(tb[IPSET_ATTR_IP]), + sizeof(struct in6_addr)); + else + return -IPSET_ERR_PROTOCOL; + + if (tb[IPSET_ATTR_PORT]) + data.port = ip_set_get_n16(tb[IPSET_ATTR_PORT]); + else + return -IPSET_ERR_PROTOCOL; + + if (tb[IPSET_ATTR_TIMEOUT]) { + if (!with_timeout(h->timeout)) + return -IPSET_ERR_TIMEOUT; + timeout = ip_set_timeout_uget(tb[IPSET_ATTR_TIMEOUT]); } - map->first_ip = req->from; - map->last_ip = req->to; - return 0; + + return adtfn(set, &data, GFP_KERNEL, timeout); } -HASH_CREATE(ipporthash, ip_set_ip_t) -HASH_DESTROY(ipporthash) -HASH_FLUSH(ipporthash, ip_set_ip_t) +/* Create hash:ip type of sets */ -static inline void -__ipporthash_list_header(const struct ip_set_ipporthash *map, - struct ip_set_req_ipporthash_create *header) +static const struct nla_policy +hash_ipport_create_policy[IPSET_ATTR_CREATE_MAX+1] __read_mostly = { + [IPSET_ATTR_HASHSIZE] = { .type = NLA_U32 }, + [IPSET_ATTR_MAXELEM] = { .type = NLA_U32 }, + [IPSET_ATTR_PROBES] = { .type = NLA_U8 }, + [IPSET_ATTR_RESIZE] = { .type = NLA_U8 }, + [IPSET_ATTR_TIMEOUT] = { .type = NLA_U32 }, +}; + +static int +hash_ipport_create(struct ip_set *set, struct nlattr *head, int len, u32 flags) { - header->from = map->first_ip; - header->to = map->last_ip; -} + struct nlattr *tb[IPSET_ATTR_CREATE_MAX]; + u32 hashsize = IPSET_DEFAULT_HASHSIZE, maxelem = IPSET_DEFAULT_MAXELEM; + struct chash *h; -HASH_LIST_HEADER(ipporthash) -HASH_LIST_MEMBERS_SIZE(ipporthash, ip_set_ip_t) -HASH_LIST_MEMBERS(ipporthash, ip_set_ip_t) + if (!(set->family == AF_INET || set->family == AF_INET6)) + return -IPSET_ERR_INVALID_FAMILY; -IP_SET_RTYPE(ipporthash, IPSET_TYPE_IP | IPSET_TYPE_PORT | IPSET_DATA_DOUBLE) + if (nla_parse(tb, IPSET_ATTR_CREATE_MAX, head, len, + hash_ipport_create_policy)) + return -IPSET_ERR_PROTOCOL; -MODULE_LICENSE("GPL"); -MODULE_AUTHOR("Jozsef Kadlecsik "); -MODULE_DESCRIPTION("ipporthash type of IP sets"); -module_param(limit, int, 0600); -MODULE_PARM_DESC(limit, "maximal number of elements stored in the sets"); + if (tb[IPSET_ATTR_HASHSIZE]) { + hashsize = ip_set_get_h32(tb[IPSET_ATTR_HASHSIZE]); + if (hashsize < IPSET_MIMINAL_HASHSIZE) + hashsize = IPSET_MIMINAL_HASHSIZE; + } + + if (tb[IPSET_ATTR_MAXELEM]) + maxelem = ip_set_get_h32(tb[IPSET_ATTR_MAXELEM]); + + h = kzalloc(sizeof(*h), GFP_KERNEL); + if (!h) + return -ENOMEM; + + h->maxelem = maxelem; + h->htable_bits = htable_bits(hashsize); + h->array_size = CHASH_DEFAULT_ARRAY_SIZE; + h->chain_limit = CHASH_DEFAULT_CHAIN_LIMIT; + get_random_bytes(&h->initval, sizeof(h->initval)); + h->timeout = IPSET_NO_TIMEOUT; + + h->htable = ip_set_alloc(jhash_size(h->htable_bits) * sizeof(struct slist), + GFP_KERNEL, &set->flags); + if (!h->htable) { + kfree(h); + return -ENOMEM; + } + + set->data = h; + + if (tb[IPSET_ATTR_TIMEOUT]) { + h->timeout = ip_set_timeout_uget(tb[IPSET_ATTR_TIMEOUT]); + + set->variant = set->family == AF_INET + ? &hash_ipport4_tvariant : &hash_ipport6_tvariant; + + if (set->family == AF_INET) + hash_ipport4_gc_init(set); + else + hash_ipport6_gc_init(set); + } else { + set->variant = set->family == AF_INET + ? &hash_ipport4_variant : &hash_ipport6_variant; + } + + pr_debug("create %s hashsize %u (%u) maxelem %u: %p(%p)", + set->name, jhash_size(h->htable_bits), + h->htable_bits, h->maxelem, set->data, h->htable); + + return 0; +} + +static struct ip_set_type hash_ipport_type = { + .name = "hash:ip,port", + .protocol = IPSET_PROTOCOL, + .features = IPSET_TYPE_IP | IPSET_TYPE_PORT, + .dimension = IPSET_DIM_TWO, + .family = AF_UNSPEC, + .revision = 0, + .create = hash_ipport_create, + .me = THIS_MODULE, +}; + +static int __init +hash_ipport_init(void) +{ + return ip_set_type_register(&hash_ipport_type); +} + +static void __exit +hash_ipport_fini(void) +{ + ip_set_type_unregister(&hash_ipport_type); +} -REGISTER_MODULE(ipporthash) +module_init(hash_ipport_init); +module_exit(hash_ipport_fini); diff --git a/kernel/ip_set_hash_ipportip.c b/kernel/ip_set_hash_ipportip.c index 8b8f2a2..fbf2780 100644 --- a/kernel/ip_set_hash_ipportip.c +++ b/kernel/ip_set_hash_ipportip.c @@ -1,215 +1,485 @@ -/* Copyright (C) 2008 Jozsef Kadlecsik +/* Copyright (C) 2003-2010 Jozsef Kadlecsik * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as * published by the Free Software Foundation. */ -/* Kernel module implementing an ip+port+ip hash set */ +/* Kernel module implementing an IP set type: the hash:ip,port,ip type */ +#include +#include #include -#include #include -#include -#include #include -#include #include #include #include #include #include - #include +#include +#include +#include -#include -#include +#include +#include +#include +#include +#include -static int limit = MAX_RANGE; +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Jozsef Kadlecsik "); +MODULE_DESCRIPTION("hash:ip,port,ip type of IP sets"); +MODULE_ALIAS("ip_set_hash:ip,port,ip"); + +/* Type specific function prefix */ +#define TYPE hash_ipportip + +static bool +hash_ipportip_same_set(const struct ip_set *a, const struct ip_set *b); + +#define hash_ipportip4_same_set hash_ipportip_same_set +#define hash_ipportip6_same_set hash_ipportip_same_set + +/* The type variant functions: IPv4 */ + +/* Member elements without timeout */ +struct hash_ipportip4_elem { + u32 ip; + u32 ip2; + u16 port; + u16 match; +}; + +/* Member elements with timeout support */ +struct hash_ipportip4_telem { + u32 ip; + u32 ip2; + u16 port; + u16 match; + unsigned long timeout; +}; + +static inline bool +hash_ipportip4_data_equal(const struct hash_ipportip4_elem *ip1, + const struct hash_ipportip4_elem *ip2) +{ + return ip1->ip == ip2->ip + && ip1->ip2 == ip2->ip2 + && ip1->port == ip2->port; +} -#define jhash_ip2(map, i, ipport, ip1) \ - jhash_2words(ipport, ip1, *(map->initval + i)) +static inline bool +hash_ipportip4_data_isnull(const struct hash_ipportip4_elem *elem) +{ + return elem->match == 0; +} -static inline __u32 -ipportiphash_id(struct ip_set *set, - ip_set_ip_t ip, ip_set_ip_t port, ip_set_ip_t ip1) +static inline void +hash_ipportip4_data_copy(struct hash_ipportip4_elem *dst, + const struct hash_ipportip4_elem *src) { - struct ip_set_ipportiphash *map = set->data; - __u32 id; - u_int16_t i; - struct ipportip *elem; + memcpy(dst, src, sizeof(*dst)); + dst->match = 1; +} - ip = pack_ip_port(map, ip, port); - if (!(ip || ip1)) - return UINT_MAX; - - for (i = 0; i < map->probes; i++) { - id = jhash_ip2(map, i, ip, ip1) % map->hashsize; - DP("hash key: %u", id); - elem = HARRAY_ELEM(map->members, struct ipportip *, id); - if (elem->ip == ip && elem->ip1 == ip1) - return id; - /* No shortcut - there can be deleted entries. */ - } - return UINT_MAX; +static inline void +hash_ipportip4_data_swap(struct hash_ipportip4_elem *dst, + struct hash_ipportip4_elem *src) +{ + struct hash_ipportip4_elem tmp; + + memcpy(&tmp, dst, sizeof(tmp)); + memcpy(dst, src, sizeof(tmp)); + memcpy(src, &tmp, sizeof(tmp)); } -static inline int -ipportiphash_test(struct ip_set *set, - ip_set_ip_t ip, ip_set_ip_t port, ip_set_ip_t ip1) +static inline void +hash_ipportip4_data_zero_out(struct hash_ipportip4_elem *elem) { - struct ip_set_ipportiphash *map = set->data; + elem->match = 0; +} + +static inline bool +hash_ipportip4_data_list(struct sk_buff *skb, + const struct hash_ipportip4_elem *data) +{ + NLA_PUT_NET32(skb, IPSET_ATTR_IP, data->ip); + NLA_PUT_NET32(skb, IPSET_ATTR_IP2, data->ip2); + NLA_PUT_NET16(skb, IPSET_ATTR_PORT, data->port); + return 0; + +nla_put_failure: + return 1; +} + +static inline bool +hash_ipportip4_data_tlist(struct sk_buff *skb, + const struct hash_ipportip4_elem *data) +{ + const struct hash_ipportip4_telem *tdata = + (const struct hash_ipportip4_telem *)data; + + NLA_PUT_NET32(skb, IPSET_ATTR_IP, tdata->ip); + NLA_PUT_NET32(skb, IPSET_ATTR_IP2, tdata->ip2); + NLA_PUT_NET16(skb, IPSET_ATTR_PORT, tdata->port); + NLA_PUT_NET32(skb, IPSET_ATTR_TIMEOUT, + htonl(ip_set_timeout_get(tdata->timeout))); + + return 0; + +nla_put_failure: + return 1; +} + +#define PF 4 +#define HOST_MASK 32 +#include + +static int +hash_ipportip4_kadt(struct ip_set *set, const struct sk_buff *skb, + enum ipset_adt adt, u8 pf, u8 dim, u8 flags) +{ + struct chash *h = set->data; + ipset_adtfn adtfn = set->variant->adt[adt]; + struct hash_ipportip4_elem data = {}; - if (ip < map->first_ip || ip > map->last_ip) - return -ERANGE; - - return (ipportiphash_id(set, ip, port, ip1) != UINT_MAX); -} - -#define KADT_CONDITION \ - ip_set_ip_t port, ip1; \ - \ - if (flags[2] == 0) \ - return 0; \ - \ - port = get_port(skb, flags++); \ - ip1 = ipaddr(skb, flags++); \ - \ - if (port == INVALID_PORT) \ - return 0; - -UADT(ipportiphash, test, req->port, req->ip1) -KADT(ipportiphash, test, ipaddr, port, ip1) - -static inline int -__ipportip_add(struct ip_set_ipportiphash *map, - ip_set_ip_t ip, ip_set_ip_t ip1) -{ - __u32 probe; - u_int16_t i; - struct ipportip *elem, *slot = NULL; - - for (i = 0; i < map->probes; i++) { - probe = jhash_ip2(map, i, ip, ip1) % map->hashsize; - elem = HARRAY_ELEM(map->members, struct ipportip *, probe); - if (elem->ip == ip && elem->ip1 == ip1) - return -EEXIST; - if (!(slot || elem->ip || elem->ip1)) - slot = elem; - /* There can be deleted entries, must check all slots */ + ip4addrptr(skb, flags & IPSET_DIM_ONE_SRC, &data.ip); + if (!get_port(AF_INET, skb, flags & IPSET_DIM_TWO_SRC, &data.port)) + return -EINVAL; + ip4addrptr(skb, flags & IPSET_DIM_THREE_SRC, &data.ip2); + + return adtfn(set, &data, GFP_ATOMIC, h->timeout); +} + +static const struct nla_policy +hash_ipportip4_adt_policy[IPSET_ATTR_ADT_MAX + 1] __read_mostly = { + [IPSET_ATTR_IP] = { .type = NLA_U32 }, + [IPSET_ATTR_IP2] = { .type = NLA_U32 }, + [IPSET_ATTR_PORT] = { .type = NLA_U16 }, + [IPSET_ATTR_TIMEOUT] = { .type = NLA_U32 }, +}; + +static int +hash_ipportip4_uadt(struct ip_set *set, struct nlattr *head, int len, + enum ipset_adt adt, u32 *lineno, u32 flags) +{ + struct chash *h = set->data; + struct nlattr *tb[IPSET_ATTR_ADT_MAX]; + bool eexist = flags & IPSET_FLAG_EXIST; + ipset_adtfn adtfn = set->variant->adt[adt]; + struct hash_ipportip4_elem data = {}; + u32 timeout = h->timeout; + int ret; + + if (nla_parse(tb, IPSET_ATTR_ADT_MAX, head, len, + hash_ipportip4_adt_policy)) + return -IPSET_ERR_PROTOCOL; + + if (tb[IPSET_ATTR_IP]) + data.ip = ip_set_get_n32(tb[IPSET_ATTR_IP]); + else + return -IPSET_ERR_PROTOCOL; + + if (tb[IPSET_ATTR_IP2]) + data.ip2 = ip_set_get_n32(tb[IPSET_ATTR_IP2]); + else + return -IPSET_ERR_PROTOCOL; + + if (tb[IPSET_ATTR_PORT]) + data.port = ip_set_get_n16(tb[IPSET_ATTR_PORT]); + else + return -IPSET_ERR_PROTOCOL; + + if (tb[IPSET_ATTR_TIMEOUT]) { + if (!with_timeout(h->timeout)) + return -IPSET_ERR_TIMEOUT; + timeout = ip_set_timeout_uget(tb[IPSET_ATTR_TIMEOUT]); } - if (slot) { - slot->ip = ip; - slot->ip1 = ip1; - map->elements++; - return 0; + + ret = adtfn(set, &data, GFP_KERNEL, timeout); + + if (ret && !(ret == -IPSET_ERR_EXIST && eexist)) { + if (tb[IPSET_ATTR_LINENO]) + *lineno = nla_get_u32(tb[IPSET_ATTR_LINENO]); } - /* Trigger rehashing */ - return -EAGAIN; + return ret; } -static inline int -__ipportiphash_add(struct ip_set_ipportiphash *map, - struct ipportip *elem) +static bool +hash_ipportip_same_set(const struct ip_set *a, const struct ip_set *b) { - return __ipportip_add(map, elem->ip, elem->ip1); + struct chash *x = a->data; + struct chash *y = b->data; + + return x->maxelem == y->maxelem + && x->timeout == y->timeout + && x->htable_bits == y->htable_bits /* resizing ? */ + && x->array_size == y->array_size + && x->chain_limit == y->chain_limit; } -static inline int -ipportiphash_add(struct ip_set *set, - ip_set_ip_t ip, ip_set_ip_t port, ip_set_ip_t ip1) +/* The type variant functions: IPv6 */ + +struct hash_ipportip6_elem { + union nf_inet_addr ip; + union nf_inet_addr ip2; + u16 port; + u16 match; +}; + +struct hash_ipportip6_telem { + union nf_inet_addr ip; + union nf_inet_addr ip2; + u16 port; + u16 match; + unsigned long timeout; +}; + +static inline bool +hash_ipportip6_data_equal(const struct hash_ipportip6_elem *ip1, + const struct hash_ipportip6_elem *ip2) { - struct ip_set_ipportiphash *map = set->data; - - if (map->elements > limit) - return -ERANGE; - if (ip < map->first_ip || ip > map->last_ip) - return -ERANGE; - - ip = pack_ip_port(map, ip, port); - if (!(ip || ip1)) - return -ERANGE; - - return __ipportip_add(map, ip, ip1); + return ipv6_addr_cmp(&ip1->ip.in6, &ip2->ip.in6) == 0 + && ipv6_addr_cmp(&ip1->ip2.in6, &ip2->ip2.in6) == 0 + && ip1->port == ip2->port; } -UADT(ipportiphash, add, req->port, req->ip1) -KADT(ipportiphash, add, ipaddr, port, ip1) +static inline bool +hash_ipportip6_data_isnull(const struct hash_ipportip6_elem *elem) +{ + return elem->match == 0; +} static inline void -__ipportiphash_retry(struct ip_set_ipportiphash *tmp, - struct ip_set_ipportiphash *map) +hash_ipportip6_data_copy(struct hash_ipportip6_elem *dst, + const struct hash_ipportip6_elem *src) { - tmp->first_ip = map->first_ip; - tmp->last_ip = map->last_ip; + memcpy(dst, src, sizeof(*dst)); + dst->match = 1; } -HASH_RETRY2(ipportiphash, struct ipportip) - -static inline int -ipportiphash_del(struct ip_set *set, - ip_set_ip_t ip, ip_set_ip_t port, ip_set_ip_t ip1) +static inline void +hash_ipportip6_data_swap(struct hash_ipportip6_elem *dst, + struct hash_ipportip6_elem *src) { - struct ip_set_ipportiphash *map = set->data; - ip_set_ip_t id; - struct ipportip *elem; + struct hash_ipportip6_elem tmp; + + memcpy(&tmp, dst, sizeof(tmp)); + memcpy(dst, src, sizeof(tmp)); + memcpy(src, &tmp, sizeof(tmp)); +} - if (ip < map->first_ip || ip > map->last_ip) - return -ERANGE; +static inline void +hash_ipportip6_data_zero_out(struct hash_ipportip6_elem *elem) +{ + elem->match = 0; +} - id = ipportiphash_id(set, ip, port, ip1); +static inline bool +hash_ipportip6_data_list(struct sk_buff *skb, + const struct hash_ipportip6_elem *data) +{ + NLA_PUT(skb, IPSET_ATTR_IP, sizeof(struct in6_addr), &data->ip); + NLA_PUT(skb, IPSET_ATTR_IP2, sizeof(struct in6_addr), &data->ip2); + NLA_PUT_NET16(skb, IPSET_ATTR_PORT, data->port); + return 0; - if (id == UINT_MAX) - return -EEXIST; - - elem = HARRAY_ELEM(map->members, struct ipportip *, id); - elem->ip = elem->ip1 = 0; - map->elements--; +nla_put_failure: + return 1; +} +static inline bool +hash_ipportip6_data_tlist(struct sk_buff *skb, + const struct hash_ipportip6_elem *data) +{ + const struct hash_ipportip6_telem *e = + (const struct hash_ipportip6_telem *)data; + + NLA_PUT(skb, IPSET_ATTR_IP, sizeof(struct in6_addr), &e->ip); + NLA_PUT(skb, IPSET_ATTR_IP2, sizeof(struct in6_addr), &data->ip2); + NLA_PUT_NET16(skb, IPSET_ATTR_PORT, data->port); + NLA_PUT_NET32(skb, IPSET_ATTR_TIMEOUT, + htonl(ip_set_timeout_get(e->timeout))); return 0; + +nla_put_failure: + return 1; } -UADT(ipportiphash, del, req->port, req->ip1) -KADT(ipportiphash, del, ipaddr, port, ip1) +#undef PF +#undef HOST_MASK + +#define PF 6 +#define HOST_MASK 128 +#include + +static int +hash_ipportip6_kadt(struct ip_set *set, const struct sk_buff *skb, + enum ipset_adt adt, u8 pf, u8 dim, u8 flags) +{ + struct chash *h = set->data; + ipset_adtfn adtfn = set->variant->adt[adt]; + struct hash_ipportip6_elem data = {}; + + ip6addrptr(skb, flags & IPSET_DIM_ONE_SRC, &data.ip.in6); + if (!get_port(AF_INET, skb, flags & IPSET_DIM_TWO_SRC, &data.port)) + return -EINVAL; + ip6addrptr(skb, flags & IPSET_DIM_THREE_SRC, &data.ip2.in6); + + return adtfn(set, &data, GFP_ATOMIC, h->timeout); +} -static inline int -__ipportiphash_create(const struct ip_set_req_ipportiphash_create *req, - struct ip_set_ipportiphash *map) +static const struct nla_policy +hash_ipportip6_adt_policy[IPSET_ATTR_ADT_MAX + 1] __read_mostly = { + [IPSET_ATTR_IP] = { .type = NLA_BINARY, + .len = sizeof(struct in6_addr) }, + [IPSET_ATTR_IP2] = { .type = NLA_BINARY, + .len = sizeof(struct in6_addr) }, + [IPSET_ATTR_PORT] = { .type = NLA_U16 }, + [IPSET_ATTR_TIMEOUT] = { .type = NLA_U32 }, +}; + +static int +hash_ipportip6_uadt(struct ip_set *set, struct nlattr *head, int len, + enum ipset_adt adt, u32 *lineno, u32 flags) { - if (req->to - req->from > MAX_RANGE) { - ip_set_printk("range too big, %d elements (max %d)", - req->to - req->from + 1, MAX_RANGE+1); - return -ENOEXEC; + struct chash *h = set->data; + struct nlattr *tb[IPSET_ATTR_ADT_MAX]; + ipset_adtfn adtfn = set->variant->adt[adt]; + struct hash_ipportip6_elem data = {}; + u32 timeout = h->timeout; + + if (nla_parse(tb, IPSET_ATTR_ADT_MAX, head, len, + hash_ipportip6_adt_policy)) + return -IPSET_ERR_PROTOCOL; + + if (tb[IPSET_ATTR_IP]) + memcpy(&data.ip, nla_data(tb[IPSET_ATTR_IP]), + sizeof(struct in6_addr)); + else + return -IPSET_ERR_PROTOCOL; + + if (tb[IPSET_ATTR_IP2]) + memcpy(&data.ip2, nla_data(tb[IPSET_ATTR_IP2]), + sizeof(struct in6_addr)); + else + return -IPSET_ERR_PROTOCOL; + + if (tb[IPSET_ATTR_PORT]) + data.port = ip_set_get_n16(tb[IPSET_ATTR_PORT]); + else + return -IPSET_ERR_PROTOCOL; + + if (tb[IPSET_ATTR_TIMEOUT]) { + if (!with_timeout(h->timeout)) + return -IPSET_ERR_TIMEOUT; + timeout = ip_set_timeout_uget(tb[IPSET_ATTR_TIMEOUT]); } - map->first_ip = req->from; - map->last_ip = req->to; - return 0; + + return adtfn(set, &data, GFP_KERNEL, timeout); } -HASH_CREATE(ipportiphash, struct ipportip) -HASH_DESTROY(ipportiphash) -HASH_FLUSH(ipportiphash, struct ipportip) +/* Create hash:ip type of sets */ -static inline void -__ipportiphash_list_header(const struct ip_set_ipportiphash *map, - struct ip_set_req_ipportiphash_create *header) +static const struct nla_policy +hash_ipportip_create_policy[IPSET_ATTR_CREATE_MAX+1] __read_mostly = { + [IPSET_ATTR_HASHSIZE] = { .type = NLA_U32 }, + [IPSET_ATTR_MAXELEM] = { .type = NLA_U32 }, + [IPSET_ATTR_PROBES] = { .type = NLA_U8 }, + [IPSET_ATTR_RESIZE] = { .type = NLA_U8 }, + [IPSET_ATTR_TIMEOUT] = { .type = NLA_U32 }, +}; + +static int +hash_ipportip_create(struct ip_set *set, struct nlattr *head, + int len, u32 flags) { - header->from = map->first_ip; - header->to = map->last_ip; -} + struct nlattr *tb[IPSET_ATTR_CREATE_MAX]; + u32 hashsize = IPSET_DEFAULT_HASHSIZE, maxelem = IPSET_DEFAULT_MAXELEM; + struct chash *h; -HASH_LIST_HEADER(ipportiphash) -HASH_LIST_MEMBERS_SIZE(ipportiphash, struct ipportip) -HASH_LIST_MEMBERS_MEMCPY(ipportiphash, struct ipportip, - (elem->ip || elem->ip1)) + if (!(set->family == AF_INET || set->family == AF_INET6)) + return -IPSET_ERR_INVALID_FAMILY; -IP_SET_RTYPE(ipportiphash, IPSET_TYPE_IP | IPSET_TYPE_PORT - | IPSET_TYPE_IP1 | IPSET_DATA_TRIPLE) + if (nla_parse(tb, IPSET_ATTR_CREATE_MAX, head, len, + hash_ipportip_create_policy)) + return -IPSET_ERR_PROTOCOL; -MODULE_LICENSE("GPL"); -MODULE_AUTHOR("Jozsef Kadlecsik "); -MODULE_DESCRIPTION("ipportiphash type of IP sets"); -module_param(limit, int, 0600); -MODULE_PARM_DESC(limit, "maximal number of elements stored in the sets"); + if (tb[IPSET_ATTR_HASHSIZE]) { + hashsize = ip_set_get_h32(tb[IPSET_ATTR_HASHSIZE]); + if (hashsize < IPSET_MIMINAL_HASHSIZE) + hashsize = IPSET_MIMINAL_HASHSIZE; + } + + if (tb[IPSET_ATTR_MAXELEM]) + maxelem = ip_set_get_h32(tb[IPSET_ATTR_MAXELEM]); + + h = kzalloc(sizeof(*h), GFP_KERNEL); + if (!h) + return -ENOMEM; + + h->maxelem = maxelem; + h->htable_bits = htable_bits(hashsize); + h->array_size = CHASH_DEFAULT_ARRAY_SIZE; + h->chain_limit = CHASH_DEFAULT_CHAIN_LIMIT; + get_random_bytes(&h->initval, sizeof(h->initval)); + h->timeout = IPSET_NO_TIMEOUT; + + h->htable = ip_set_alloc(jhash_size(h->htable_bits) * sizeof(struct slist), + GFP_KERNEL, &set->flags); + if (!h->htable) { + kfree(h); + return -ENOMEM; + } + + set->data = h; + + if (tb[IPSET_ATTR_TIMEOUT]) { + h->timeout = ip_set_timeout_uget(tb[IPSET_ATTR_TIMEOUT]); + + set->variant = set->family == AF_INET + ? &hash_ipportip4_tvariant : &hash_ipportip6_tvariant; + + if (set->family == AF_INET) + hash_ipportip4_gc_init(set); + else + hash_ipportip6_gc_init(set); + } else { + set->variant = set->family == AF_INET + ? &hash_ipportip4_variant : &hash_ipportip6_variant; + } + + pr_debug("create %s hashsize %u (%u) maxelem %u: %p(%p)", + set->name, jhash_size(h->htable_bits), + h->htable_bits, h->maxelem, set->data, h->htable); + + return 0; +} + +static struct ip_set_type hash_ipportip_type = { + .name = "hash:ip,port,ip", + .protocol = IPSET_PROTOCOL, + .features = IPSET_TYPE_IP | IPSET_TYPE_PORT | IPSET_TYPE_IP2, + .dimension = IPSET_DIM_THREE, + .family = AF_UNSPEC, + .revision = 0, + .create = hash_ipportip_create, + .me = THIS_MODULE, +}; + +static int __init +hash_ipportip_init(void) +{ + return ip_set_type_register(&hash_ipportip_type); +} + +static void __exit +hash_ipportip_fini(void) +{ + ip_set_type_unregister(&hash_ipportip_type); +} -REGISTER_MODULE(ipportiphash) +module_init(hash_ipportip_init); +module_exit(hash_ipportip_fini); diff --git a/kernel/ip_set_hash_ipportnet.c b/kernel/ip_set_hash_ipportnet.c index e0bb352..dfe9348 100644 --- a/kernel/ip_set_hash_ipportnet.c +++ b/kernel/ip_set_hash_ipportnet.c @@ -1,298 +1,554 @@ -/* Copyright (C) 2008 Jozsef Kadlecsik +/* Copyright (C) 2003-2010 Jozsef Kadlecsik * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as * published by the Free Software Foundation. */ -/* Kernel module implementing an ip+port+net hash set */ +/* Kernel module implementing an IP set type: the hash:ip,port,net type */ +#include +#include #include -#include #include -#include -#include #include -#include #include #include #include #include #include - #include +#include +#include +#include -#include -#include +#include +#include +#include +#include +#include -static int limit = MAX_RANGE; +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Jozsef Kadlecsik "); +MODULE_DESCRIPTION("hash:ip,port,net type of IP sets"); +MODULE_ALIAS("ip_set_hash:ip,port,net"); + +/* Type specific function prefix */ +#define TYPE hash_ipportnet + +static bool +hash_ipportnet_same_set(const struct ip_set *a, const struct ip_set *b); + +#define hash_ipportnet4_same_set hash_ipportnet_same_set +#define hash_ipportnet6_same_set hash_ipportnet_same_set + +/* The type variant functions: IPv4 */ + +/* Member elements without timeout */ +struct hash_ipportnet4_elem { + u32 ip; + u32 ip2; + u16 port; + u8 cidr; + u8 match; +}; + +/* Member elements with timeout support */ +struct hash_ipportnet4_telem { + u32 ip; + u32 ip2; + u16 port; + u8 cidr; + u8 match; + unsigned long timeout; +}; + +static inline bool +hash_ipportnet4_data_equal(const struct hash_ipportnet4_elem *ip1, + const struct hash_ipportnet4_elem *ip2) +{ + return ip1->ip == ip2->ip + && ip1->ip2 == ip2->ip2 + && ip1->cidr == ip2->cidr + && ip1->port == ip2->port; +} -#define jhash_ip2(map, i, ipport, ip1) \ - jhash_2words(ipport, ip1, *(map->initval + i)) +static inline bool +hash_ipportnet4_data_isnull(const struct hash_ipportnet4_elem *elem) +{ + return elem->match == 0; +} -static inline __u32 -ipportnethash_id_cidr(struct ip_set *set, - ip_set_ip_t ip, ip_set_ip_t port, - ip_set_ip_t ip1, uint8_t cidr) +static inline void +hash_ipportnet4_data_copy(struct hash_ipportnet4_elem *dst, + const struct hash_ipportnet4_elem *src) { - struct ip_set_ipportnethash *map = set->data; - __u32 id; - u_int16_t i; - struct ipportip *elem; - - ip = pack_ip_port(map, ip, port); - ip1 = pack_ip_cidr(ip1, cidr); - if (!(ip || ip1)) - return UINT_MAX; - - for (i = 0; i < map->probes; i++) { - id = jhash_ip2(map, i, ip, ip1) % map->hashsize; - DP("hash key: %u", id); - elem = HARRAY_ELEM(map->members, struct ipportip *, id); - if (elem->ip == ip && elem->ip1 == ip1) - return id; - /* No shortcut - there can be deleted entries. */ - } - return UINT_MAX; + memcpy(dst, src, sizeof(*dst)); + dst->match = 1; } -static inline __u32 -ipportnethash_id(struct ip_set *set, - ip_set_ip_t ip, ip_set_ip_t port, ip_set_ip_t ip1) +static inline void +hash_ipportnet4_data_swap(struct hash_ipportnet4_elem *dst, + struct hash_ipportnet4_elem *src) { - struct ip_set_ipportnethash *map = set->data; - __u32 id = UINT_MAX; - int i; - - for (i = 0; i < 30 && map->cidr[i]; i++) { - id = ipportnethash_id_cidr(set, ip, port, ip1, map->cidr[i]); - if (id != UINT_MAX) - break; - } - return id; + struct hash_ipportnet4_elem tmp; + + memcpy(&tmp, dst, sizeof(tmp)); + memcpy(dst, src, sizeof(tmp)); + memcpy(src, &tmp, sizeof(tmp)); +} + +static inline void +hash_ipportnet4_data_netmask(struct hash_ipportnet4_elem *elem, u8 cidr) +{ + elem->ip2 &= NETMASK(cidr); + elem->cidr = cidr; } -static inline int -ipportnethash_test_cidr(struct ip_set *set, - ip_set_ip_t ip, ip_set_ip_t port, - ip_set_ip_t ip1, uint8_t cidr) +static inline void +hash_ipportnet4_data_zero_out(struct hash_ipportnet4_elem *elem) { - struct ip_set_ipportnethash *map = set->data; - - if (ip < map->first_ip || ip > map->last_ip) - return -ERANGE; + elem->match = 0; +} + +static inline bool +hash_ipportnet4_data_list(struct sk_buff *skb, + const struct hash_ipportnet4_elem *data) +{ + NLA_PUT_NET32(skb, IPSET_ATTR_IP, data->ip); + NLA_PUT_NET32(skb, IPSET_ATTR_IP2, data->ip2); + NLA_PUT_NET16(skb, IPSET_ATTR_PORT, data->port); + NLA_PUT_U8(skb, IPSET_ATTR_CIDR2, data->cidr); + return 0; - return (ipportnethash_id_cidr(set, ip, port, ip1, cidr) != UINT_MAX); +nla_put_failure: + return 1; } -static inline int -ipportnethash_test(struct ip_set *set, - ip_set_ip_t ip, ip_set_ip_t port, ip_set_ip_t ip1) +static inline bool +hash_ipportnet4_data_tlist(struct sk_buff *skb, + const struct hash_ipportnet4_elem *data) { - struct ip_set_ipportnethash *map = set->data; - - if (ip < map->first_ip || ip > map->last_ip) - return -ERANGE; + const struct hash_ipportnet4_telem *tdata = + (const struct hash_ipportnet4_telem *)data; + + NLA_PUT_NET32(skb, IPSET_ATTR_IP, tdata->ip); + NLA_PUT_NET32(skb, IPSET_ATTR_IP2, tdata->ip2); + NLA_PUT_NET16(skb, IPSET_ATTR_PORT, tdata->port); + NLA_PUT_U8(skb, IPSET_ATTR_CIDR2, data->cidr); + NLA_PUT_NET32(skb, IPSET_ATTR_TIMEOUT, + htonl(ip_set_timeout_get(tdata->timeout))); - return (ipportnethash_id(set, ip, port, ip1) != UINT_MAX); + return 0; + +nla_put_failure: + return 1; } +#define IP_SET_HASH_WITH_NETS + +#define PF 4 +#define HOST_MASK 32 +#include + static int -ipportnethash_utest(struct ip_set *set, const void *data, u_int32_t size) +hash_ipportnet4_kadt(struct ip_set *set, const struct sk_buff *skb, + enum ipset_adt adt, u8 pf, u8 dim, u8 flags) { - const struct ip_set_req_ipportnethash *req = data; + struct chash *h = set->data; + ipset_adtfn adtfn = set->variant->adt[adt]; + struct hash_ipportnet4_elem data = + { .cidr = h->nets[0].cidr || HOST_MASK }; + + if (data.cidr == 0) + return -EINVAL; + if (adt == IPSET_TEST) + data.cidr = HOST_MASK; - if (req->cidr <= 0 || req->cidr > 32) + ip4addrptr(skb, flags & IPSET_DIM_ONE_SRC, &data.ip); + if (!get_port(AF_INET, skb, flags & IPSET_DIM_TWO_SRC, &data.port)) return -EINVAL; - return (req->cidr == 32 - ? ipportnethash_test(set, req->ip, req->port, req->ip1) - : ipportnethash_test_cidr(set, req->ip, req->port, - req->ip1, req->cidr)); + ip4addrptr(skb, flags & IPSET_DIM_THREE_SRC, &data.ip2); + data.ip2 &= NETMASK(data.cidr); + + return adtfn(set, &data, GFP_ATOMIC, h->timeout); } -#define KADT_CONDITION \ - ip_set_ip_t port, ip1; \ - \ - if (flags[2] == 0) \ - return 0; \ - \ - port = get_port(skb, flags++); \ - ip1 = ipaddr(skb, flags++); \ - \ - if (port == INVALID_PORT) \ - return 0; - -KADT(ipportnethash, test, ipaddr, port, ip1) - -static inline int -__ipportnet_add(struct ip_set_ipportnethash *map, - ip_set_ip_t ip, ip_set_ip_t ip1) +static const struct nla_policy +hash_ipportnet4_adt_policy[IPSET_ATTR_ADT_MAX + 1] __read_mostly = { + [IPSET_ATTR_IP] = { .type = NLA_U32 }, + [IPSET_ATTR_IP2] = { .type = NLA_U32 }, + [IPSET_ATTR_PORT] = { .type = NLA_U16 }, + [IPSET_ATTR_CIDR2] = { .type = NLA_U8 }, + [IPSET_ATTR_TIMEOUT] = { .type = NLA_U32 }, +}; + +static int +hash_ipportnet4_uadt(struct ip_set *set, struct nlattr *head, int len, + enum ipset_adt adt, u32 *lineno, u32 flags) { - __u32 probe; - u_int16_t i; - struct ipportip *elem, *slot = NULL; - - for (i = 0; i < map->probes; i++) { - probe = jhash_ip2(map, i, ip, ip1) % map->hashsize; - elem = HARRAY_ELEM(map->members, struct ipportip *, probe); - if (elem->ip == ip && elem->ip1 == ip1) - return -EEXIST; - if (!(slot || elem->ip || elem->ip1)) - slot = elem; - /* There can be deleted entries, must check all slots */ + struct chash *h = set->data; + struct nlattr *tb[IPSET_ATTR_ADT_MAX]; + bool eexist = flags & IPSET_FLAG_EXIST; + ipset_adtfn adtfn = set->variant->adt[adt]; + struct hash_ipportnet4_elem data = { .cidr = HOST_MASK }; + u32 timeout = h->timeout; + int ret; + + if (nla_parse(tb, IPSET_ATTR_ADT_MAX, head, len, + hash_ipportnet4_adt_policy)) + return -IPSET_ERR_PROTOCOL; + + if (tb[IPSET_ATTR_IP]) + data.ip = ip_set_get_n32(tb[IPSET_ATTR_IP]); + else + return -IPSET_ERR_PROTOCOL; + + if (tb[IPSET_ATTR_IP2]) + data.ip2 = ip_set_get_n32(tb[IPSET_ATTR_IP2]); + else + return -IPSET_ERR_PROTOCOL; + + if (tb[IPSET_ATTR_CIDR2]) + data.cidr = nla_get_u8(tb[IPSET_ATTR_CIDR2]); + + if (!data.cidr) + return -IPSET_ERR_INVALID_CIDR; + + data.ip2 &= NETMASK(data.cidr); + + if (tb[IPSET_ATTR_PORT]) + data.port = ip_set_get_n16(tb[IPSET_ATTR_PORT]); + else + return -IPSET_ERR_PROTOCOL; + + if (tb[IPSET_ATTR_TIMEOUT]) { + if (!with_timeout(h->timeout)) + return -IPSET_ERR_TIMEOUT; + timeout = ip_set_timeout_uget(tb[IPSET_ATTR_TIMEOUT]); } - if (slot) { - slot->ip = ip; - slot->ip1 = ip1; - map->elements++; - return 0; + + ret = adtfn(set, &data, GFP_KERNEL, timeout); + + if (ret && !(ret == -IPSET_ERR_EXIST && eexist)) { + if (tb[IPSET_ATTR_LINENO]) + *lineno = nla_get_u32(tb[IPSET_ATTR_LINENO]); } - /* Trigger rehashing */ - return -EAGAIN; + return ret; } -static inline int -__ipportnethash_add(struct ip_set_ipportnethash *map, - struct ipportip *elem) +static bool +hash_ipportnet_same_set(const struct ip_set *a, const struct ip_set *b) { - return __ipportnet_add(map, elem->ip, elem->ip1); + struct chash *x = a->data; + struct chash *y = b->data; + + return x->maxelem == y->maxelem + && x->timeout == y->timeout + && x->htable_bits == y->htable_bits /* resizing ? */ + && x->array_size == y->array_size + && x->chain_limit == y->chain_limit; } -static inline int -ipportnethash_add(struct ip_set *set, - ip_set_ip_t ip, ip_set_ip_t port, - ip_set_ip_t ip1, uint8_t cidr) +/* The type variant functions: IPv6 */ + +struct hash_ipportnet6_elem { + union nf_inet_addr ip; + union nf_inet_addr ip2; + u16 port; + u8 cidr; + u8 match; +}; + +struct hash_ipportnet6_telem { + union nf_inet_addr ip; + union nf_inet_addr ip2; + u16 port; + u8 cidr; + u8 match; + unsigned long timeout; +}; + +static inline bool +hash_ipportnet6_data_equal(const struct hash_ipportnet6_elem *ip1, + const struct hash_ipportnet6_elem *ip2) { - struct ip_set_ipportnethash *map = set->data; - struct ipportip; - int ret; - - if (map->elements > limit) - return -ERANGE; - if (ip < map->first_ip || ip > map->last_ip) - return -ERANGE; - if (cidr <= 0 || cidr >= 32) - return -EINVAL; - if (map->nets[cidr-1] == UINT16_MAX) - return -ERANGE; + return ipv6_addr_cmp(&ip1->ip.in6, &ip2->ip.in6) == 0 + && ipv6_addr_cmp(&ip1->ip2.in6, &ip2->ip2.in6) == 0 + && ip1->cidr == ip2->cidr + && ip1->port == ip2->port; +} - ip = pack_ip_port(map, ip, port); - ip1 = pack_ip_cidr(ip1, cidr); - if (!(ip || ip1)) - return -ERANGE; - - ret =__ipportnet_add(map, ip, ip1); - if (ret == 0) { - if (!map->nets[cidr-1]++) - add_cidr_size(map->cidr, cidr); - } - return ret; +static inline bool +hash_ipportnet6_data_isnull(const struct hash_ipportnet6_elem *elem) +{ + return elem->match == 0; } -#undef KADT_CONDITION -#define KADT_CONDITION \ - struct ip_set_ipportnethash *map = set->data; \ - uint8_t cidr = map->cidr[0] ? map->cidr[0] : 31; \ - ip_set_ip_t port, ip1; \ - \ - if (flags[2] == 0) \ - return 0; \ - \ - port = get_port(skb, flags++); \ - ip1 = ipaddr(skb, flags++); \ - \ - if (port == INVALID_PORT) \ - return 0; - -UADT(ipportnethash, add, req->port, req->ip1, req->cidr) -KADT(ipportnethash, add, ipaddr, port, ip1, cidr) +static inline void +hash_ipportnet6_data_copy(struct hash_ipportnet6_elem *dst, + const struct hash_ipportnet6_elem *src) +{ + memcpy(dst, src, sizeof(*dst)); + dst->match = 1; +} static inline void -__ipportnethash_retry(struct ip_set_ipportnethash *tmp, - struct ip_set_ipportnethash *map) +hash_ipportnet6_data_swap(struct hash_ipportnet6_elem *dst, + struct hash_ipportnet6_elem *src) { - tmp->first_ip = map->first_ip; - tmp->last_ip = map->last_ip; - memcpy(tmp->cidr, map->cidr, sizeof(tmp->cidr)); - memcpy(tmp->nets, map->nets, sizeof(tmp->nets)); + struct hash_ipportnet6_elem tmp; + + memcpy(&tmp, dst, sizeof(tmp)); + memcpy(dst, src, sizeof(tmp)); + memcpy(src, &tmp, sizeof(tmp)); } -HASH_RETRY2(ipportnethash, struct ipportip) +static inline void +hash_ipportnet6_data_zero_out(struct hash_ipportnet6_elem *elem) +{ + elem->match = 0; +} -static inline int -ipportnethash_del(struct ip_set *set, - ip_set_ip_t ip, ip_set_ip_t port, - ip_set_ip_t ip1, uint8_t cidr) +static inline void +ip6_netmask(union nf_inet_addr *ip, u8 prefix) { - struct ip_set_ipportnethash *map = set->data; - ip_set_ip_t id; - struct ipportip *elem; + ip->ip6[0] &= NETMASK6(prefix)[0]; + ip->ip6[1] &= NETMASK6(prefix)[1]; + ip->ip6[2] &= NETMASK6(prefix)[2]; + ip->ip6[3] &= NETMASK6(prefix)[3]; +} - if (ip < map->first_ip || ip > map->last_ip) - return -ERANGE; - if (!ip) - return -ERANGE; - if (cidr <= 0 || cidr >= 32) - return -EINVAL; +static inline void +hash_ipportnet6_data_netmask(struct hash_ipportnet6_elem *elem, u8 cidr) +{ + ip6_netmask(&elem->ip2, cidr); + elem->cidr = cidr; +} - id = ipportnethash_id_cidr(set, ip, port, ip1, cidr); +static inline bool +hash_ipportnet6_data_list(struct sk_buff *skb, + const struct hash_ipportnet6_elem *data) +{ + NLA_PUT(skb, IPSET_ATTR_IP, sizeof(struct in6_addr), &data->ip); + NLA_PUT(skb, IPSET_ATTR_IP2, sizeof(struct in6_addr), &data->ip2); + NLA_PUT_NET16(skb, IPSET_ATTR_PORT, data->port); + NLA_PUT_U8(skb, IPSET_ATTR_CIDR2, data->cidr); + return 0; - if (id == UINT_MAX) - return -EEXIST; - - elem = HARRAY_ELEM(map->members, struct ipportip *, id); - elem->ip = elem->ip1 = 0; - map->elements--; - if (!map->nets[cidr-1]--) - del_cidr_size(map->cidr, cidr); +nla_put_failure: + return 1; +} +static inline bool +hash_ipportnet6_data_tlist(struct sk_buff *skb, + const struct hash_ipportnet6_elem *data) +{ + const struct hash_ipportnet6_telem *e = + (const struct hash_ipportnet6_telem *)data; + + NLA_PUT(skb, IPSET_ATTR_IP, sizeof(struct in6_addr), &e->ip); + NLA_PUT(skb, IPSET_ATTR_IP2, sizeof(struct in6_addr), &data->ip2); + NLA_PUT_NET16(skb, IPSET_ATTR_PORT, data->port); + NLA_PUT_U8(skb, IPSET_ATTR_CIDR2, data->cidr); + NLA_PUT_NET32(skb, IPSET_ATTR_TIMEOUT, + htonl(ip_set_timeout_get(e->timeout))); return 0; + +nla_put_failure: + return 1; } -UADT(ipportnethash, del, req->port, req->ip1, req->cidr) -KADT(ipportnethash, del, ipaddr, port, ip1, cidr) +#undef PF +#undef HOST_MASK + +#define PF 6 +#define HOST_MASK 128 +#include -static inline int -__ipportnethash_create(const struct ip_set_req_ipportnethash_create *req, - struct ip_set_ipportnethash *map) +static int +hash_ipportnet6_kadt(struct ip_set *set, const struct sk_buff *skb, + enum ipset_adt adt, u8 pf, u8 dim, u8 flags) { - if (req->to - req->from > MAX_RANGE) { - ip_set_printk("range too big, %d elements (max %d)", - req->to - req->from + 1, MAX_RANGE+1); - return -ENOEXEC; - } - map->first_ip = req->from; - map->last_ip = req->to; - memset(map->cidr, 0, sizeof(map->cidr)); - memset(map->nets, 0, sizeof(map->nets)); - return 0; + struct chash *h = set->data; + ipset_adtfn adtfn = set->variant->adt[adt]; + struct hash_ipportnet6_elem data = + { .cidr = h->nets[0].cidr || HOST_MASK }; + + if (data.cidr == 0) + return -EINVAL; + if (adt == IPSET_TEST) + data.cidr = HOST_MASK; + + ip6addrptr(skb, flags & IPSET_DIM_ONE_SRC, &data.ip.in6); + if (!get_port(AF_INET, skb, flags & IPSET_DIM_TWO_SRC, &data.port)) + return -EINVAL; + ip6addrptr(skb, flags & IPSET_DIM_THREE_SRC, &data.ip2.in6); + ip6_netmask(&data.ip2, data.cidr); + + return adtfn(set, &data, GFP_ATOMIC, h->timeout); } -HASH_CREATE(ipportnethash, struct ipportip) -HASH_DESTROY(ipportnethash) -HASH_FLUSH_CIDR(ipportnethash, struct ipportip); +static const struct nla_policy +hash_ipportnet6_adt_policy[IPSET_ATTR_ADT_MAX + 1] __read_mostly = { + [IPSET_ATTR_IP] = { .type = NLA_BINARY, + .len = sizeof(struct in6_addr) }, + [IPSET_ATTR_IP2] = { .type = NLA_BINARY, + .len = sizeof(struct in6_addr) }, + [IPSET_ATTR_PORT] = { .type = NLA_U16 }, + [IPSET_ATTR_CIDR2] = { .type = NLA_U8 }, + [IPSET_ATTR_TIMEOUT] = { .type = NLA_U32 }, +}; -static inline void -__ipportnethash_list_header(const struct ip_set_ipportnethash *map, - struct ip_set_req_ipportnethash_create *header) +static int +hash_ipportnet6_uadt(struct ip_set *set, struct nlattr *head, int len, + enum ipset_adt adt, u32 *lineno, u32 flags) { - header->from = map->first_ip; - header->to = map->last_ip; + struct chash *h = set->data; + struct nlattr *tb[IPSET_ATTR_ADT_MAX]; + ipset_adtfn adtfn = set->variant->adt[adt]; + struct hash_ipportnet6_elem data = { .cidr = HOST_MASK }; + u32 timeout = h->timeout; + + if (nla_parse(tb, IPSET_ATTR_ADT_MAX, head, len, + hash_ipportnet6_adt_policy)) + return -IPSET_ERR_PROTOCOL; + + if (tb[IPSET_ATTR_IP]) + memcpy(&data.ip, nla_data(tb[IPSET_ATTR_IP]), + sizeof(struct in6_addr)); + else + return -IPSET_ERR_PROTOCOL; + + if (tb[IPSET_ATTR_IP2]) + memcpy(&data.ip2, nla_data(tb[IPSET_ATTR_IP2]), + sizeof(struct in6_addr)); + else + return -IPSET_ERR_PROTOCOL; + + if (tb[IPSET_ATTR_CIDR2]) + data.cidr = nla_get_u8(tb[IPSET_ATTR_CIDR2]); + + if (!data.cidr) + return -IPSET_ERR_INVALID_CIDR; + + ip6_netmask(&data.ip2, data.cidr); + + if (tb[IPSET_ATTR_PORT]) + data.port = ip_set_get_n16(tb[IPSET_ATTR_PORT]); + else + return -IPSET_ERR_PROTOCOL; + + if (tb[IPSET_ATTR_TIMEOUT]) { + if (!with_timeout(h->timeout)) + return -IPSET_ERR_TIMEOUT; + timeout = ip_set_timeout_uget(tb[IPSET_ATTR_TIMEOUT]); + } + + return adtfn(set, &data, GFP_KERNEL, timeout); } -HASH_LIST_HEADER(ipportnethash) +/* Create hash:ip type of sets */ + +static const struct nla_policy +hash_ipportnet_create_policy[IPSET_ATTR_CREATE_MAX+1] __read_mostly = { + [IPSET_ATTR_HASHSIZE] = { .type = NLA_U32 }, + [IPSET_ATTR_MAXELEM] = { .type = NLA_U32 }, + [IPSET_ATTR_PROBES] = { .type = NLA_U8 }, + [IPSET_ATTR_RESIZE] = { .type = NLA_U8 }, + [IPSET_ATTR_TIMEOUT] = { .type = NLA_U32 }, +}; + +static int +hash_ipportnet_create(struct ip_set *set, struct nlattr *head, + int len, u32 flags) +{ + struct nlattr *tb[IPSET_ATTR_CREATE_MAX]; + u32 hashsize = IPSET_DEFAULT_HASHSIZE, maxelem = IPSET_DEFAULT_MAXELEM; + struct chash *h; -HASH_LIST_MEMBERS_SIZE(ipportnethash, struct ipportip) -HASH_LIST_MEMBERS_MEMCPY(ipportnethash, struct ipportip, - (elem->ip || elem->ip1)) + if (!(set->family == AF_INET || set->family == AF_INET6)) + return -IPSET_ERR_INVALID_FAMILY; -IP_SET_RTYPE(ipportnethash, IPSET_TYPE_IP | IPSET_TYPE_PORT - | IPSET_TYPE_IP1 | IPSET_DATA_TRIPLE) + if (nla_parse(tb, IPSET_ATTR_CREATE_MAX, head, len, + hash_ipportnet_create_policy)) + return -IPSET_ERR_PROTOCOL; -MODULE_LICENSE("GPL"); -MODULE_AUTHOR("Jozsef Kadlecsik "); -MODULE_DESCRIPTION("ipportnethash type of IP sets"); -module_param(limit, int, 0600); -MODULE_PARM_DESC(limit, "maximal number of elements stored in the sets"); + if (tb[IPSET_ATTR_HASHSIZE]) { + hashsize = ip_set_get_h32(tb[IPSET_ATTR_HASHSIZE]); + if (hashsize < IPSET_MIMINAL_HASHSIZE) + hashsize = IPSET_MIMINAL_HASHSIZE; + } + + if (tb[IPSET_ATTR_MAXELEM]) + maxelem = ip_set_get_h32(tb[IPSET_ATTR_MAXELEM]); + + h = kzalloc(sizeof(*h) + + sizeof(struct chash_nets) + * (set->family == AF_INET ? 31 : 127), GFP_KERNEL); + if (!h) + return -ENOMEM; + + h->maxelem = maxelem; + h->htable_bits = htable_bits(hashsize); + h->array_size = CHASH_DEFAULT_ARRAY_SIZE; + h->chain_limit = CHASH_DEFAULT_CHAIN_LIMIT; + get_random_bytes(&h->initval, sizeof(h->initval)); + h->timeout = IPSET_NO_TIMEOUT; + + h->htable = ip_set_alloc(jhash_size(h->htable_bits) * sizeof(struct slist), + GFP_KERNEL, &set->flags); + if (!h->htable) { + kfree(h); + return -ENOMEM; + } + + set->data = h; + + if (tb[IPSET_ATTR_TIMEOUT]) { + h->timeout = ip_set_timeout_uget(tb[IPSET_ATTR_TIMEOUT]); + + set->variant = set->family == AF_INET + ? &hash_ipportnet4_tvariant : &hash_ipportnet6_tvariant; + + if (set->family == AF_INET) + hash_ipportnet4_gc_init(set); + else + hash_ipportnet6_gc_init(set); + } else { + set->variant = set->family == AF_INET + ? &hash_ipportnet4_variant : &hash_ipportnet6_variant; + } + + pr_debug("create %s hashsize %u (%u) maxelem %u: %p(%p)", + set->name, jhash_size(h->htable_bits), + h->htable_bits, h->maxelem, set->data, h->htable); + + return 0; +} + +static struct ip_set_type hash_ipportnet_type = { + .name = "hash:ip,port,net", + .protocol = IPSET_PROTOCOL, + .features = IPSET_TYPE_IP | IPSET_TYPE_PORT | IPSET_TYPE_IP2, + .dimension = IPSET_DIM_THREE, + .family = AF_UNSPEC, + .revision = 0, + .create = hash_ipportnet_create, + .me = THIS_MODULE, +}; + +static int __init +hash_ipportnet_init(void) +{ + return ip_set_type_register(&hash_ipportnet_type); +} + +static void __exit +hash_ipportnet_fini(void) +{ + ip_set_type_unregister(&hash_ipportnet_type); +} -REGISTER_MODULE(ipportnethash) +module_init(hash_ipportnet_init); +module_exit(hash_ipportnet_fini); diff --git a/kernel/ip_set_hash_net.c b/kernel/ip_set_hash_net.c index e3b09e0..a8611c2 100644 --- a/kernel/ip_set_hash_net.c +++ b/kernel/ip_set_hash_net.c @@ -1,218 +1,488 @@ -/* Copyright (C) 2003-2008 Jozsef Kadlecsik +/* Copyright (C) 2003-2010 Jozsef Kadlecsik * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as * published by the Free Software Foundation. */ -/* Kernel module implementing a cidr nethash set */ +/* Kernel module implementing an IP set type: the hash:net type */ +#include +#include #include -#include #include #include -#include #include #include #include #include #include - #include +#include +#include +#include + +#include +#include +#include +#include + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Jozsef Kadlecsik "); +MODULE_DESCRIPTION("hash:net type of IP sets"); +MODULE_ALIAS("ip_set_hash:net"); + +/* Type specific function prefix */ +#define TYPE hash_net + +static bool +hash_net_same_set(const struct ip_set *a, const struct ip_set *b); + +#define hash_net4_same_set hash_net_same_set +#define hash_net6_same_set hash_net_same_set -#include +/* The type variant functions: IPv4 */ -static int limit = MAX_RANGE; +/* Member elements without timeout */ +struct hash_net4_elem { + u32 ip; + u8 cidr; /* Not hashed, zero for null value */ +}; -static inline __u32 -nethash_id_cidr(const struct ip_set_nethash *map, - ip_set_ip_t ip, - uint8_t cidr) +/* Member elements with timeout support */ +struct hash_net4_telem { + u32 ip; + u8 cidr; /* Not hashed, zero for null value */ + unsigned long timeout; +}; + +static inline bool +hash_net4_data_equal(const struct hash_net4_elem *ip1, + const struct hash_net4_elem *ip2) { - __u32 id; - u_int16_t i; - ip_set_ip_t *elem; + /* We don't have to check the cidr equality + * because overlapping nets cannot be added to the set + */ + return ip1->ip == ip2->ip; +} - ip = pack_ip_cidr(ip, cidr); - if (!ip) - return MAX_RANGE; - - for (i = 0; i < map->probes; i++) { - id = jhash_ip(map, i, ip) % map->hashsize; - DP("hash key: %u", id); - elem = HARRAY_ELEM(map->members, ip_set_ip_t *, id); - if (*elem == ip) - return id; - /* No shortcut - there can be deleted entries. */ - } - return UINT_MAX; +static inline bool +hash_net4_data_isnull(const struct hash_net4_elem *elem) +{ + return elem->cidr == 0; } -static inline __u32 -nethash_id(struct ip_set *set, ip_set_ip_t ip) +static inline void +hash_net4_data_copy(struct hash_net4_elem *dst, + const struct hash_net4_elem *src) { - const struct ip_set_nethash *map = set->data; - __u32 id = UINT_MAX; - int i; + dst->ip = src->ip; + dst->cidr = src->cidr; +} - for (i = 0; i < 30 && map->cidr[i]; i++) { - id = nethash_id_cidr(map, ip, map->cidr[i]); - if (id != UINT_MAX) - break; - } - return id; +static inline void +hash_net4_data_swap(struct hash_net4_elem *dst, + struct hash_net4_elem *src) +{ + swap(dst->ip, src->ip); + swap(dst->cidr, src->cidr); } -static inline int -nethash_test_cidr(struct ip_set *set, ip_set_ip_t ip, uint8_t cidr) +static inline void +hash_net4_data_netmask(struct hash_net4_elem *elem, u8 cidr) { - const struct ip_set_nethash *map = set->data; + elem->ip &= NETMASK(cidr); + elem->cidr = cidr; +} - return (nethash_id_cidr(map, ip, cidr) != UINT_MAX); +/* Zero CIDR values cannot be stored */ +static inline void +hash_net4_data_zero_out(struct hash_net4_elem *elem) +{ + elem->cidr = 0; } -static inline int -nethash_test(struct ip_set *set, ip_set_ip_t ip) +static inline bool +hash_net4_data_list(struct sk_buff *skb, const struct hash_net4_elem *data) { - return (nethash_id(set, ip) != UINT_MAX); + NLA_PUT_NET32(skb, IPSET_ATTR_IP, data->ip); + NLA_PUT_U8(skb, IPSET_ATTR_CIDR, data->cidr); + return 0; + +nla_put_failure: + return 1; } -static int -nethash_utest(struct ip_set *set, const void *data, u_int32_t size) +static inline bool +hash_net4_data_tlist(struct sk_buff *skb, const struct hash_net4_elem *data) { - const struct ip_set_req_nethash *req = data; + const struct hash_net4_telem *tdata = + (const struct hash_net4_telem *)data; - if (req->cidr <= 0 || req->cidr > 32) - return -EINVAL; - return (req->cidr == 32 ? nethash_test(set, req->ip) - : nethash_test_cidr(set, req->ip, req->cidr)); + NLA_PUT_NET32(skb, IPSET_ATTR_IP, tdata->ip); + NLA_PUT_U8(skb, IPSET_ATTR_CIDR, tdata->cidr); + NLA_PUT_NET32(skb, IPSET_ATTR_TIMEOUT, + htonl(ip_set_timeout_get(tdata->timeout))); + + return 0; + +nla_put_failure: + return 1; } -#define KADT_CONDITION +#define IP_SET_HASH_WITH_NETS -KADT(nethash, test, ipaddr) +#define PF 4 +#define HOST_MASK 32 +#include -static inline int -__nethash_add(struct ip_set_nethash *map, ip_set_ip_t *ip) +static int +hash_net4_kadt(struct ip_set *set, const struct sk_buff *skb, + enum ipset_adt adt, u8 pf, u8 dim, u8 flags) { - __u32 probe; - u_int16_t i; - ip_set_ip_t *elem, *slot = NULL; + struct chash *h = set->data; + ipset_adtfn adtfn = set->variant->adt[adt]; + struct hash_net4_elem data = { .cidr = h->nets[0].cidr || HOST_MASK }; - for (i = 0; i < map->probes; i++) { - probe = jhash_ip(map, i, *ip) % map->hashsize; - elem = HARRAY_ELEM(map->members, ip_set_ip_t *, probe); - if (*elem == *ip) - return -EEXIST; - if (!(slot || *elem)) - slot = elem; - /* There can be deleted entries, must check all slots */ - } - if (slot) { - *slot = *ip; - map->elements++; - return 0; - } - /* Trigger rehashing */ - return -EAGAIN; + if (data.cidr == 0) + return -EINVAL; + if (adt == IPSET_TEST) + data.cidr = HOST_MASK; + + ip4addrptr(skb, flags & IPSET_DIM_ONE_SRC, &data.ip); + data.ip &= NETMASK(data.cidr); + + return adtfn(set, &data, GFP_ATOMIC, h->timeout); } -static inline int -nethash_add(struct ip_set *set, ip_set_ip_t ip, uint8_t cidr) +static const struct nla_policy +hash_net4_adt_policy[IPSET_ATTR_ADT_MAX + 1] __read_mostly = { + [IPSET_ATTR_IP] = { .type = NLA_U32 }, + [IPSET_ATTR_CIDR] = { .type = NLA_U8 }, + [IPSET_ATTR_TIMEOUT] = { .type = NLA_U32 }, +}; + +static int +hash_net4_uadt(struct ip_set *set, struct nlattr *head, int len, + enum ipset_adt adt, u32 *lineno, u32 flags) { - struct ip_set_nethash *map = set->data; + struct chash *h = set->data; + struct nlattr *tb[IPSET_ATTR_ADT_MAX]; + bool eexist = flags & IPSET_FLAG_EXIST; + ipset_adtfn adtfn = set->variant->adt[adt]; + struct hash_net4_elem data = { .cidr = HOST_MASK }; + u32 timeout = h->timeout; int ret; - - if (map->elements >= limit || map->nets[cidr-1] == UINT16_MAX) - return -ERANGE; - if (cidr <= 0 || cidr >= 32) - return -EINVAL; - ip = pack_ip_cidr(ip, cidr); - if (!ip) - return -ERANGE; - - ret = __nethash_add(map, &ip); - if (ret == 0) { - if (!map->nets[cidr-1]++) - add_cidr_size(map->cidr, cidr); + if (nla_parse(tb, IPSET_ATTR_ADT_MAX, head, len, + hash_net4_adt_policy)) + return -IPSET_ERR_PROTOCOL; + + if (tb[IPSET_ATTR_IP]) + data.ip = ip_set_get_n32(tb[IPSET_ATTR_IP]); + else + return -IPSET_ERR_PROTOCOL; + + if (tb[IPSET_ATTR_CIDR]) + data.cidr = nla_get_u8(tb[IPSET_ATTR_CIDR]); + + if (!data.cidr) + return -IPSET_ERR_INVALID_CIDR; + + data.ip &= NETMASK(data.cidr); + + if (tb[IPSET_ATTR_TIMEOUT]) { + if (!with_timeout(h->timeout)) + return -IPSET_ERR_TIMEOUT; + timeout = ip_set_timeout_uget(tb[IPSET_ATTR_TIMEOUT]); + } + + ret = adtfn(set, &data, GFP_KERNEL, timeout); + + if (ret && !(ret == -IPSET_ERR_EXIST && eexist)) { + if (tb[IPSET_ATTR_LINENO]) + *lineno = nla_get_u32(tb[IPSET_ATTR_LINENO]); } - return ret; } -#undef KADT_CONDITION -#define KADT_CONDITION \ - struct ip_set_nethash *map = set->data; \ - uint8_t cidr = map->cidr[0] ? map->cidr[0] : 31; +static bool +hash_net_same_set(const struct ip_set *a, const struct ip_set *b) +{ + struct chash *x = a->data; + struct chash *y = b->data; + + return x->maxelem == y->maxelem + && x->timeout == y->timeout + && x->htable_bits == y->htable_bits /* resizing ? */ + && x->array_size == y->array_size + && x->chain_limit == y->chain_limit; +} + +/* The type variant functions: IPv6 */ + +struct hash_net6_elem { + union nf_inet_addr ip; + u8 cidr; /* Not hashed */ +}; -UADT(nethash, add, req->cidr) -KADT(nethash, add, ipaddr, cidr) +struct hash_net6_telem { + union nf_inet_addr ip; + u8 cidr; /* Not hashed */ + unsigned long timeout; +}; + +static inline bool +hash_net6_data_equal(const struct hash_net6_elem *ip1, + const struct hash_net6_elem *ip2) +{ + return ipv6_addr_cmp(&ip1->ip.in6, &ip2->ip.in6) == 0; +} + +static inline bool +hash_net6_data_isnull(const struct hash_net6_elem *elem) +{ + return elem->cidr == 0; +} static inline void -__nethash_retry(struct ip_set_nethash *tmp, struct ip_set_nethash *map) +hash_net6_data_copy(struct hash_net6_elem *dst, + const struct hash_net6_elem *src) { - memcpy(tmp->cidr, map->cidr, sizeof(tmp->cidr)); - memcpy(tmp->nets, map->nets, sizeof(tmp->nets)); + ipv6_addr_copy(&dst->ip.in6, &src->ip.in6); + dst->cidr = src->cidr; } -HASH_RETRY(nethash, ip_set_ip_t) +static inline void +hash_net6_data_swap(struct hash_net6_elem *dst, struct hash_net6_elem *src) +{ + struct hash_net6_elem tmp; + + memcpy(&tmp, dst, sizeof(tmp)); + memcpy(dst, src, sizeof(tmp)); + memcpy(src, &tmp, sizeof(tmp)); +} -static inline int -nethash_del(struct ip_set *set, ip_set_ip_t ip, uint8_t cidr) +static inline void +hash_net6_data_zero_out(struct hash_net6_elem *elem) { - struct ip_set_nethash *map = set->data; - ip_set_ip_t id, *elem; + elem->cidr = 0; +} - if (cidr <= 0 || cidr >= 32) - return -EINVAL; - - id = nethash_id_cidr(map, ip, cidr); - if (id == UINT_MAX) - return -EEXIST; - - elem = HARRAY_ELEM(map->members, ip_set_ip_t *, id); - *elem = 0; - map->elements--; - if (!map->nets[cidr-1]--) - del_cidr_size(map->cidr, cidr); - return 0; +static inline void +ip6_netmask(union nf_inet_addr *ip, u8 prefix) +{ + ip->ip6[0] &= NETMASK6(prefix)[0]; + ip->ip6[1] &= NETMASK6(prefix)[1]; + ip->ip6[2] &= NETMASK6(prefix)[2]; + ip->ip6[3] &= NETMASK6(prefix)[3]; } -UADT(nethash, del, req->cidr) -KADT(nethash, del, ipaddr, cidr) +static inline void +hash_net6_data_netmask(struct hash_net6_elem *elem, u8 cidr) +{ + ip6_netmask(&elem->ip, cidr); + elem->cidr = cidr; +} -static inline int -__nethash_create(const struct ip_set_req_nethash_create *req, - struct ip_set_nethash *map) +static inline bool +hash_net6_data_list(struct sk_buff *skb, const struct hash_net6_elem *data) { - memset(map->cidr, 0, sizeof(map->cidr)); - memset(map->nets, 0, sizeof(map->nets)); + NLA_PUT(skb, IPSET_ATTR_IP, sizeof(struct in6_addr), &data->ip); + NLA_PUT_U8(skb, IPSET_ATTR_CIDR, data->cidr); + return 0; + +nla_put_failure: + return 1; +} + +static inline bool +hash_net6_data_tlist(struct sk_buff *skb, const struct hash_net6_elem *data) +{ + const struct hash_net6_telem *e = + (const struct hash_net6_telem *)data; + NLA_PUT(skb, IPSET_ATTR_IP, sizeof(struct in6_addr), &e->ip); + NLA_PUT_U8(skb, IPSET_ATTR_CIDR, e->cidr); + NLA_PUT_NET32(skb, IPSET_ATTR_TIMEOUT, + htonl(ip_set_timeout_get(e->timeout))); return 0; + +nla_put_failure: + return 1; } -HASH_CREATE(nethash, ip_set_ip_t) -HASH_DESTROY(nethash) +#undef PF +#undef HOST_MASK -HASH_FLUSH_CIDR(nethash, ip_set_ip_t) +#define PF 6 +#define HOST_MASK 128 +#include -static inline void -__nethash_list_header(const struct ip_set_nethash *map, - struct ip_set_req_nethash_create *header) -{ +static int +hash_net6_kadt(struct ip_set *set, const struct sk_buff *skb, + enum ipset_adt adt, u8 pf, u8 dim, u8 flags) +{ + struct chash *h = set->data; + ipset_adtfn adtfn = set->variant->adt[adt]; + struct hash_net6_elem data = { .cidr = h->nets[0].cidr || HOST_MASK }; + + if (data.cidr == 0) + return -EINVAL; + if (adt == IPSET_TEST) + data.cidr = HOST_MASK; + + ip6addrptr(skb, flags & IPSET_DIM_ONE_SRC, &data.ip.in6); + ip6_netmask(&data.ip, data.cidr); + + return adtfn(set, &data, GFP_ATOMIC, h->timeout); +} + +static const struct nla_policy +hash_net6_adt_policy[IPSET_ATTR_ADT_MAX + 1] __read_mostly = { + [IPSET_ATTR_IP] = { .type = NLA_BINARY, + .len = sizeof(struct in6_addr) }, + [IPSET_ATTR_CIDR] = { .type = NLA_U8 }, + [IPSET_ATTR_TIMEOUT] = { .type = NLA_U32 }, +}; + +static int +hash_net6_uadt(struct ip_set *set, struct nlattr *head, int len, + enum ipset_adt adt, u32 *lineno, u32 flags) +{ + struct chash *h = set->data; + struct nlattr *tb[IPSET_ATTR_ADT_MAX]; + ipset_adtfn adtfn = set->variant->adt[adt]; + struct hash_net6_elem data = { .cidr = HOST_MASK }; + u32 timeout = h->timeout; + + if (nla_parse(tb, IPSET_ATTR_ADT_MAX, head, len, + hash_net6_adt_policy)) + return -IPSET_ERR_PROTOCOL; + + if (tb[IPSET_ATTR_IP]) + memcpy(&data.ip, nla_data(tb[IPSET_ATTR_IP]), + sizeof(struct in6_addr)); + else + return -IPSET_ERR_PROTOCOL; + + if (tb[IPSET_ATTR_CIDR]) + data.cidr = nla_get_u8(tb[IPSET_ATTR_CIDR]); + + if (!data.cidr) + return -IPSET_ERR_INVALID_CIDR; + + ip6_netmask(&data.ip, data.cidr); + + if (tb[IPSET_ATTR_TIMEOUT]) { + if (!with_timeout(h->timeout)) + return -IPSET_ERR_TIMEOUT; + timeout = ip_set_timeout_uget(tb[IPSET_ATTR_TIMEOUT]); + } + + return adtfn(set, &data, GFP_KERNEL, timeout); } -HASH_LIST_HEADER(nethash) -HASH_LIST_MEMBERS_SIZE(nethash, ip_set_ip_t) -HASH_LIST_MEMBERS(nethash, ip_set_ip_t) +/* Create hash:ip type of sets */ -IP_SET_RTYPE(nethash, IPSET_TYPE_IP | IPSET_DATA_SINGLE) +static const struct nla_policy +hash_net_create_policy[IPSET_ATTR_CREATE_MAX+1] __read_mostly = { + [IPSET_ATTR_HASHSIZE] = { .type = NLA_U32 }, + [IPSET_ATTR_MAXELEM] = { .type = NLA_U32 }, + [IPSET_ATTR_PROBES] = { .type = NLA_U8 }, + [IPSET_ATTR_RESIZE] = { .type = NLA_U8 }, + [IPSET_ATTR_TIMEOUT] = { .type = NLA_U32 }, +}; -MODULE_LICENSE("GPL"); -MODULE_AUTHOR("Jozsef Kadlecsik "); -MODULE_DESCRIPTION("nethash type of IP sets"); -module_param(limit, int, 0600); -MODULE_PARM_DESC(limit, "maximal number of elements stored in the sets"); +static int +hash_net_create(struct ip_set *set, struct nlattr *head, int len, u32 flags) +{ + struct nlattr *tb[IPSET_ATTR_CREATE_MAX]; + u32 hashsize = IPSET_DEFAULT_HASHSIZE, maxelem = IPSET_DEFAULT_MAXELEM; + struct chash *h; + + if (!(set->family == AF_INET || set->family == AF_INET6)) + return -IPSET_ERR_INVALID_FAMILY; + + if (nla_parse(tb, IPSET_ATTR_CREATE_MAX, head, len, + hash_net_create_policy)) + return -IPSET_ERR_PROTOCOL; + + if (tb[IPSET_ATTR_HASHSIZE]) { + hashsize = ip_set_get_h32(tb[IPSET_ATTR_HASHSIZE]); + if (hashsize < IPSET_MIMINAL_HASHSIZE) + hashsize = IPSET_MIMINAL_HASHSIZE; + } + + if (tb[IPSET_ATTR_MAXELEM]) + maxelem = ip_set_get_h32(tb[IPSET_ATTR_MAXELEM]); + + h = kzalloc(sizeof(*h) + + sizeof(struct chash_nets) + * (set->family == AF_INET ? 31 : 127), GFP_KERNEL); + if (!h) + return -ENOMEM; + + h->maxelem = maxelem; + h->htable_bits = htable_bits(hashsize); + h->array_size = CHASH_DEFAULT_ARRAY_SIZE; + h->chain_limit = CHASH_DEFAULT_CHAIN_LIMIT; + get_random_bytes(&h->initval, sizeof(h->initval)); + h->timeout = IPSET_NO_TIMEOUT; + + h->htable = ip_set_alloc(jhash_size(h->htable_bits) * sizeof(struct slist), + GFP_KERNEL, &set->flags); + if (!h->htable) { + kfree(h); + return -ENOMEM; + } + + set->data = h; + + if (tb[IPSET_ATTR_TIMEOUT]) { + h->timeout = ip_set_timeout_uget(tb[IPSET_ATTR_TIMEOUT]); + + set->variant = set->family == AF_INET + ? &hash_net4_tvariant : &hash_net6_tvariant; + + if (set->family == AF_INET) + hash_net4_gc_init(set); + else + hash_net6_gc_init(set); + } else { + set->variant = set->family == AF_INET + ? &hash_net4_variant : &hash_net6_variant; + } + + pr_debug("create %s hashsize %u (%u) maxelem %u: %p(%p)", + set->name, jhash_size(h->htable_bits), + h->htable_bits, h->maxelem, set->data, h->htable); + + return 0; +} + +static struct ip_set_type hash_net_type = { + .name = "hash:net", + .protocol = IPSET_PROTOCOL, + .features = IPSET_TYPE_IP, + .dimension = IPSET_DIM_ONE, + .family = AF_UNSPEC, + .revision = 0, + .create = hash_net_create, + .me = THIS_MODULE, +}; + +static int __init +hash_net_init(void) +{ + return ip_set_type_register(&hash_net_type); +} + +static void __exit +hash_net_fini(void) +{ + ip_set_type_unregister(&hash_net_type); +} -REGISTER_MODULE(nethash) +module_init(hash_net_init); +module_exit(hash_net_fini); diff --git a/kernel/ip_set_iptreemap.c b/kernel/ip_set_iptreemap.c deleted file mode 100644 index 02f657e..0000000 --- a/kernel/ip_set_iptreemap.c +++ /dev/null @@ -1,700 +0,0 @@ -/* Copyright (C) 2007 Sven Wegener - * - * This program is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 as published by - * the Free Software Foundation. - */ - -/* This modules implements the iptreemap ipset type. It uses bitmaps to - * represent every single IPv4 address as a bit. The bitmaps are managed in a - * tree structure, where the first three octets of an address are used as an - * index to find the bitmap and the last octet is used as the bit number. - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include - -#define IPTREEMAP_DEFAULT_GC_TIME (5 * 60) -#define IPTREEMAP_DESTROY_SLEEP (100) - -static __KMEM_CACHE_T__ *cachep_b; -static __KMEM_CACHE_T__ *cachep_c; -static __KMEM_CACHE_T__ *cachep_d; - -static struct ip_set_iptreemap_d *fullbitmap_d; -static struct ip_set_iptreemap_c *fullbitmap_c; -static struct ip_set_iptreemap_b *fullbitmap_b; - -#if defined(__LITTLE_ENDIAN) -#define ABCD(a, b, c, d, addr) \ - do { \ - a = ((unsigned char *)addr)[3]; \ - b = ((unsigned char *)addr)[2]; \ - c = ((unsigned char *)addr)[1]; \ - d = ((unsigned char *)addr)[0]; \ - } while (0) -#elif defined(__BIG_ENDIAN) -#define ABCD(a,b,c,d,addrp) do { \ - a = ((unsigned char *)addrp)[0]; \ - b = ((unsigned char *)addrp)[1]; \ - c = ((unsigned char *)addrp)[2]; \ - d = ((unsigned char *)addrp)[3]; \ -} while (0) -#else -#error "Please fix asm/byteorder.h" -#endif /* __LITTLE_ENDIAN */ - -#define TESTIP_WALK(map, elem, branch, full) \ - do { \ - branch = (map)->tree[elem]; \ - if (!branch) \ - return 0; \ - else if (branch == full) \ - return 1; \ - } while (0) - -#define ADDIP_WALK(map, elem, branch, type, cachep, full) \ - do { \ - branch = (map)->tree[elem]; \ - if (!branch) { \ - branch = (type *) kmem_cache_alloc(cachep, GFP_ATOMIC); \ - if (!branch) \ - return -ENOMEM; \ - memset(branch, 0, sizeof(*branch)); \ - (map)->tree[elem] = branch; \ - } else if (branch == full) { \ - return -EEXIST; \ - } \ - } while (0) - -#define ADDIP_RANGE_LOOP(map, a, a1, a2, hint, branch, full, cachep, free) \ - for (a = a1; a <= a2; a++) { \ - branch = (map)->tree[a]; \ - if (branch != full) { \ - if ((a > a1 && a < a2) || (hint)) { \ - if (branch) \ - free(branch); \ - (map)->tree[a] = full; \ - continue; \ - } else if (!branch) { \ - branch = kmem_cache_alloc(cachep, GFP_ATOMIC); \ - if (!branch) \ - return -ENOMEM; \ - memset(branch, 0, sizeof(*branch)); \ - (map)->tree[a] = branch; \ - } - -#define ADDIP_RANGE_LOOP_END() \ - } \ - } - -#define DELIP_WALK(map, elem, branch, cachep, full, flags) \ - do { \ - branch = (map)->tree[elem]; \ - if (!branch) { \ - return -EEXIST; \ - } else if (branch == full) { \ - branch = kmem_cache_alloc(cachep, flags); \ - if (!branch) \ - return -ENOMEM; \ - memcpy(branch, full, sizeof(*full)); \ - (map)->tree[elem] = branch; \ - } \ - } while (0) - -#define DELIP_RANGE_LOOP(map, a, a1, a2, hint, branch, full, cachep, free, flags) \ - for (a = a1; a <= a2; a++) { \ - branch = (map)->tree[a]; \ - if (branch) { \ - if ((a > a1 && a < a2) || (hint)) { \ - if (branch != full) \ - free(branch); \ - (map)->tree[a] = NULL; \ - continue; \ - } else if (branch == full) { \ - branch = kmem_cache_alloc(cachep, flags); \ - if (!branch) \ - return -ENOMEM; \ - memcpy(branch, full, sizeof(*branch)); \ - (map)->tree[a] = branch; \ - } - -#define DELIP_RANGE_LOOP_END() \ - } \ - } - -#define LOOP_WALK_BEGIN(map, i, branch) \ - for (i = 0; i < 256; i++) { \ - branch = (map)->tree[i]; \ - if (likely(!branch)) \ - continue; - -#define LOOP_WALK_END() \ - } - -#define LOOP_WALK_BEGIN_GC(map, i, branch, full, cachep, count) \ - count = -256; \ - for (i = 0; i < 256; i++) { \ - branch = (map)->tree[i]; \ - if (likely(!branch)) \ - continue; \ - count++; \ - if (branch == full) { \ - count++; \ - continue; \ - } - -#define LOOP_WALK_END_GC(map, i, branch, full, cachep, count) \ - if (-256 == count) { \ - kmem_cache_free(cachep, branch); \ - (map)->tree[i] = NULL; \ - } else if (256 == count) { \ - kmem_cache_free(cachep, branch); \ - (map)->tree[i] = full; \ - } \ - } - -#define LOOP_WALK_BEGIN_COUNT(map, i, branch, inrange, count) \ - for (i = 0; i < 256; i++) { \ - if (!(map)->tree[i]) { \ - if (inrange) { \ - count++; \ - inrange = 0; \ - } \ - continue; \ - } \ - branch = (map)->tree[i]; - -#define LOOP_WALK_END_COUNT() \ - } - -#define GETVALUE1(a, a1, b1, r) \ - (a == a1 ? b1 : r) - -#define GETVALUE2(a, b, a1, b1, c1, r) \ - (a == a1 && b == b1 ? c1 : r) - -#define GETVALUE3(a, b, c, a1, b1, c1, d1, r) \ - (a == a1 && b == b1 && c == c1 ? d1 : r) - -#define CHECK1(a, a1, a2, b1, b2, c1, c2, d1, d2) \ - ( \ - GETVALUE1(a, a1, b1, 0) == 0 \ - && GETVALUE1(a, a2, b2, 255) == 255 \ - && c1 == 0 \ - && c2 == 255 \ - && d1 == 0 \ - && d2 == 255 \ - ) - -#define CHECK2(a, b, a1, a2, b1, b2, c1, c2, d1, d2) \ - ( \ - GETVALUE2(a, b, a1, b1, c1, 0) == 0 \ - && GETVALUE2(a, b, a2, b2, c2, 255) == 255 \ - && d1 == 0 \ - && d2 == 255 \ - ) - -#define CHECK3(a, b, c, a1, a2, b1, b2, c1, c2, d1, d2) \ - ( \ - GETVALUE3(a, b, c, a1, b1, c1, d1, 0) == 0 \ - && GETVALUE3(a, b, c, a2, b2, c2, d2, 255) == 255 \ - ) - - -static inline void -free_d(struct ip_set_iptreemap_d *map) -{ - kmem_cache_free(cachep_d, map); -} - -static inline void -free_c(struct ip_set_iptreemap_c *map) -{ - struct ip_set_iptreemap_d *dtree; - unsigned int i; - - LOOP_WALK_BEGIN(map, i, dtree) { - if (dtree != fullbitmap_d) - free_d(dtree); - } LOOP_WALK_END(); - - kmem_cache_free(cachep_c, map); -} - -static inline void -free_b(struct ip_set_iptreemap_b *map) -{ - struct ip_set_iptreemap_c *ctree; - unsigned int i; - - LOOP_WALK_BEGIN(map, i, ctree) { - if (ctree != fullbitmap_c) - free_c(ctree); - } LOOP_WALK_END(); - - kmem_cache_free(cachep_b, map); -} - -static inline int -iptreemap_test(struct ip_set *set, ip_set_ip_t ip) -{ - struct ip_set_iptreemap *map = set->data; - struct ip_set_iptreemap_b *btree; - struct ip_set_iptreemap_c *ctree; - struct ip_set_iptreemap_d *dtree; - unsigned char a, b, c, d; - - ABCD(a, b, c, d, &ip); - - TESTIP_WALK(map, a, btree, fullbitmap_b); - TESTIP_WALK(btree, b, ctree, fullbitmap_c); - TESTIP_WALK(ctree, c, dtree, fullbitmap_d); - - return !!test_bit(d, (void *) dtree->bitmap); -} - -#define KADT_CONDITION - -UADT(iptreemap, test) -KADT(iptreemap, test, ipaddr) - -static inline int -__addip_single(struct ip_set *set, ip_set_ip_t ip) -{ - struct ip_set_iptreemap *map = (struct ip_set_iptreemap *) set->data; - struct ip_set_iptreemap_b *btree; - struct ip_set_iptreemap_c *ctree; - struct ip_set_iptreemap_d *dtree; - unsigned char a, b, c, d; - - ABCD(a, b, c, d, &ip); - - ADDIP_WALK(map, a, btree, struct ip_set_iptreemap_b, cachep_b, fullbitmap_b); - ADDIP_WALK(btree, b, ctree, struct ip_set_iptreemap_c, cachep_c, fullbitmap_c); - ADDIP_WALK(ctree, c, dtree, struct ip_set_iptreemap_d, cachep_d, fullbitmap_d); - - if (__test_and_set_bit(d, (void *) dtree->bitmap)) - return -EEXIST; - - __set_bit(b, (void *) btree->dirty); - - return 0; -} - -static inline int -iptreemap_add(struct ip_set *set, ip_set_ip_t start, ip_set_ip_t end) -{ - struct ip_set_iptreemap *map = set->data; - struct ip_set_iptreemap_b *btree; - struct ip_set_iptreemap_c *ctree; - struct ip_set_iptreemap_d *dtree; - unsigned int a, b, c, d; - unsigned char a1, b1, c1, d1; - unsigned char a2, b2, c2, d2; - - if (start == end) - return __addip_single(set, start); - - ABCD(a1, b1, c1, d1, &start); - ABCD(a2, b2, c2, d2, &end); - - /* This is sooo ugly... */ - ADDIP_RANGE_LOOP(map, a, a1, a2, CHECK1(a, a1, a2, b1, b2, c1, c2, d1, d2), btree, fullbitmap_b, cachep_b, free_b) { - ADDIP_RANGE_LOOP(btree, b, GETVALUE1(a, a1, b1, 0), GETVALUE1(a, a2, b2, 255), CHECK2(a, b, a1, a2, b1, b2, c1, c2, d1, d2), ctree, fullbitmap_c, cachep_c, free_c) { - ADDIP_RANGE_LOOP(ctree, c, GETVALUE2(a, b, a1, b1, c1, 0), GETVALUE2(a, b, a2, b2, c2, 255), CHECK3(a, b, c, a1, a2, b1, b2, c1, c2, d1, d2), dtree, fullbitmap_d, cachep_d, free_d) { - for (d = GETVALUE3(a, b, c, a1, b1, c1, d1, 0); d <= GETVALUE3(a, b, c, a2, b2, c2, d2, 255); d++) - __set_bit(d, (void *) dtree->bitmap); - __set_bit(b, (void *) btree->dirty); - } ADDIP_RANGE_LOOP_END(); - } ADDIP_RANGE_LOOP_END(); - } ADDIP_RANGE_LOOP_END(); - - return 0; -} - -UADT0(iptreemap, add, min(req->ip, req->end), max(req->ip, req->end)) -KADT(iptreemap, add, ipaddr, ip) - -static inline int -__delip_single(struct ip_set *set, ip_set_ip_t ip, gfp_t flags) -{ - struct ip_set_iptreemap *map = set->data; - struct ip_set_iptreemap_b *btree; - struct ip_set_iptreemap_c *ctree; - struct ip_set_iptreemap_d *dtree; - unsigned char a,b,c,d; - - ABCD(a, b, c, d, &ip); - - DELIP_WALK(map, a, btree, cachep_b, fullbitmap_b, flags); - DELIP_WALK(btree, b, ctree, cachep_c, fullbitmap_c, flags); - DELIP_WALK(ctree, c, dtree, cachep_d, fullbitmap_d, flags); - - if (!__test_and_clear_bit(d, (void *) dtree->bitmap)) - return -EEXIST; - - __set_bit(b, (void *) btree->dirty); - - return 0; -} - -static inline int -iptreemap_del(struct ip_set *set, - ip_set_ip_t start, ip_set_ip_t end, gfp_t flags) -{ - struct ip_set_iptreemap *map = set->data; - struct ip_set_iptreemap_b *btree; - struct ip_set_iptreemap_c *ctree; - struct ip_set_iptreemap_d *dtree; - unsigned int a, b, c, d; - unsigned char a1, b1, c1, d1; - unsigned char a2, b2, c2, d2; - - if (start == end) - return __delip_single(set, start, flags); - - ABCD(a1, b1, c1, d1, &start); - ABCD(a2, b2, c2, d2, &end); - - /* This is sooo ugly... */ - DELIP_RANGE_LOOP(map, a, a1, a2, CHECK1(a, a1, a2, b1, b2, c1, c2, d1, d2), btree, fullbitmap_b, cachep_b, free_b, flags) { - DELIP_RANGE_LOOP(btree, b, GETVALUE1(a, a1, b1, 0), GETVALUE1(a, a2, b2, 255), CHECK2(a, b, a1, a2, b1, b2, c1, c2, d1, d2), ctree, fullbitmap_c, cachep_c, free_c, flags) { - DELIP_RANGE_LOOP(ctree, c, GETVALUE2(a, b, a1, b1, c1, 0), GETVALUE2(a, b, a2, b2, c2, 255), CHECK3(a, b, c, a1, a2, b1, b2, c1, c2, d1, d2), dtree, fullbitmap_d, cachep_d, free_d, flags) { - for (d = GETVALUE3(a, b, c, a1, b1, c1, d1, 0); d <= GETVALUE3(a, b, c, a2, b2, c2, d2, 255); d++) - __clear_bit(d, (void *) dtree->bitmap); - __set_bit(b, (void *) btree->dirty); - } DELIP_RANGE_LOOP_END(); - } DELIP_RANGE_LOOP_END(); - } DELIP_RANGE_LOOP_END(); - - return 0; -} - -UADT0(iptreemap, del, min(req->ip, req->end), max(req->ip, req->end), GFP_KERNEL) -KADT(iptreemap, del, ipaddr, ip, GFP_ATOMIC) - -/* Check the status of the bitmap - * -1 == all bits cleared - * 1 == all bits set - * 0 == anything else - */ -static inline int -bitmap_status(struct ip_set_iptreemap_d *dtree) -{ - unsigned char first = dtree->bitmap[0]; - int a; - - for (a = 1; a < 32; a++) - if (dtree->bitmap[a] != first) - return 0; - - return (first == 0 ? -1 : (first == 255 ? 1 : 0)); -} - -static void -gc(unsigned long addr) -{ - struct ip_set *set = (struct ip_set *) addr; - struct ip_set_iptreemap *map = set->data; - struct ip_set_iptreemap_b *btree; - struct ip_set_iptreemap_c *ctree; - struct ip_set_iptreemap_d *dtree; - unsigned int a, b, c; - int i, j, k; - - write_lock_bh(&set->lock); - - LOOP_WALK_BEGIN_GC(map, a, btree, fullbitmap_b, cachep_b, i) { - LOOP_WALK_BEGIN_GC(btree, b, ctree, fullbitmap_c, cachep_c, j) { - if (!__test_and_clear_bit(b, (void *) btree->dirty)) - continue; - LOOP_WALK_BEGIN_GC(ctree, c, dtree, fullbitmap_d, cachep_d, k) { - switch (bitmap_status(dtree)) { - case -1: - kmem_cache_free(cachep_d, dtree); - ctree->tree[c] = NULL; - k--; - break; - case 1: - kmem_cache_free(cachep_d, dtree); - ctree->tree[c] = fullbitmap_d; - k++; - break; - } - } LOOP_WALK_END(); - } LOOP_WALK_END_GC(btree, b, ctree, fullbitmap_c, cachep_c, k); - } LOOP_WALK_END_GC(map, a, btree, fullbitmap_b, cachep_b, j); - - write_unlock_bh(&set->lock); - - map->gc.expires = jiffies + map->gc_interval * HZ; - add_timer(&map->gc); -} - -static inline void -init_gc_timer(struct ip_set *set) -{ - struct ip_set_iptreemap *map = set->data; - - init_timer(&map->gc); - map->gc.data = (unsigned long) set; - map->gc.function = gc; - map->gc.expires = jiffies + map->gc_interval * HZ; - add_timer(&map->gc); -} - -static int -iptreemap_create(struct ip_set *set, const void *data, u_int32_t size) -{ - const struct ip_set_req_iptreemap_create *req = data; - struct ip_set_iptreemap *map; - - map = kzalloc(sizeof(*map), GFP_KERNEL); - if (!map) - return -ENOMEM; - - map->gc_interval = req->gc_interval ? req->gc_interval : IPTREEMAP_DEFAULT_GC_TIME; - set->data = map; - - init_gc_timer(set); - - return 0; -} - -static inline void -__flush(struct ip_set_iptreemap *map) -{ - struct ip_set_iptreemap_b *btree; - unsigned int a; - - LOOP_WALK_BEGIN(map, a, btree); - if (btree != fullbitmap_b) - free_b(btree); - LOOP_WALK_END(); -} - -static void -iptreemap_destroy(struct ip_set *set) -{ - struct ip_set_iptreemap *map = set->data; - - while (!del_timer(&map->gc)) - msleep(IPTREEMAP_DESTROY_SLEEP); - - __flush(map); - kfree(map); - - set->data = NULL; -} - -static void -iptreemap_flush(struct ip_set *set) -{ - struct ip_set_iptreemap *map = set->data; - unsigned int gc_interval = map->gc_interval; - - while (!del_timer(&map->gc)) - msleep(IPTREEMAP_DESTROY_SLEEP); - - __flush(map); - - memset(map, 0, sizeof(*map)); - map->gc_interval = gc_interval; - - init_gc_timer(set); -} - -static void -iptreemap_list_header(const struct ip_set *set, void *data) -{ - struct ip_set_iptreemap *map = set->data; - struct ip_set_req_iptreemap_create *header = data; - - header->gc_interval = map->gc_interval; -} - -static int -iptreemap_list_members_size(const struct ip_set *set, char dont_align) -{ - struct ip_set_iptreemap *map = set->data; - struct ip_set_iptreemap_b *btree; - struct ip_set_iptreemap_c *ctree; - struct ip_set_iptreemap_d *dtree; - unsigned int a, b, c, d, inrange = 0, count = 0; - - LOOP_WALK_BEGIN_COUNT(map, a, btree, inrange, count) { - LOOP_WALK_BEGIN_COUNT(btree, b, ctree, inrange, count) { - LOOP_WALK_BEGIN_COUNT(ctree, c, dtree, inrange, count) { - for (d = 0; d < 256; d++) { - if (test_bit(d, (void *) dtree->bitmap)) { - inrange = 1; - } else if (inrange) { - count++; - inrange = 0; - } - } - } LOOP_WALK_END_COUNT(); - } LOOP_WALK_END_COUNT(); - } LOOP_WALK_END_COUNT(); - - if (inrange) - count++; - - return (count * IPSET_VALIGN(sizeof(struct ip_set_req_iptreemap), dont_align)); -} - -static inline void -add_member(void *data, size_t offset, ip_set_ip_t start, ip_set_ip_t end) -{ - struct ip_set_req_iptreemap *entry = data + offset; - - entry->ip = start; - entry->end = end; -} - -static void -iptreemap_list_members(const struct ip_set *set, void *data, char dont_align) -{ - struct ip_set_iptreemap *map = set->data; - struct ip_set_iptreemap_b *btree; - struct ip_set_iptreemap_c *ctree; - struct ip_set_iptreemap_d *dtree; - unsigned int a, b, c, d, inrange = 0; - size_t offset = 0, datasize; - ip_set_ip_t start = 0, end = 0, ip; - - datasize = IPSET_VALIGN(sizeof(struct ip_set_req_iptreemap), dont_align); - LOOP_WALK_BEGIN(map, a, btree) { - LOOP_WALK_BEGIN(btree, b, ctree) { - LOOP_WALK_BEGIN(ctree, c, dtree) { - for (d = 0; d < 256; d++) { - if (test_bit(d, (void *) dtree->bitmap)) { - ip = ((a << 24) | (b << 16) | (c << 8) | d); - if (!inrange) { - inrange = 1; - start = ip; - } else if (end < ip - 1) { - add_member(data, offset, start, end); - offset += datasize; - start = ip; - } - end = ip; - } else if (inrange) { - add_member(data, offset, start, end); - offset += datasize; - inrange = 0; - } - } - } LOOP_WALK_END(); - } LOOP_WALK_END(); - } LOOP_WALK_END(); - - if (inrange) - add_member(data, offset, start, end); -} - -IP_SET_TYPE(iptreemap, IPSET_TYPE_IP | IPSET_DATA_SINGLE) - -MODULE_LICENSE("GPL"); -MODULE_AUTHOR("Sven Wegener "); -MODULE_DESCRIPTION("iptreemap type of IP sets"); - -static int __init ip_set_iptreemap_init(void) -{ - int ret = -ENOMEM; - int a; - - cachep_b = KMEM_CACHE_CREATE("ip_set_iptreemap_b", - sizeof(struct ip_set_iptreemap_b)); - if (!cachep_b) { - ip_set_printk("Unable to create ip_set_iptreemap_b slab cache"); - goto out; - } - - cachep_c = KMEM_CACHE_CREATE("ip_set_iptreemap_c", - sizeof(struct ip_set_iptreemap_c)); - if (!cachep_c) { - ip_set_printk("Unable to create ip_set_iptreemap_c slab cache"); - goto outb; - } - - cachep_d = KMEM_CACHE_CREATE("ip_set_iptreemap_d", - sizeof(struct ip_set_iptreemap_d)); - if (!cachep_d) { - ip_set_printk("Unable to create ip_set_iptreemap_d slab cache"); - goto outc; - } - - fullbitmap_d = kmem_cache_alloc(cachep_d, GFP_KERNEL); - if (!fullbitmap_d) - goto outd; - - fullbitmap_c = kmem_cache_alloc(cachep_c, GFP_KERNEL); - if (!fullbitmap_c) - goto outbitmapd; - - fullbitmap_b = kmem_cache_alloc(cachep_b, GFP_KERNEL); - if (!fullbitmap_b) - goto outbitmapc; - - ret = ip_set_register_set_type(&ip_set_iptreemap); - if (0 > ret) - goto outbitmapb; - - /* Now init our global bitmaps */ - memset(fullbitmap_d->bitmap, 0xff, sizeof(fullbitmap_d->bitmap)); - - for (a = 0; a < 256; a++) - fullbitmap_c->tree[a] = fullbitmap_d; - - for (a = 0; a < 256; a++) - fullbitmap_b->tree[a] = fullbitmap_c; - memset(fullbitmap_b->dirty, 0, sizeof(fullbitmap_b->dirty)); - - return 0; - -outbitmapb: - kmem_cache_free(cachep_b, fullbitmap_b); -outbitmapc: - kmem_cache_free(cachep_c, fullbitmap_c); -outbitmapd: - kmem_cache_free(cachep_d, fullbitmap_d); -outd: - kmem_cache_destroy(cachep_d); -outc: - kmem_cache_destroy(cachep_c); -outb: - kmem_cache_destroy(cachep_b); -out: - - return ret; -} - -static void __exit ip_set_iptreemap_fini(void) -{ - ip_set_unregister_set_type(&ip_set_iptreemap); - kmem_cache_free(cachep_d, fullbitmap_d); - kmem_cache_free(cachep_c, fullbitmap_c); - kmem_cache_free(cachep_b, fullbitmap_b); - kmem_cache_destroy(cachep_d); - kmem_cache_destroy(cachep_c); - kmem_cache_destroy(cachep_b); -} - -module_init(ip_set_iptreemap_init); -module_exit(ip_set_iptreemap_fini); diff --git a/kernel/ip_set_list_set.c b/kernel/ip_set_list_set.c index 3cfdae8..ce6c4d1 100644 --- a/kernel/ip_set_list_set.c +++ b/kernel/ip_set_list_set.c @@ -1,324 +1,589 @@ -/* Copyright (C) 2008 Jozsef Kadlecsik +/* Copyright (C) 2008-2010 Jozsef Kadlecsik * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as * published by the Free Software Foundation. */ -/* Kernel module implementing an IP set type: the setlist type */ +/* Kernel module implementing an IP set type: the list:set type */ +#include #include #include #include #include -#include -#include -#include +#include +#include +#include -/* - * before ==> index, ref - * after ==> ref, index - */ +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Jozsef Kadlecsik "); +MODULE_DESCRIPTION("list:set type of IP sets"); +MODULE_ALIAS("ip_set_list:set"); + +/* Member elements without and with timeout */ +struct set_elem { + ip_set_id_t id; +}; + +struct set_telem { + ip_set_id_t id; + unsigned long timeout; +}; + +/* Type structure */ +struct list_set { + size_t dsize; /* element size */ + u32 size; /* size of set list array */ + u32 timeout; /* timeout value */ + struct timer_list gc; /* garbage collection */ + struct set_elem members[0]; /* the set members */ +}; + +static inline struct set_elem * +list_set_elem(const struct list_set *map, u32 id) +{ + return (struct set_elem *)((char *)map->members + id * map->dsize); +} + +static inline bool +list_set_timeout(const struct list_set *map, u32 id) +{ + const struct set_telem *elem = + (const struct set_telem *) list_set_elem(map, id); + + return ip_set_timeout_test(elem->timeout); +} + +static inline bool +list_set_expired(const struct list_set *map, u32 id) +{ + const struct set_telem *elem = + (const struct set_telem *) list_set_elem(map, id); + + return ip_set_timeout_expired(elem->timeout); +} static inline int -next_index_eq(const struct ip_set_setlist *map, int i, ip_set_id_t index) +list_set_exist(const struct set_telem *elem) { - return i < map->size && map->index[i] == index; + return elem->id != IPSET_INVALID_ID + && !ip_set_timeout_expired(elem->timeout); } +/* Set list without and with timeout */ + static int -setlist_utest(struct ip_set *set, const void *data, u_int32_t size) +list_set_kadt(struct ip_set *set, const struct sk_buff *skb, + enum ipset_adt adt, u8 pf, u8 dim, u8 flags) { - const struct ip_set_setlist *map = set->data; - const struct ip_set_req_setlist *req = data; - ip_set_id_t index, ref = IP_SET_INVALID_ID; - int i, res = 0; - struct ip_set *s; - - if (req->before && req->ref[0] == '\0') - return 0; - - index = __ip_set_get_byname(req->name, &s); - if (index == IP_SET_INVALID_ID) - return 0; - if (req->ref[0] != '\0') { - ref = __ip_set_get_byname(req->ref, &s); - if (ref == IP_SET_INVALID_ID) - goto finish; - } - for (i = 0; i < map->size - && map->index[i] != IP_SET_INVALID_ID; i++) { - if (req->before && map->index[i] == index) { - res = next_index_eq(map, i + 1, ref); - break; - } else if (!req->before) { - if ((ref == IP_SET_INVALID_ID - && map->index[i] == index) - || (map->index[i] == ref - && next_index_eq(map, i + 1, index))) { - res = 1; - break; - } + struct list_set *map = set->data; + struct set_elem *elem; + u32 i; + int ret; + + for (i = 0; i < map->size; i++) { + elem = list_set_elem(map, i); + if (elem->id == IPSET_INVALID_ID) + return 0; + if (with_timeout(map->timeout) && list_set_expired(map, i)) + continue; + switch (adt) { + case IPSET_TEST: + ret = ip_set_test(elem->id, skb, pf, dim, flags); + if (ret > 0) + return ret; + break; + case IPSET_ADD: + ret = ip_set_add(elem->id, skb, pf, dim, flags); + if (ret == 0) + return ret; + break; + case IPSET_DEL: + ret = ip_set_del(elem->id, skb, pf, dim, flags); + if (ret == 0) + return ret; + break; + default: + break; } } - if (ref != IP_SET_INVALID_ID) - __ip_set_put_byindex(ref); -finish: - __ip_set_put_byindex(index); - return res; + return -EINVAL; +} + +static const struct nla_policy +list_set_adt_policy[IPSET_ATTR_ADT_MAX+1] __read_mostly = { + [IPSET_ATTR_NAME] = { .type = NLA_STRING, + .len = IPSET_MAXNAMELEN }, + [IPSET_ATTR_NAMEREF] = { .type = NLA_STRING, + .len = IPSET_MAXNAMELEN }, + [IPSET_ATTR_TIMEOUT] = { .type = NLA_U32 }, + [IPSET_ATTR_CADT_FLAGS] = { .type = NLA_U32 }, +}; + +static inline bool +next_id_eq(const struct list_set *map, u32 i, ip_set_id_t id) +{ + const struct set_elem *elem; + + if (i + 1 < map->size) { + elem = list_set_elem(map, i + 1); + return !!(elem->id == id + && !(with_timeout(map->timeout) + && list_set_expired(map, i + 1))); + } + + return 0; } +static inline void +list_elem_add(struct list_set *map, u32 i, ip_set_id_t id) +{ + struct set_elem *e; + + for (; i < map->size; i++) { + e = list_set_elem(map, i); + swap(e->id, id); + if (e->id == IPSET_INVALID_ID) + break; + } +} + +static inline void +list_elem_tadd(struct list_set *map, u32 i, ip_set_id_t id, + unsigned long timeout) +{ + struct set_telem *e; + + for (; i < map->size; i++) { + e = (struct set_telem *)list_set_elem(map, i); + swap(e->id, id); + if (e->id == IPSET_INVALID_ID) + break; + swap(e->timeout, timeout); + } +} + static int -setlist_ktest(struct ip_set *set, - const struct sk_buff *skb, - const u_int32_t *flags) +list_set_add(struct list_set *map, u32 i, ip_set_id_t id, + unsigned long timeout) { - struct ip_set_setlist *map = set->data; - int i, res = 0; + struct set_elem *e = list_set_elem(map, i); + + if (i == map->size - 1 && e->id != IPSET_INVALID_ID) + /* Last element replaced: e.g. add new,before,last */ + ip_set_put_byindex(e->id); + if (with_timeout(map->timeout)) + list_elem_tadd(map, i, id, timeout); + else + list_elem_add(map, i, id); - for (i = 0; i < map->size - && map->index[i] != IP_SET_INVALID_ID - && res == 0; i++) - res = ip_set_testip_kernel(map->index[i], skb, flags); - return res; + return 0; } -static inline int -insert_setlist(struct ip_set_setlist *map, int i, ip_set_id_t index) +static int +list_set_del(struct list_set *map, ip_set_id_t id, u32 i) { - ip_set_id_t tmp; - int j; + struct set_elem *a = list_set_elem(map, i), *b; - DP("i: %u, last %u\n", i, map->index[map->size - 1]); - if (i >= map->size || map->index[map->size - 1] != IP_SET_INVALID_ID) - return -ERANGE; - - for (j = i; j < map->size - && index != IP_SET_INVALID_ID; j++) { - tmp = map->index[j]; - map->index[j] = index; - index = tmp; + ip_set_put_byindex(id); + + for (; i < map->size - 1; i++) { + b = list_set_elem(map, i + 1); + a->id = b->id; + if (with_timeout(map->timeout)) + ((struct set_telem *)a)->timeout = + ((struct set_telem *)b)->timeout; + a = b; + if (a->id == IPSET_INVALID_ID) + break; } + /* Last element */ + a->id = IPSET_INVALID_ID; return 0; } static int -setlist_uadd(struct ip_set *set, const void *data, u_int32_t size) +list_set_uadt(struct ip_set *set, struct nlattr *head, int len, + enum ipset_adt adt, u32 *lineno, u32 flags) { - struct ip_set_setlist *map = set->data; - const struct ip_set_req_setlist *req = data; - ip_set_id_t index, ref = IP_SET_INVALID_ID; - int i, res = -ERANGE; + struct list_set *map = set->data; + struct nlattr *tb[IPSET_ATTR_ADT_MAX]; + bool eexist = flags & IPSET_FLAG_EXIST, + with_timeout = with_timeout(map->timeout); + int before = 0; + u32 timeout = map->timeout; + ip_set_id_t id, refid = IPSET_INVALID_ID; + struct set_elem *elem; struct ip_set *s; - - if (req->before && req->ref[0] == '\0') - return -EINVAL; - - index = __ip_set_get_byname(req->name, &s); - if (index == IP_SET_INVALID_ID) - return -EEXIST; - /* "Loop detection" */ - if (strcmp(s->type->typename, "setlist") == 0) + u32 i; + int ret = 0; + + if (nla_parse(tb, IPSET_ATTR_ADT_MAX, head, len, + list_set_adt_policy)) + return -IPSET_ERR_PROTOCOL; + + if (tb[IPSET_ATTR_NAME]) { + id = ip_set_get_byname(nla_data(tb[IPSET_ATTR_NAME]), &s); + if (id == IPSET_INVALID_ID) + return -IPSET_ERR_NAME; + /* "Loop detection" */ + if (s->type->features & IPSET_TYPE_NAME) { + ret = -IPSET_ERR_LOOP; + goto finish; + } + } else + return -IPSET_ERR_PROTOCOL; + + if (tb[IPSET_ATTR_CADT_FLAGS]) { + u32 f = ip_set_get_h32(tb[IPSET_ATTR_CADT_FLAGS]); + before = f & IPSET_FLAG_BEFORE; + } + + if (before && !tb[IPSET_ATTR_NAMEREF]) { + ret = -IPSET_ERR_BEFORE; goto finish; + } - if (req->ref[0] != '\0') { - ref = __ip_set_get_byname(req->ref, &s); - if (ref == IP_SET_INVALID_ID) { - res = -EEXIST; + if (tb[IPSET_ATTR_NAMEREF]) { + refid = ip_set_get_byname(nla_data(tb[IPSET_ATTR_NAMEREF]), &s); + if (refid == IPSET_INVALID_ID) { + ret = -IPSET_ERR_NAMEREF; goto finish; } + if (!before) + before = -1; } - for (i = 0; i < map->size; i++) { - if (map->index[i] != ref) - continue; - if (req->before) - res = insert_setlist(map, i, index); - else - res = insert_setlist(map, - ref == IP_SET_INVALID_ID ? i : i + 1, - index); + if (tb[IPSET_ATTR_TIMEOUT]) { + if (!with_timeout) { + ret = -IPSET_ERR_TIMEOUT; + goto finish; + } + timeout = ip_set_timeout_uget(tb[IPSET_ATTR_TIMEOUT]); + } + + switch (adt) { + case IPSET_TEST: + for (i = 0; i < map->size && !ret; i++) { + elem = list_set_elem(map, i); + if (elem->id == IPSET_INVALID_ID + || (before != 0 && i + 1 >= map->size)) + break; + else if (with_timeout && list_set_expired(map, i)) + continue; + else if (before > 0 && elem->id == id) + ret = next_id_eq(map, i, refid); + else if (before < 0 && elem->id == refid) + ret = next_id_eq(map, i, id); + else if (before == 0 && elem->id == id) + ret = 1; + } + break; + case IPSET_ADD: + for (i = 0; i < map->size && !ret; i++) { + elem = list_set_elem(map, i); + if (elem->id == id + && !(with_timeout && list_set_expired(map, i))) + ret = -IPSET_ERR_EXIST; + } + if (ret == -IPSET_ERR_EXIST) + break; + ret = -IPSET_ERR_LIST_FULL; + for (i = 0; i < map->size && ret == -IPSET_ERR_LIST_FULL; i++) { + elem = list_set_elem(map, i); + if (elem->id == IPSET_INVALID_ID) + ret = before != 0 ? -IPSET_ERR_REF_EXIST + : list_set_add(map, i, id, timeout); + else if (elem->id != refid) + continue; + else if (with_timeout && list_set_expired(map, i)) + ret = -IPSET_ERR_REF_EXIST; + else if (before) + ret = list_set_add(map, i, id, timeout); + else if (i + 1 < map->size) + ret = list_set_add(map, i + 1, id, timeout); + } + break; + case IPSET_DEL: + ret = -IPSET_ERR_EXIST; + for (i = 0; i < map->size && ret == -IPSET_ERR_EXIST; i++) { + elem = list_set_elem(map, i); + if (elem->id == IPSET_INVALID_ID) { + ret = before != 0 ? -IPSET_ERR_REF_EXIST + : -IPSET_ERR_EXIST; + break; + } else if (with_timeout && list_set_expired(map, i)) + continue; + else if (elem->id == id + && (before == 0 + || (before > 0 && next_id_eq(map, i, refid)))) + ret = list_set_del(map, id, i); + else if (before < 0 && elem->id == refid + && next_id_eq(map, i, id)) + ret = list_set_del(map, id, i + 1); + } + break; + default: break; } - if (ref != IP_SET_INVALID_ID) - __ip_set_put_byindex(ref); - /* In case of success, we keep the reference to the set */ + finish: - if (res != 0) - __ip_set_put_byindex(index); - return res; + if (refid != IPSET_INVALID_ID) + ip_set_put_byindex(refid); + if (adt != IPSET_ADD || ret) + ip_set_put_byindex(id); + + if (ret && !(ret == -IPSET_ERR_EXIST && eexist)) { + if (tb[IPSET_ATTR_LINENO]) + *lineno = nla_get_u32(tb[IPSET_ATTR_LINENO]); + return ret; + } + return ret; } -static int -setlist_kadd(struct ip_set *set, - const struct sk_buff *skb, - const u_int32_t *flags) +static void +list_set_flush(struct ip_set *set) { - struct ip_set_setlist *map = set->data; - int i, res = -EINVAL; + struct list_set *map = set->data; + struct set_elem *elem; + u32 i; + + for (i = 0; i < map->size; i++) { + elem = list_set_elem(map, i); + if (elem->id != IPSET_INVALID_ID) { + ip_set_put_byindex(elem->id); + elem->id = IPSET_INVALID_ID; + } + } +} + +static void +list_set_destroy(struct ip_set *set) +{ + struct list_set *map = set->data; + + if (with_timeout(map->timeout)) + del_timer_sync(&map->gc); + list_set_flush(set); + kfree(map); - for (i = 0; i < map->size - && map->index[i] != IP_SET_INVALID_ID - && res != 0; i++) - res = ip_set_addip_kernel(map->index[i], skb, flags); - return res; + set->data = NULL; } -static inline int -unshift_setlist(struct ip_set_setlist *map, int i) +static int +list_set_head(struct ip_set *set, struct sk_buff *skb) { - int j; + const struct list_set *map = set->data; + struct nlattr *nested; + + nested = ipset_nest_start(skb, IPSET_ATTR_DATA); + if (!nested) + goto nla_put_failure; + NLA_PUT_NET32(skb, IPSET_ATTR_SIZE, htonl(map->size)); + if (with_timeout(map->timeout)) + NLA_PUT_NET32(skb, IPSET_ATTR_TIMEOUT, htonl(map->timeout)); + NLA_PUT_NET32(skb, IPSET_ATTR_REFERENCES, + htonl(atomic_read(&set->ref) - 1)); + NLA_PUT_NET32(skb, IPSET_ATTR_MEMSIZE, + htonl(sizeof(*map) + map->size * map->dsize)); + ipset_nest_end(skb, nested); - for (j = i; j < map->size - 1; j++) - map->index[j] = map->index[j+1]; - map->index[map->size-1] = IP_SET_INVALID_ID; return 0; +nla_put_failure: + return -EFAULT; } static int -setlist_udel(struct ip_set *set, const void *data, u_int32_t size) +list_set_list(struct ip_set *set, + struct sk_buff *skb, struct netlink_callback *cb) { - struct ip_set_setlist *map = set->data; - const struct ip_set_req_setlist *req = data; - ip_set_id_t index, ref = IP_SET_INVALID_ID; - int i, res = -EEXIST; - struct ip_set *s; - - if (req->before && req->ref[0] == '\0') - return -EINVAL; - - index = __ip_set_get_byname(req->name, &s); - if (index == IP_SET_INVALID_ID) - return -EEXIST; - if (req->ref[0] != '\0') { - ref = __ip_set_get_byname(req->ref, &s); - if (ref == IP_SET_INVALID_ID) + const struct list_set *map = set->data; + struct nlattr *atd, *nested; + u32 i, first = cb->args[2]; + const struct set_elem *e; + + atd = ipset_nest_start(skb, IPSET_ATTR_ADT); + if (!atd) + return -EFAULT; + for (; cb->args[2] < map->size; cb->args[2]++) { + i = cb->args[2]; + e = list_set_elem(map, i); + if (e->id == IPSET_INVALID_ID) goto finish; - } - for (i = 0; i < map->size - && map->index[i] != IP_SET_INVALID_ID; i++) { - if (req->before) { - if (map->index[i] == index - && next_index_eq(map, i + 1, ref)) { - res = unshift_setlist(map, i); - break; - } - } else if (ref == IP_SET_INVALID_ID) { - if (map->index[i] == index) { - res = unshift_setlist(map, i); - break; - } - } else if (map->index[i] == ref - && next_index_eq(map, i + 1, index)) { - res = unshift_setlist(map, i + 1); - break; + if (with_timeout(map->timeout) && list_set_expired(map, i)) + continue; + nested = ipset_nest_start(skb, IPSET_ATTR_DATA); + if (!nested) { + if (i == first) { + nla_nest_cancel(skb, atd); + return -EFAULT; + } else + goto nla_put_failure; } + NLA_PUT_STRING(skb, IPSET_ATTR_NAME, + ip_set_name_byindex(e->id)); + if (with_timeout(map->timeout)) { + const struct set_telem *te = + (const struct set_telem *) e; + NLA_PUT_NET32(skb, IPSET_ATTR_TIMEOUT, + htonl(ip_set_timeout_get(te->timeout))); + } + ipset_nest_end(skb, nested); } - if (ref != IP_SET_INVALID_ID) - __ip_set_put_byindex(ref); finish: - __ip_set_put_byindex(index); - /* In case of success, release the reference to the set */ - if (res == 0) - __ip_set_put_byindex(index); - return res; + ipset_nest_end(skb, atd); + /* Set listing finished */ + cb->args[2] = 0; + return 0; + +nla_put_failure: + nla_nest_cancel(skb, nested); + ipset_nest_end(skb, atd); + return 0; } -static int -setlist_kdel(struct ip_set *set, - const struct sk_buff *skb, - const u_int32_t *flags) +static bool +list_set_same_set(const struct ip_set *a, const struct ip_set *b) { - struct ip_set_setlist *map = set->data; - int i, res = -EINVAL; + struct list_set *x = a->data; + struct list_set *y = b->data; - for (i = 0; i < map->size - && map->index[i] != IP_SET_INVALID_ID - && res != 0; i++) - res = ip_set_delip_kernel(map->index[i], skb, flags); - return res; + return x->size == y->size + && x->timeout == y->timeout; } -static int -setlist_create(struct ip_set *set, const void *data, u_int32_t size) -{ - struct ip_set_setlist *map; - const struct ip_set_req_setlist_create *req = data; - int i; - - map = kmalloc(sizeof(struct ip_set_setlist) + - req->size * sizeof(ip_set_id_t), GFP_KERNEL); - if (!map) - return -ENOMEM; - map->size = req->size; - for (i = 0; i < map->size; i++) - map->index[i] = IP_SET_INVALID_ID; - - set->data = map; - return 0; -} +static const struct ip_set_type_variant list_set __read_mostly = { + .kadt = list_set_kadt, + .uadt = list_set_uadt, + .destroy = list_set_destroy, + .flush = list_set_flush, + .head = list_set_head, + .list = list_set_list, + .same_set = list_set_same_set, +}; static void -setlist_destroy(struct ip_set *set) +list_set_gc(unsigned long ul_set) { - struct ip_set_setlist *map = set->data; - int i; - - for (i = 0; i < map->size - && map->index[i] != IP_SET_INVALID_ID; i++) - __ip_set_put_byindex(map->index[i]); + struct ip_set *set = (struct ip_set *) ul_set; + struct list_set *map = set->data; + struct set_telem *e; + u32 i; - kfree(map); - set->data = NULL; + /* We run parallel with other readers (test element) + * but adding/deleting new entries is locked out */ + read_lock_bh(&set->lock); + for (i = map->size - 1; i >= 0; i--) { + e = (struct set_telem *) list_set_elem(map, i); + if (e->id != IPSET_INVALID_ID + && list_set_expired(map, i)) + list_set_del(map, e->id, i); + } + read_unlock_bh(&set->lock); + + map->gc.expires = jiffies + IPSET_GC_PERIOD(map->timeout) * HZ; + add_timer(&map->gc); } -static void -setlist_flush(struct ip_set *set) +static inline void +list_set_gc_init(struct ip_set *set) { - struct ip_set_setlist *map = set->data; - int i; - - for (i = 0; i < map->size - && map->index[i] != IP_SET_INVALID_ID; i++) { - __ip_set_put_byindex(map->index[i]); - map->index[i] = IP_SET_INVALID_ID; - } + struct list_set *map = set->data; + + init_timer(&map->gc); + map->gc.data = (unsigned long) set; + map->gc.function = list_set_gc; + map->gc.expires = jiffies + IPSET_GC_PERIOD(map->timeout) * HZ; + add_timer(&map->gc); } -static void -setlist_list_header(const struct ip_set *set, void *data) +/* Create list:set type of sets */ + +static const struct nla_policy +list_set_create_policy[IPSET_ATTR_CREATE_MAX+1] __read_mostly = { + [IPSET_ATTR_SIZE] = { .type = NLA_U32 }, + [IPSET_ATTR_TIMEOUT] = { .type = NLA_U32 }, +}; + +static inline bool +init_list_set(struct ip_set *set, u32 size, size_t dsize, + unsigned long timeout) { - const struct ip_set_setlist *map = set->data; - struct ip_set_req_setlist_create *header = data; + struct list_set *map; + struct set_elem *e; + u32 i; - header->size = map->size; + map = kzalloc(sizeof(*map) + size * dsize, GFP_KERNEL); + if (!map) + return false; + + map->size = size; + map->dsize = dsize; + map->timeout = timeout; + set->data = map; + + for (i = 0; i < size; i++) { + e = list_set_elem(map, i); + e->id = IPSET_INVALID_ID; + } + + return true; } static int -setlist_list_members_size(const struct ip_set *set, char dont_align) +list_set_create(struct ip_set *set, struct nlattr *head, int len, + u32 flags) { - const struct ip_set_setlist *map = set->data; - - return map->size * IPSET_VALIGN(sizeof(ip_set_id_t), dont_align); -} + struct nlattr *tb[IPSET_ATTR_CREATE_MAX]; + u32 size = IP_SET_LIST_DEFAULT_SIZE; -static void -setlist_list_members(const struct ip_set *set, void *data, char dont_align) -{ - struct ip_set_setlist *map = set->data; - ip_set_id_t *d; - int i; + if (nla_parse(tb, IPSET_ATTR_CREATE_MAX, head, len, + list_set_create_policy)) + return -IPSET_ERR_PROTOCOL; - for (i = 0; i < map->size; i++) { - d = data + i * IPSET_VALIGN(sizeof(ip_set_id_t), dont_align); - *d = ip_set_id(map->index[i]); + if (tb[IPSET_ATTR_SIZE]) + size = ip_set_get_h32(tb[IPSET_ATTR_SIZE]); + if (size < IP_SET_LIST_MIN_SIZE) + size = IP_SET_LIST_MIN_SIZE; + + if (tb[IPSET_ATTR_TIMEOUT]) { + if (!init_list_set(set, size, sizeof(struct set_telem), + ip_set_timeout_uget(tb[IPSET_ATTR_TIMEOUT]))) + return -ENOMEM; + + list_set_gc_init(set); + } else { + if (!init_list_set(set, size, sizeof(struct set_elem), + IPSET_NO_TIMEOUT)) + return -ENOMEM; } + set->variant = &list_set; + return 0; } -IP_SET_TYPE(setlist, IPSET_TYPE_SETNAME | IPSET_DATA_SINGLE) +static struct ip_set_type list_set_type = { + .name = "list:set", + .protocol = IPSET_PROTOCOL, + .features = IPSET_TYPE_NAME, + .dimension = IPSET_DIM_ONE, + .family = AF_UNSPEC, + .revision = 0, + .create = list_set_create, + .me = THIS_MODULE, +}; -MODULE_LICENSE("GPL"); -MODULE_AUTHOR("Jozsef Kadlecsik "); -MODULE_DESCRIPTION("setlist type of IP sets"); +static int __init +list_set_init(void) +{ + return ip_set_type_register(&list_set_type); +} + +static void __exit +list_set_fini(void) +{ + ip_set_type_unregister(&list_set_type); +} -REGISTER_MODULE(setlist) +module_init(list_set_init); +module_exit(list_set_fini); diff --git a/kernel/ip_set_tree_ip.c b/kernel/ip_set_tree_ip.c deleted file mode 100644 index 77eb180..0000000 --- a/kernel/ip_set_tree_ip.c +++ /dev/null @@ -1,464 +0,0 @@ -/* Copyright (C) 2005-2008 Jozsef Kadlecsik - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - */ - -/* Kernel module implementing an IP set type: the iptree type */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include - -static int limit = MAX_RANGE; - -/* Garbage collection interval in seconds: */ -#define IPTREE_GC_TIME 5*60 -/* Sleep so many milliseconds before trying again - * to delete the gc timer at destroying/flushing a set */ -#define IPTREE_DESTROY_SLEEP 100 - -static __KMEM_CACHE_T__ *branch_cachep; -static __KMEM_CACHE_T__ *leaf_cachep; - - -#if defined(__LITTLE_ENDIAN) -#define ABCD(a,b,c,d,addrp) do { \ - a = ((unsigned char *)addrp)[3]; \ - b = ((unsigned char *)addrp)[2]; \ - c = ((unsigned char *)addrp)[1]; \ - d = ((unsigned char *)addrp)[0]; \ -} while (0) -#elif defined(__BIG_ENDIAN) -#define ABCD(a,b,c,d,addrp) do { \ - a = ((unsigned char *)addrp)[0]; \ - b = ((unsigned char *)addrp)[1]; \ - c = ((unsigned char *)addrp)[2]; \ - d = ((unsigned char *)addrp)[3]; \ -} while (0) -#else -#error "Please fix asm/byteorder.h" -#endif /* __LITTLE_ENDIAN */ - -#define TESTIP_WALK(map, elem, branch) do { \ - if ((map)->tree[elem]) { \ - branch = (map)->tree[elem]; \ - } else \ - return 0; \ -} while (0) - -static inline int -iptree_test(struct ip_set *set, ip_set_ip_t ip) -{ - struct ip_set_iptree *map = set->data; - struct ip_set_iptreeb *btree; - struct ip_set_iptreec *ctree; - struct ip_set_iptreed *dtree; - unsigned char a,b,c,d; - - if (!ip) - return -ERANGE; - - ABCD(a, b, c, d, &ip); - DP("%u %u %u %u timeout %u", a, b, c, d, map->timeout); - TESTIP_WALK(map, a, btree); - TESTIP_WALK(btree, b, ctree); - TESTIP_WALK(ctree, c, dtree); - DP("%lu %lu", dtree->expires[d], jiffies); - return dtree->expires[d] - && (!map->timeout - || time_after(dtree->expires[d], jiffies)); -} - -#define KADT_CONDITION - -UADT(iptree, test) -KADT(iptree, test, ipaddr) - -#define ADDIP_WALK(map, elem, branch, type, cachep) do { \ - if ((map)->tree[elem]) { \ - DP("found %u", elem); \ - branch = (map)->tree[elem]; \ - } else { \ - branch = (type *) \ - kmem_cache_alloc(cachep, GFP_ATOMIC); \ - if (branch == NULL) \ - return -ENOMEM; \ - memset(branch, 0, sizeof(*branch)); \ - (map)->tree[elem] = branch; \ - DP("alloc %u", elem); \ - } \ -} while (0) - -static inline int -iptree_add(struct ip_set *set, ip_set_ip_t ip, unsigned int timeout) -{ - struct ip_set_iptree *map = set->data; - struct ip_set_iptreeb *btree; - struct ip_set_iptreec *ctree; - struct ip_set_iptreed *dtree; - unsigned char a,b,c,d; - int ret = 0; - - if (!ip || map->elements >= limit) - /* We could call the garbage collector - * but it's probably overkill */ - return -ERANGE; - - ABCD(a, b, c, d, &ip); - DP("%u %u %u %u timeout %u", a, b, c, d, timeout); - ADDIP_WALK(map, a, btree, struct ip_set_iptreeb, branch_cachep); - ADDIP_WALK(btree, b, ctree, struct ip_set_iptreec, branch_cachep); - ADDIP_WALK(ctree, c, dtree, struct ip_set_iptreed, leaf_cachep); - if (dtree->expires[d] - && (!map->timeout || time_after(dtree->expires[d], jiffies))) - ret = -EEXIST; - if (map->timeout && timeout == 0) - timeout = map->timeout; - dtree->expires[d] = map->timeout ? (timeout * HZ + jiffies) : 1; - /* Lottery: I won! */ - if (dtree->expires[d] == 0) - dtree->expires[d] = 1; - DP("%u %lu", d, dtree->expires[d]); - if (ret == 0) - map->elements++; - return ret; -} - -UADT(iptree, add, req->timeout) -KADT(iptree, add, ipaddr, 0) - -#define DELIP_WALK(map, elem, branch) do { \ - if ((map)->tree[elem]) { \ - branch = (map)->tree[elem]; \ - } else \ - return -EEXIST; \ -} while (0) - -static inline int -iptree_del(struct ip_set *set, ip_set_ip_t ip) -{ - struct ip_set_iptree *map = set->data; - struct ip_set_iptreeb *btree; - struct ip_set_iptreec *ctree; - struct ip_set_iptreed *dtree; - unsigned char a,b,c,d; - - if (!ip) - return -ERANGE; - - ABCD(a, b, c, d, &ip); - DELIP_WALK(map, a, btree); - DELIP_WALK(btree, b, ctree); - DELIP_WALK(ctree, c, dtree); - - if (dtree->expires[d]) { - dtree->expires[d] = 0; - map->elements--; - return 0; - } - return -EEXIST; -} - -UADT(iptree, del) -KADT(iptree, del, ipaddr) - -#define LOOP_WALK_BEGIN(map, i, branch) \ - for (i = 0; i < 256; i++) { \ - if (!(map)->tree[i]) \ - continue; \ - branch = (map)->tree[i] - -#define LOOP_WALK_END } - -static void -ip_tree_gc(unsigned long ul_set) -{ - struct ip_set *set = (struct ip_set *) ul_set; - struct ip_set_iptree *map = set->data; - struct ip_set_iptreeb *btree; - struct ip_set_iptreec *ctree; - struct ip_set_iptreed *dtree; - unsigned int a,b,c,d; - unsigned char i,j,k; - - i = j = k = 0; - DP("gc: %s", set->name); - write_lock_bh(&set->lock); - LOOP_WALK_BEGIN(map, a, btree); - LOOP_WALK_BEGIN(btree, b, ctree); - LOOP_WALK_BEGIN(ctree, c, dtree); - for (d = 0; d < 256; d++) { - if (dtree->expires[d]) { - DP("gc: %u %u %u %u: expires %lu jiffies %lu", - a, b, c, d, - dtree->expires[d], jiffies); - if (map->timeout - && time_before(dtree->expires[d], jiffies)) { - dtree->expires[d] = 0; - map->elements--; - } else - k = 1; - } - } - if (k == 0) { - DP("gc: %s: leaf %u %u %u empty", - set->name, a, b, c); - kmem_cache_free(leaf_cachep, dtree); - ctree->tree[c] = NULL; - } else { - DP("gc: %s: leaf %u %u %u not empty", - set->name, a, b, c); - j = 1; - k = 0; - } - LOOP_WALK_END; - if (j == 0) { - DP("gc: %s: branch %u %u empty", - set->name, a, b); - kmem_cache_free(branch_cachep, ctree); - btree->tree[b] = NULL; - } else { - DP("gc: %s: branch %u %u not empty", - set->name, a, b); - i = 1; - j = k = 0; - } - LOOP_WALK_END; - if (i == 0) { - DP("gc: %s: branch %u empty", - set->name, a); - kmem_cache_free(branch_cachep, btree); - map->tree[a] = NULL; - } else { - DP("gc: %s: branch %u not empty", - set->name, a); - i = j = k = 0; - } - LOOP_WALK_END; - write_unlock_bh(&set->lock); - - map->gc.expires = jiffies + map->gc_interval * HZ; - add_timer(&map->gc); -} - -static inline void -init_gc_timer(struct ip_set *set) -{ - struct ip_set_iptree *map = set->data; - - /* Even if there is no timeout for the entries, - * we still have to call gc because delete - * do not clean up empty branches */ - map->gc_interval = IPTREE_GC_TIME; - init_timer(&map->gc); - map->gc.data = (unsigned long) set; - map->gc.function = ip_tree_gc; - map->gc.expires = jiffies + map->gc_interval * HZ; - add_timer(&map->gc); -} - -static int -iptree_create(struct ip_set *set, const void *data, u_int32_t size) -{ - const struct ip_set_req_iptree_create *req = data; - struct ip_set_iptree *map; - - if (size != sizeof(struct ip_set_req_iptree_create)) { - ip_set_printk("data length wrong (want %zu, have %lu)", - sizeof(struct ip_set_req_iptree_create), - (unsigned long)size); - return -EINVAL; - } - - map = kmalloc(sizeof(struct ip_set_iptree), GFP_KERNEL); - if (!map) { - DP("out of memory for %zu bytes", - sizeof(struct ip_set_iptree)); - return -ENOMEM; - } - memset(map, 0, sizeof(*map)); - map->timeout = req->timeout; - map->elements = 0; - set->data = map; - - init_gc_timer(set); - - return 0; -} - -static inline void -__flush(struct ip_set_iptree *map) -{ - struct ip_set_iptreeb *btree; - struct ip_set_iptreec *ctree; - struct ip_set_iptreed *dtree; - unsigned int a,b,c; - - LOOP_WALK_BEGIN(map, a, btree); - LOOP_WALK_BEGIN(btree, b, ctree); - LOOP_WALK_BEGIN(ctree, c, dtree); - kmem_cache_free(leaf_cachep, dtree); - LOOP_WALK_END; - kmem_cache_free(branch_cachep, ctree); - LOOP_WALK_END; - kmem_cache_free(branch_cachep, btree); - LOOP_WALK_END; - map->elements = 0; -} - -static void -iptree_destroy(struct ip_set *set) -{ - struct ip_set_iptree *map = set->data; - - /* gc might be running */ - while (!del_timer(&map->gc)) - msleep(IPTREE_DESTROY_SLEEP); - __flush(map); - kfree(map); - set->data = NULL; -} - -static void -iptree_flush(struct ip_set *set) -{ - struct ip_set_iptree *map = set->data; - unsigned int timeout = map->timeout; - - /* gc might be running */ - while (!del_timer(&map->gc)) - msleep(IPTREE_DESTROY_SLEEP); - __flush(map); - memset(map, 0, sizeof(*map)); - map->timeout = timeout; - - init_gc_timer(set); -} - -static void -iptree_list_header(const struct ip_set *set, void *data) -{ - const struct ip_set_iptree *map = set->data; - struct ip_set_req_iptree_create *header = data; - - header->timeout = map->timeout; -} - -static int -iptree_list_members_size(const struct ip_set *set, char dont_align) -{ - const struct ip_set_iptree *map = set->data; - struct ip_set_iptreeb *btree; - struct ip_set_iptreec *ctree; - struct ip_set_iptreed *dtree; - unsigned int a,b,c,d; - unsigned int count = 0; - - LOOP_WALK_BEGIN(map, a, btree); - LOOP_WALK_BEGIN(btree, b, ctree); - LOOP_WALK_BEGIN(ctree, c, dtree); - for (d = 0; d < 256; d++) { - if (dtree->expires[d] - && (!map->timeout || time_after(dtree->expires[d], jiffies))) - count++; - } - LOOP_WALK_END; - LOOP_WALK_END; - LOOP_WALK_END; - - DP("members %u", count); - return (count * IPSET_VALIGN(sizeof(struct ip_set_req_iptree), dont_align)); -} - -static void -iptree_list_members(const struct ip_set *set, void *data, char dont_align) -{ - const struct ip_set_iptree *map = set->data; - struct ip_set_iptreeb *btree; - struct ip_set_iptreec *ctree; - struct ip_set_iptreed *dtree; - unsigned int a,b,c,d; - size_t offset = 0, datasize; - struct ip_set_req_iptree *entry; - - datasize = IPSET_VALIGN(sizeof(struct ip_set_req_iptree), dont_align); - LOOP_WALK_BEGIN(map, a, btree); - LOOP_WALK_BEGIN(btree, b, ctree); - LOOP_WALK_BEGIN(ctree, c, dtree); - for (d = 0; d < 256; d++) { - if (dtree->expires[d] - && (!map->timeout || time_after(dtree->expires[d], jiffies))) { - entry = data + offset; - entry->ip = ((a << 24) | (b << 16) | (c << 8) | d); - entry->timeout = !map->timeout ? 0 - : (dtree->expires[d] - jiffies)/HZ; - offset += datasize; - } - } - LOOP_WALK_END; - LOOP_WALK_END; - LOOP_WALK_END; -} - -IP_SET_TYPE(iptree, IPSET_TYPE_IP | IPSET_DATA_SINGLE) - -MODULE_LICENSE("GPL"); -MODULE_AUTHOR("Jozsef Kadlecsik "); -MODULE_DESCRIPTION("iptree type of IP sets"); -module_param(limit, int, 0600); -MODULE_PARM_DESC(limit, "maximal number of elements stored in the sets"); - -static int __init ip_set_iptree_init(void) -{ - int ret; - - branch_cachep = KMEM_CACHE_CREATE("ip_set_iptreeb", - sizeof(struct ip_set_iptreeb)); - if (!branch_cachep) { - printk(KERN_ERR "Unable to create ip_set_iptreeb slab cache\n"); - ret = -ENOMEM; - goto out; - } - leaf_cachep = KMEM_CACHE_CREATE("ip_set_iptreed", - sizeof(struct ip_set_iptreed)); - if (!leaf_cachep) { - printk(KERN_ERR "Unable to create ip_set_iptreed slab cache\n"); - ret = -ENOMEM; - goto free_branch; - } - ret = ip_set_register_set_type(&ip_set_iptree); - if (ret == 0) - goto out; - - kmem_cache_destroy(leaf_cachep); - free_branch: - kmem_cache_destroy(branch_cachep); - out: - return ret; -} - -static void __exit ip_set_iptree_fini(void) -{ - /* FIXME: possible race with ip_set_create() */ - ip_set_unregister_set_type(&ip_set_iptree); - kmem_cache_destroy(leaf_cachep); - kmem_cache_destroy(branch_cachep); -} - -module_init(ip_set_iptree_init); -module_exit(ip_set_iptree_fini); diff --git a/kernel/ipt_SET.c b/kernel/ipt_SET.c deleted file mode 100644 index 6009d64..0000000 --- a/kernel/ipt_SET.c +++ /dev/null @@ -1,242 +0,0 @@ -/* Copyright (C) 2000-2002 Joakim Axelsson - * Patrick Schaaf - * Martin Josefsson - * Copyright (C) 2003-2004 Jozsef Kadlecsik - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - */ - -/* ipt_SET.c - netfilter target to manipulate IP sets */ - -#include -#include -#include -#include - -#include -#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,16) -#include -#define xt_register_target ipt_register_target -#define xt_unregister_target ipt_unregister_target -#define xt_target ipt_target -#define XT_CONTINUE IPT_CONTINUE -#else -#include -#endif -#include - -static unsigned int -#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,0) -target(struct sk_buff **pskb, - unsigned int hooknum, - const struct net_device *in, - const struct net_device *out, - const void *targinfo, - void *userinfo) -#elif LINUX_VERSION_CODE < KERNEL_VERSION(2,6,17) -target(struct sk_buff **pskb, - const struct net_device *in, - const struct net_device *out, - unsigned int hooknum, - const void *targinfo, - void *userinfo) -#elif LINUX_VERSION_CODE < KERNEL_VERSION(2,6,19) -target(struct sk_buff **pskb, - const struct net_device *in, - const struct net_device *out, - unsigned int hooknum, - const struct xt_target *target, - const void *targinfo, - void *userinfo) -#elif LINUX_VERSION_CODE < KERNEL_VERSION(2,6,24) -target(struct sk_buff **pskb, - const struct net_device *in, - const struct net_device *out, - unsigned int hooknum, - const struct xt_target *target, - const void *targinfo) -#elif LINUX_VERSION_CODE < KERNEL_VERSION(2,6,28) -target(struct sk_buff *skb, - const struct net_device *in, - const struct net_device *out, - unsigned int hooknum, - const struct xt_target *target, - const void *targinfo) -#else /* LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,28) */ -target(struct sk_buff *skb, - const struct xt_target_param *par) -#endif -{ -#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,28) - const struct ipt_set_info_target *info = targinfo; -#else - const struct ipt_set_info_target *info = par->targinfo; -#endif -#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,24) - struct sk_buff *skb = *pskb; -#endif - - - if (info->add_set.index != IP_SET_INVALID_ID) - ip_set_addip_kernel(info->add_set.index, - skb, - info->add_set.flags); - if (info->del_set.index != IP_SET_INVALID_ID) - ip_set_delip_kernel(info->del_set.index, - skb, - info->del_set.flags); - - return XT_CONTINUE; -} - -#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,16) -static int -checkentry(const char *tablename, - const struct ipt_entry *e, - void *targinfo, - unsigned int targinfosize, - unsigned int hook_mask) -#elif LINUX_VERSION_CODE < KERNEL_VERSION(2,6,17) -static int -checkentry(const char *tablename, - const void *e, - void *targinfo, - unsigned int targinfosize, - unsigned int hook_mask) -#elif LINUX_VERSION_CODE < KERNEL_VERSION(2,6,19) -static int -checkentry(const char *tablename, - const void *e, - const struct xt_target *target, - void *targinfo, - unsigned int targinfosize, - unsigned int hook_mask) -#elif LINUX_VERSION_CODE < KERNEL_VERSION(2,6,23) -static int -checkentry(const char *tablename, - const void *e, - const struct xt_target *target, - void *targinfo, - unsigned int hook_mask) -#elif LINUX_VERSION_CODE < KERNEL_VERSION(2,6,28) -static bool -checkentry(const char *tablename, - const void *e, - const struct xt_target *target, - void *targinfo, - unsigned int hook_mask) -#else /* LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,28) */ -static bool -checkentry(const struct xt_tgchk_param *par) -#endif -{ -#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,28) - const struct ipt_set_info_target *info = targinfo; -#else - const struct ipt_set_info_target *info = par->targinfo; -#endif - ip_set_id_t index; - -#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,17) - if (targinfosize != IPT_ALIGN(sizeof(*info))) { - DP("bad target info size %u", targinfosize); - return 0; - } -#endif - - if (info->add_set.index != IP_SET_INVALID_ID) { - index = ip_set_get_byindex(info->add_set.index); - if (index == IP_SET_INVALID_ID) { - ip_set_printk("cannot find add_set index %u as target", - info->add_set.index); - return 0; /* error */ - } - } - - if (info->del_set.index != IP_SET_INVALID_ID) { - index = ip_set_get_byindex(info->del_set.index); - if (index == IP_SET_INVALID_ID) { - ip_set_printk("cannot find del_set index %u as target", - info->del_set.index); - return 0; /* error */ - } - } - if (info->add_set.flags[IP_SET_MAX_BINDINGS] != 0 - || info->del_set.flags[IP_SET_MAX_BINDINGS] != 0) { - ip_set_printk("That's nasty!"); - return 0; /* error */ - } - - return 1; -} - -#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,17) -static void destroy(void *targetinfo, - unsigned int targetsize) -#elif LINUX_VERSION_CODE < KERNEL_VERSION(2,6,19) -static void destroy(const struct xt_target *target, - void *targetinfo, - unsigned int targetsize) -#elif LINUX_VERSION_CODE < KERNEL_VERSION(2,6,28) -static void destroy(const struct xt_target *target, - void *targetinfo) -#else /* LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,28) */ -static void destroy(const struct xt_tgdtor_param *par) -#endif -{ -#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,28) - const struct ipt_set_info_target *info = targetinfo; -#else - const struct ipt_set_info_target *info = par->targinfo; -#endif - -#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,17) - if (targetsize != IPT_ALIGN(sizeof(struct ipt_set_info_target))) { - ip_set_printk("invalid targetsize %d", targetsize); - return; - } -#endif - if (info->add_set.index != IP_SET_INVALID_ID) - ip_set_put_byindex(info->add_set.index); - if (info->del_set.index != IP_SET_INVALID_ID) - ip_set_put_byindex(info->del_set.index); -} - -#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,17) -static struct xt_target SET_target = { - .name = "SET", - .target = target, - .checkentry = checkentry, - .destroy = destroy, - .me = THIS_MODULE -}; -#else /* LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,17) */ -static struct xt_target SET_target = { - .name = "SET", - .family = AF_INET, - .target = target, - .targetsize = sizeof(struct ipt_set_info_target), - .checkentry = checkentry, - .destroy = destroy, - .me = THIS_MODULE -}; -#endif - -MODULE_LICENSE("GPL"); -MODULE_AUTHOR("Jozsef Kadlecsik "); -MODULE_DESCRIPTION("iptables IP set target module"); - -static int __init ipt_SET_init(void) -{ - return xt_register_target(&SET_target); -} - -static void __exit ipt_SET_fini(void) -{ - xt_unregister_target(&SET_target); -} - -module_init(ipt_SET_init); -module_exit(ipt_SET_fini); diff --git a/kernel/ipt_set.c b/kernel/ipt_set.c deleted file mode 100644 index 2f97cbb..0000000 --- a/kernel/ipt_set.c +++ /dev/null @@ -1,238 +0,0 @@ -/* Copyright (C) 2000-2002 Joakim Axelsson - * Patrick Schaaf - * Martin Josefsson - * Copyright (C) 2003-2004 Jozsef Kadlecsik - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - */ - -/* Kernel module to match an IP set. */ - -#include -#include -#include -#include - -#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,16) -#include -#define xt_register_match ipt_register_match -#define xt_unregister_match ipt_unregister_match -#define xt_match ipt_match -#else -#include -#endif -#include -#include - -static inline int -match_set(const struct ipt_set_info *info, - const struct sk_buff *skb, - int inv) -{ - if (ip_set_testip_kernel(info->index, skb, info->flags)) - inv = !inv; - return inv; -} - -#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,0) -static int -match(const struct sk_buff *skb, - const struct net_device *in, - const struct net_device *out, - const void *matchinfo, - int offset, - const void *hdr, - u_int16_t datalen, - int *hotdrop) -#elif LINUX_VERSION_CODE < KERNEL_VERSION(2,6,16) -static int -match(const struct sk_buff *skb, - const struct net_device *in, - const struct net_device *out, - const void *matchinfo, - int offset, - int *hotdrop) -#elif LINUX_VERSION_CODE < KERNEL_VERSION(2,6,17) -static int -match(const struct sk_buff *skb, - const struct net_device *in, - const struct net_device *out, - const void *matchinfo, - int offset, - unsigned int protoff, - int *hotdrop) -#elif LINUX_VERSION_CODE < KERNEL_VERSION(2,6,23) -static int -match(const struct sk_buff *skb, - const struct net_device *in, - const struct net_device *out, - const struct xt_match *match, - const void *matchinfo, - int offset, - unsigned int protoff, - int *hotdrop) -#elif LINUX_VERSION_CODE < KERNEL_VERSION(2,6,28) -static bool -match(const struct sk_buff *skb, - const struct net_device *in, - const struct net_device *out, - const struct xt_match *match, - const void *matchinfo, - int offset, - unsigned int protoff, - bool *hotdrop) -#else /* LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,28) */ -static bool -match(const struct sk_buff *skb, - const struct xt_match_param *par) -#endif -{ -#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,28) - const struct ipt_set_info_match *info = matchinfo; -#else - const struct ipt_set_info_match *info = par->matchinfo; -#endif - - return match_set(&info->match_set, - skb, - info->match_set.flags[0] & IPSET_MATCH_INV); -} - -#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,16) -static int -checkentry(const char *tablename, - const struct ipt_ip *ip, - void *matchinfo, - unsigned int matchsize, - unsigned int hook_mask) -#elif LINUX_VERSION_CODE < KERNEL_VERSION(2,6,17) -static int -checkentry(const char *tablename, - const void *inf, - void *matchinfo, - unsigned int matchsize, - unsigned int hook_mask) -#elif LINUX_VERSION_CODE < KERNEL_VERSION(2,6,19) -static int -checkentry(const char *tablename, - const void *inf, - const struct xt_match *match, - void *matchinfo, - unsigned int matchsize, - unsigned int hook_mask) -#elif LINUX_VERSION_CODE < KERNEL_VERSION(2,6,23) -static int -checkentry(const char *tablename, - const void *inf, - const struct xt_match *match, - void *matchinfo, - unsigned int hook_mask) -#elif LINUX_VERSION_CODE < KERNEL_VERSION(2,6,28) -static bool -checkentry(const char *tablename, - const void *inf, - const struct xt_match *match, - void *matchinfo, - unsigned int hook_mask) -#else /* LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,28) */ -static bool -checkentry(const struct xt_mtchk_param *par) -#endif -{ -#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,28) - struct ipt_set_info_match *info = matchinfo; -#else - struct ipt_set_info_match *info = par->matchinfo; -#endif - ip_set_id_t index; - -#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,17) - if (matchsize != IPT_ALIGN(sizeof(struct ipt_set_info_match))) { - ip_set_printk("invalid matchsize %d", matchsize); - return 0; - } -#endif - - index = ip_set_get_byindex(info->match_set.index); - - if (index == IP_SET_INVALID_ID) { - ip_set_printk("Cannot find set indentified by id %u to match", - info->match_set.index); - return 0; /* error */ - } - if (info->match_set.flags[IP_SET_MAX_BINDINGS] != 0) { - ip_set_printk("That's nasty!"); - return 0; /* error */ - } - - return 1; -} - -#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,17) -static void destroy(void *matchinfo, - unsigned int matchsize) -#elif LINUX_VERSION_CODE < KERNEL_VERSION(2,6,19) -static void destroy(const struct xt_match *match, - void *matchinfo, - unsigned int matchsize) -#elif LINUX_VERSION_CODE < KERNEL_VERSION(2,6,28) -static void destroy(const struct xt_match *match, - void *matchinfo) -#else /* LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,28) */ -static void destroy(const struct xt_mtdtor_param *par) -#endif -{ -#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,28) - struct ipt_set_info_match *info = matchinfo; -#else - struct ipt_set_info_match *info = par->matchinfo; -#endif - - -#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,17) - if (matchsize != IPT_ALIGN(sizeof(struct ipt_set_info_match))) { - ip_set_printk("invalid matchsize %d", matchsize); - return; - } -#endif - ip_set_put_byindex(info->match_set.index); -} - -#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,17) -static struct xt_match set_match = { - .name = "set", - .match = &match, - .checkentry = &checkentry, - .destroy = &destroy, - .me = THIS_MODULE -}; -#else /* LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,17) */ -static struct xt_match set_match = { - .name = "set", - .family = AF_INET, - .match = &match, - .matchsize = sizeof(struct ipt_set_info_match), - .checkentry = &checkentry, - .destroy = &destroy, - .me = THIS_MODULE -}; -#endif - -MODULE_LICENSE("GPL"); -MODULE_AUTHOR("Jozsef Kadlecsik "); -MODULE_DESCRIPTION("iptables IP set match module"); - -static int __init ipt_ipset_init(void) -{ - return xt_register_match(&set_match); -} - -static void __exit ipt_ipset_fini(void) -{ - xt_unregister_match(&set_match); -} - -module_init(ipt_ipset_init); -module_exit(ipt_ipset_fini); diff --git a/kernel/xt_set.c b/kernel/xt_set.c new file mode 100644 index 0000000..7dfd5ee --- /dev/null +++ b/kernel/xt_set.c @@ -0,0 +1,356 @@ +/* Copyright (C) 2000-2002 Joakim Axelsson + * Patrick Schaaf + * Martin Josefsson + * Copyright (C) 2003-2010 Jozsef Kadlecsik + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +/* Kernel module which implements the set match and SET target + * for netfilter/iptables. */ + +#include +#include + +#include +#include + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Jozsef Kadlecsik "); +MODULE_DESCRIPTION("Xtables: IP set match and target module"); +MODULE_ALIAS("xt_SET"); +MODULE_ALIAS("ipt_set"); +MODULE_ALIAS("ipt_SET"); + +static inline int +match_set(ip_set_id_t index, const struct sk_buff *skb, + u8 pf, u8 dim, u8 flags, int inv) +{ + if (ip_set_test(index, skb, pf, dim, flags)) + inv = !inv; + return inv; +} + +/* Revision 0 interface: backward compatible with netfilter/iptables */ + +static bool +set_match_v0(const struct sk_buff *skb, const struct xt_match_param *par) +{ + const struct xt_set_info_match_v0 *info = par->matchinfo; + + return match_set(info->match_set.index, skb, par->family, + info->match_set.u.compat.dim, + info->match_set.u.compat.flags, + info->match_set.u.compat.flags & IPSET_INV_MATCH); +} + +static void +compat_flags(struct xt_set_info_v0 *info) +{ + u_int8_t i; + + /* Fill out compatibility data according to enum ip_set_kopt */ + info->u.compat.dim = IPSET_DIM_ZERO; + if (info->u.flags[0] & IPSET_MATCH_INV) + info->u.compat.flags |= IPSET_INV_MATCH; + for (i = 0; i < IPSET_DIM_MAX-1 && info->u.flags[i]; i++) { + info->u.compat.dim++; + if (info->u.flags[i] & IPSET_SRC) + info->u.compat.flags |= (1<u.compat.dim); + } +} + +static bool +set_match_v0_checkentry(const struct xt_mtchk_param *par) +{ + struct xt_set_info_match_v0 *info = par->matchinfo; + ip_set_id_t index; + + index = ip_set_nfnl_get_byindex(info->match_set.index); + + if (index == IPSET_INVALID_ID) { + pr_warning("Cannot find set indentified by id %u to match", + info->match_set.index); + return 0; /* error */ + } + if (info->match_set.u.flags[IPSET_DIM_MAX-1] != 0) { + pr_warning("That's nasty!"); + return 0; /* error */ + } + + /* Fill out compatibility data */ + compat_flags(&info->match_set); + + return 1; +} + +static void +set_match_v0_destroy(const struct xt_mtdtor_param *par) +{ + struct xt_set_info_match *info = par->matchinfo; + + + ip_set_nfnl_put(info->match_set.index); +} + +static unsigned int +set_target_v0(struct sk_buff *skb, const struct xt_target_param *par) +{ + const struct xt_set_info_target_v0 *info = par->targinfo; + + if (info->add_set.index != IPSET_INVALID_ID) + ip_set_add(info->add_set.index, skb, par->family, + info->add_set.u.compat.dim, + info->add_set.u.compat.flags); + if (info->del_set.index != IPSET_INVALID_ID) + ip_set_del(info->del_set.index, skb, par->family, + info->del_set.u.compat.dim, + info->del_set.u.compat.flags); + + return XT_CONTINUE; +} + +static bool +set_target_v0_checkentry(const struct xt_tgchk_param *par) +{ + struct xt_set_info_target_v0 *info = par->targinfo; + ip_set_id_t index; + + if (info->add_set.index != IPSET_INVALID_ID) { + index = ip_set_nfnl_get_byindex(info->add_set.index); + if (index == IPSET_INVALID_ID) { + pr_warning("cannot find add_set index %u as target", + info->add_set.index); + return 0; /* error */ + } + } + + if (info->del_set.index != IPSET_INVALID_ID) { + index = ip_set_nfnl_get_byindex(info->del_set.index); + if (index == IPSET_INVALID_ID) { + pr_warning("cannot find del_set index %u as target", + info->del_set.index); + return 0; /* error */ + } + } + if (info->add_set.u.flags[IPSET_DIM_MAX-1] != 0 + || info->del_set.u.flags[IPSET_DIM_MAX-1] != 0) { + pr_warning("That's nasty!"); + return 0; /* error */ + } + + /* Fill out compatibility data */ + compat_flags(&info->add_set); + compat_flags(&info->del_set); + + return 1; +} + +static void +set_target_v0_destroy(const struct xt_tgdtor_param *par) +{ + const struct xt_set_info_target_v0 *info = par->targinfo; + + if (info->add_set.index != IPSET_INVALID_ID) + ip_set_nfnl_put(info->add_set.index); + if (info->del_set.index != IPSET_INVALID_ID) + ip_set_nfnl_put(info->del_set.index); +} + +/* Revision 1: current interface to netfilter/iptables */ + +static bool +set_match(const struct sk_buff *skb, const struct xt_match_param *par) +{ + const struct xt_set_info_match *info = par->matchinfo; + + return match_set(info->match_set.index, skb, par->family, + info->match_set.dim, + info->match_set.flags, + info->match_set.flags & IPSET_INV_MATCH); +} + +static bool +set_match_checkentry(const struct xt_mtchk_param *par) +{ + struct xt_set_info_match *info = par->matchinfo; + ip_set_id_t index; + + index = ip_set_nfnl_get_byindex(info->match_set.index); + + if (index == IPSET_INVALID_ID) { + pr_warning("Cannot find set indentified by id %u to match", + info->match_set.index); + return 0; /* error */ + } + if (info->match_set.dim > IPSET_DIM_MAX) { + pr_warning("That's nasty!"); + return 0; /* error */ + } + + return 1; +} + +static void +set_match_destroy(const struct xt_mtdtor_param *par) +{ + struct xt_set_info_match *info = par->matchinfo; + + + ip_set_nfnl_put(info->match_set.index); +} + +/* Set target */ + +static unsigned int +set_target(struct sk_buff *skb, const struct xt_target_param *par) +{ + const struct xt_set_info_target *info = par->targinfo; + + if (info->add_set.index != IPSET_INVALID_ID) + ip_set_add(info->add_set.index, + skb, par->family, + info->add_set.dim, + info->add_set.flags); + if (info->del_set.index != IPSET_INVALID_ID) + ip_set_del(info->del_set.index, + skb, par->family, + info->add_set.dim, + info->del_set.flags); + + return XT_CONTINUE; +} + +static bool +set_target_checkentry(const struct xt_tgchk_param *par) +{ + const struct xt_set_info_target *info = par->targinfo; + ip_set_id_t index; + + if (info->add_set.index != IPSET_INVALID_ID) { + index = ip_set_nfnl_get_byindex(info->add_set.index); + if (index == IPSET_INVALID_ID) { + pr_warning("cannot find add_set index %u as target", + info->add_set.index); + return 0; /* error */ + } + } + + if (info->del_set.index != IPSET_INVALID_ID) { + index = ip_set_nfnl_get_byindex(info->del_set.index); + if (index == IPSET_INVALID_ID) { + pr_warning("cannot find del_set index %u as target", + info->del_set.index); + return 0; /* error */ + } + } + if (info->add_set.dim > IPSET_DIM_MAX + || info->del_set.flags > IPSET_DIM_MAX) { + pr_warning("That's nasty!"); + return 0; /* error */ + } + + return 1; +} + +static void +set_target_destroy(const struct xt_tgdtor_param *par) +{ + const struct xt_set_info_target *info = par->targinfo; + + if (info->add_set.index != IPSET_INVALID_ID) + ip_set_nfnl_put(info->add_set.index); + if (info->del_set.index != IPSET_INVALID_ID) + ip_set_nfnl_put(info->del_set.index); +} + +static struct xt_match set_matches[] __read_mostly = { + { + .name = "set", + .family = NFPROTO_IPV4, + .revision = 0, + .match = set_match_v0, + .matchsize = sizeof(struct xt_set_info_match_v0), + .checkentry = set_match_v0_checkentry, + .destroy = set_match_v0_destroy, + .me = THIS_MODULE + }, + { + .name = "set", + .family = NFPROTO_IPV4, + .revision = 1, + .match = set_match, + .matchsize = sizeof(struct xt_set_info_match), + .checkentry = set_match_checkentry, + .destroy = set_match_destroy, + .me = THIS_MODULE + }, + { + .name = "set", + .family = NFPROTO_IPV6, + .revision = 1, + .match = set_match, + .matchsize = sizeof(struct xt_set_info_match), + .checkentry = set_match_checkentry, + .destroy = set_match_destroy, + .me = THIS_MODULE + }, +}; + +static struct xt_target set_targets[] __read_mostly = { + { + .name = "SET", + .revision = 0, + .family = NFPROTO_IPV4, + .target = set_target_v0, + .targetsize = sizeof(struct xt_set_info_target_v0), + .checkentry = set_target_v0_checkentry, + .destroy = set_target_v0_destroy, + .me = THIS_MODULE + }, + { + .name = "SET", + .revision = 1, + .family = NFPROTO_IPV4, + .target = set_target, + .targetsize = sizeof(struct xt_set_info_target), + .checkentry = set_target_checkentry, + .destroy = set_target_destroy, + .me = THIS_MODULE + }, + { + .name = "SET", + .revision = 1, + .family = NFPROTO_IPV6, + .target = set_target, + .targetsize = sizeof(struct xt_set_info_target), + .checkentry = set_target_checkentry, + .destroy = set_target_destroy, + .me = THIS_MODULE + }, +}; + +static int __init xt_set_init(void) +{ + int ret = xt_register_matches(set_matches, ARRAY_SIZE(set_matches)); + + if (!ret) { + ret = xt_register_targets(set_targets, + ARRAY_SIZE(set_targets)); + if (ret) + xt_unregister_matches(set_matches, + ARRAY_SIZE(set_matches)); + } + return ret; +} + +static void __exit xt_set_fini(void) +{ + xt_unregister_matches(set_matches, ARRAY_SIZE(set_matches)); + xt_unregister_targets(set_targets, ARRAY_SIZE(set_targets)); +} + +module_init(xt_set_init); +module_exit(xt_set_fini); diff --git a/lib/Makefile.am b/lib/Makefile.am index 74b6651..bf4e133 100644 --- a/lib/Makefile.am +++ b/lib/Makefile.am @@ -12,9 +12,7 @@ libipset_la_SOURCES = \ parse.c \ print.c \ session.c \ - types.c \ - utils.c - + types.c #%.o: %.c # ${AM_VERBOSE_CC} ${CC} ${AM_DEPFLAGS} ${AM_CFLAGS} ${CFLAGS} -o $@ -c $< diff --git a/lib/PROTOCOL b/lib/PROTOCOL index e1a139e..6f07445 100644 --- a/lib/PROTOCOL +++ b/lib/PROTOCOL @@ -33,7 +33,11 @@ req: msg: IPSET_CMD_LIST|SAVE attr: IPSET_ATTR_PROTOCOL IPSET_ATTR_SETNAME (optional) -resp: attr: IPSET_ATTR_DATA +resp: attr: IPSET_ATTR_SETNAME + IPSET_ATTR_TYPENAME + IPSET_ATTR_REVISION + IPSET_ATTR_FAMILY + IPSET_ATTR_DATA create-specific-data IPSET_ATTR_ADT IPSET_ATTR_DATA diff --git a/lib/data.c b/lib/data.c index 0de91a1..f8ff4a9 100644 --- a/lib/data.c +++ b/lib/data.c @@ -12,6 +12,7 @@ #include /* memset */ #include /* IPSET_MAXNAMELEN */ +#include /* D() */ #include /* struct ipset_type */ #include /* inXcpy */ #include /* prototypes */ @@ -26,13 +27,16 @@ struct ipset_data { /* Option bits: which fields are set */ uint64_t bits; + /* Option bits: which options are ignored */ + uint64_t ignored; /* Setname */ char setname[IPSET_MAXNAMELEN]; const struct ipset_type *type; /* Common CADT options */ uint8_t cidr; uint8_t family; - uint32_t flags; + uint32_t flags; /* command level flags */ + uint32_t cadt_flags; /* data level flags */ uint32_t timeout; union nf_inet_addr ip; union nf_inet_addr ip_to; @@ -78,6 +82,25 @@ copy_addr(uint8_t family, union nf_inet_addr *ip, const void *value) in6cpy(&ip->in6, (const struct in6_addr *)value); } +/** + * ipset_strncpy - copy the string from src to dst + * @dst: the target string buffer + * @src: the source string buffer + * @len: the length of bytes to copy, including the terminating null byte. + * + * Copy the string from src to destination, but at most len bytes are + * copied. The target is unconditionally terminated by the null byte. + */ +void +ipset_strncpy(char *dst, const char *src, size_t len) +{ + assert(dst); + assert(src); + + strncpy(dst, src, len); + dst[len - 1] = '\0'; +} + /** * ipset_data_flags_test - test option bits in the data blob * @data: data blob @@ -122,12 +145,37 @@ ipset_data_flags_unset(struct ipset_data *data, uint64_t flags) data->bits &= ~flags; } -#define flag_type_attr(data, opt, flag) \ -do { \ - data->flags |= (1 << flag); \ - opt = IPSET_OPT_FLAGS; \ +#define flag_type_attr(data, opt, flag) \ +do { \ + data->flags |= flag; \ + opt = IPSET_OPT_FLAGS; \ +} while (0) + +#define cadt_flag_type_attr(data, opt, flag) \ +do { \ + data->cadt_flags |= flag; \ + opt = IPSET_OPT_CADT_FLAGS; \ } while (0) +/** + * ipset_data_ignored - test and set ignored bits in the data blob + * @data: data blob + * @flags: the option flags which is ignored + * + * Returns true if the option was not already ignored. + */ +bool +ipset_data_ignored(struct ipset_data *data, enum ipset_opt opt) +{ + bool ignored; + assert(data); + + ignored = data->ignored & IPSET_FLAG(opt); + data->ignored |= IPSET_FLAG(opt); + + return ignored; +} + /** * ipset_data_set - put data into the data blob * @data: data blob @@ -249,11 +297,14 @@ ipset_data_set(struct ipset_data *data, enum ipset_opt opt, const void *value) flag_type_attr(data, opt, IPSET_FLAG_EXIST); break; case IPSET_OPT_BEFORE: - flag_type_attr(data, opt, IPSET_FLAG_BEFORE); + cadt_flag_type_attr(data, opt, IPSET_FLAG_BEFORE); break; case IPSET_OPT_FLAGS: data->flags = *(const uint32_t *)value; break; + case IPSET_OPT_CADT_FLAGS: + data->cadt_flags = *(const uint32_t *)value; + break; default: return -1; }; @@ -351,8 +402,10 @@ ipset_data_get(const struct ipset_data *data, enum ipset_opt opt) /* flags */ case IPSET_OPT_FLAGS: case IPSET_OPT_EXIST: - case IPSET_OPT_BEFORE: return &data->flags; + case IPSET_OPT_CADT_FLAGS: + case IPSET_OPT_BEFORE: + return &data->cadt_flags; default: return NULL; } diff --git a/lib/mnl.c b/lib/mnl.c index 5662a47..8056427 100644 --- a/lib/mnl.c +++ b/lib/mnl.c @@ -8,8 +8,10 @@ #include /* errno */ #include /* calloc, free */ #include /* time */ +#include /* hto* */ #include /* enum ipset_cmd */ +#include /* D() */ #include /* ipset_session_handle */ #include /* IPSET_ENV_EXIST */ #include /* UNUSED */ diff --git a/lib/parse.c b/lib/parse.c index 0e0e7f1..e347c69 100644 --- a/lib/parse.c +++ b/lib/parse.c @@ -13,6 +13,7 @@ #include /* getaddrinfo, AF_ */ #include /* ETH_ALEN */ +#include /* D() */ #include /* IPSET_OPT_* */ #include /* prefixlen_netmask_map */ #include /* ipset_err */ @@ -22,14 +23,31 @@ /* Parse input data */ -#define ipset_cidr_separator(str) ipset_strchr(str, IPSET_CIDR_SEPARATOR) -#define ipset_range_separator(str) ipset_strchr(str, IPSET_RANGE_SEPARATOR) -#define ipset_elem_separator(str) ipset_strchr(str, IPSET_ELEM_SEPARATOR) -#define ipset_name_separator(str) ipset_strchr(str, IPSET_NAME_SEPARATOR) +#define cidr_separator(str) ipset_strchr(str, IPSET_CIDR_SEPARATOR) +#define range_separator(str) ipset_strchr(str, IPSET_RANGE_SEPARATOR) +#define elem_separator(str) ipset_strchr(str, IPSET_ELEM_SEPARATOR) +#define name_separator(str) ipset_strchr(str, IPSET_NAME_SEPARATOR) #define syntax_err(fmt, args...) \ ipset_err(session, "Syntax error: " fmt , ## args) +static char * +ipset_strchr(const char *str, const char *sep) +{ + char *match; + + assert(str); + assert(sep); + + for (; *sep != '\0'; sep++) + if ((match = strchr(str, (int)sep[0])) != NULL + && str[0] != sep[0] + && str[strlen(str)-1] != sep[0]) + return match; + + return NULL; +} + /* * Parser functions, shamelessly taken from iptables.c, ip6tables.c * and parser.c from libnetfilter_conntrack. @@ -70,33 +88,53 @@ string_to_number_ll(struct ipset_session *session, } static int -string_to_number_l(struct ipset_session *session, - const char *str, - unsigned long min, - unsigned long max, - unsigned long *ret) +string_to_u8(struct ipset_session *session, + const char *str, uint8_t *ret) { int err; - unsigned long long number = 0; + unsigned long long num = 0; - err = string_to_number_ll(session, str, min, max, &number); - *ret = (unsigned long) number; + err = string_to_number_ll(session, str, 0, 255, &num); + *ret = (uint8_t) num; return err; } static int -string_to_number(struct ipset_session *session, - const char *str, - unsigned int min, - unsigned int max, - unsigned int *ret) +string_to_cidr(struct ipset_session *session, + const char *str, uint8_t min, uint8_t max, uint8_t *ret) +{ + int err = string_to_u8(session, str, ret); + + if (!err && (*ret < min || *ret > max)) + return syntax_err("'%s' is out of range %u-%u", + str, min, max); + + return err; +} + +static int +string_to_u16(struct ipset_session *session, + const char *str, uint16_t *ret) +{ + int err; + unsigned long long num = 0; + + err = string_to_number_ll(session, str, 0, USHRT_MAX, &num); + *ret = (uint16_t) num; + + return err; +} + +static int +string_to_u32(struct ipset_session *session, + const char *str, uint32_t *ret) { int err; - unsigned long number = 0; + unsigned long long num = 0; - err = string_to_number_l(session, str, min, max, &number); - *ret = (unsigned int) number; + err = string_to_number_ll(session, str, 0, UINT_MAX, &num); + *ret = (uint32_t) num; return err; } @@ -161,12 +199,6 @@ parse_portname(struct ipset_session *session, const char *str, uint16_t *port) return syntax_err("cannot parse '%s' as a (TCP) port", str); } -static int -parse_portnum(struct ipset_session *session, const char *str, uint16_t *port) -{ - return string_to_number(session, str, 0, 65535, (unsigned int *)port); -} - /** * ipset_parse_single_port - parse a single (TCP) port number or name * @session: session structure @@ -189,7 +221,7 @@ ipset_parse_single_port(struct ipset_session *session, assert(opt == IPSET_OPT_PORT || opt == IPSET_OPT_PORT_TO); assert(str); - if ((err = parse_portnum(session, str, &port)) == 0 + if ((err = string_to_u16(session, str, &port)) == 0 || (err = parse_portname(session, str, &port)) == 0) err = ipset_session_data_set(session, opt, &port); @@ -229,7 +261,7 @@ ipset_parse_port(struct ipset_session *session, "Cannot allocate memory to duplicate %s.", str); - a = ipset_range_separator(tmp); + a = range_separator(tmp); if (a != NULL) { /* port-port */ *a++ = '\0'; @@ -256,14 +288,20 @@ error: * Returns 0 on success or a negative error code. */ int -ipset_parse_family(struct ipset_session *session, int opt, const char *str) +ipset_parse_family(struct ipset_session *session, + enum ipset_opt opt, const char *str) { + struct ipset_data *data; uint8_t family; assert(session); assert(opt == IPSET_OPT_FAMILY); assert(str); + data = ipset_session_data(session); + if (ipset_data_flags_test(data, IPSET_FLAG(IPSET_OPT_FAMILY))) + syntax_err("protocol family may not be specified multiple times"); + if (STREQ(str, "inet") || STREQ(str, "ipv4") || STREQ(str, "-4")) family = AF_INET; else if (STREQ(str, "inet6") || STREQ(str, "ipv6") || STREQ(str, "-6")) @@ -273,7 +311,7 @@ ipset_parse_family(struct ipset_session *session, int opt, const char *str) else return syntax_err("unknown INET family %s", str); - return ipset_session_data_set(session, opt, &family); + return ipset_data_set(data, opt, &family); } /* @@ -316,8 +354,8 @@ get_addrinfo##f(struct ipset_session *session, \ int found; \ \ if ((*info = get_addrinfo(session, str, family)) == NULL) { \ - syntax_err("cannot parse %s: resolving " \ - IP " failed", str); \ + syntax_err("cannot parse %s: " IP " resolving failed", \ + str); \ return EINVAL; \ } \ \ @@ -347,7 +385,7 @@ static int \ parse_ipv##f(struct ipset_session *session, \ enum ipset_opt opt, const char *str) \ { \ - unsigned int m = mask; \ + uint8_t m = mask; \ int aerr = EINVAL, err = 0, range = 0; \ char *saved = strdup(str); \ char *a, *tmp = saved; \ @@ -361,14 +399,14 @@ parse_ipv##f(struct ipset_session *session, \ return ipset_err(session, \ "Cannot allocate memory to duplicate %s.",\ str); \ - if ((a = ipset_cidr_separator(tmp)) != NULL) { \ + if ((a = cidr_separator(tmp)) != NULL) { \ /* IP/mask */ \ *a++ = '\0'; \ \ - if ((err = string_to_number(session, a, 0, m, &m)) != 0 \ + if ((err = string_to_cidr(session, a, 0, m, &m)) != 0 \ || (err = ipset_data_set(data, copt, &m)) != 0) \ goto out; \ - } else if ((a = ipset_range_separator(tmp)) != NULL) { \ + } else if ((a = range_separator(tmp)) != NULL) { \ /* IP-IP */ \ *a++ = '\0'; \ D("range %s", a); \ @@ -420,17 +458,17 @@ parse_ip(struct ipset_session *session, switch (addrtype) { case IPADDR_PLAIN: - if (ipset_range_separator(str) || ipset_cidr_separator(str)) + if (range_separator(str) || cidr_separator(str)) return syntax_err("plain IP address must be supplied: %s", str); break; case IPADDR_NET: - if (!ipset_cidr_separator(str) || ipset_range_separator(str)) + if (!cidr_separator(str) || range_separator(str)) return syntax_err("IP/netblock must be supplied: %s", str); break; case IPADDR_RANGE: - if (!ipset_range_separator(str) || ipset_cidr_separator(str)) + if (!range_separator(str) || cidr_separator(str)) return syntax_err("IP-IP range must supplied: %s", str); break; @@ -539,7 +577,7 @@ ipset_parse_range(struct ipset_session *session, enum ipset_opt opt, const char *str) { assert(session); - assert(opt == IPSET_OPT_IP); + assert(opt == IPSET_OPT_IP || opt == IPSET_OPT_IP2); assert(str); return parse_ip(session, IPSET_OPT_IP, str, IPADDR_RANGE); @@ -563,15 +601,118 @@ ipset_parse_netrange(struct ipset_session *session, enum ipset_opt opt, const char *str) { assert(session); - assert(opt == IPSET_OPT_IP); + assert(opt == IPSET_OPT_IP || opt == IPSET_OPT_IP2); assert(str); - if (!(ipset_range_separator(str) || ipset_cidr_separator(str))) - return syntax_err("IP/net or IP-IP range must be specified: %s", + if (!(range_separator(str) || cidr_separator(str))) + return syntax_err("IP/cidr or IP-IP range must be specified: %s", str); return parse_ip(session, opt, str, IPADDR_ANY); } +/** + * ipset_parse_iprange - parse IPv4|IPv6 address or range + * @session: session structure + * @opt: option kind of the data + * @str: string to parse + * + * Parse string as an IPv4|IPv6 address pattern or a range + * of addresses separated by a dash. If family is not set yet in + * the data blob, INET is assumed. + * The value is stored in the data blob of the session. + * + * Returns 0 on success or a negative error code. + */ +int +ipset_parse_iprange(struct ipset_session *session, + enum ipset_opt opt, const char *str) +{ + assert(session); + assert(opt == IPSET_OPT_IP || opt == IPSET_OPT_IP2); + assert(str); + + if (cidr_separator(str)) + return syntax_err("IP address or IP-IP range must be specified: %s", + str); + return parse_ip(session, opt, str, IPADDR_ANY); +} + +/** + * ipset_parse_ipnet - parse IPv4|IPv6 address or address/cidr pattern + * @session: session structure + * @opt: option kind of the data + * @str: string to parse + * + * Parse string as an IPv4|IPv6 address or address/cidr pattern. + * If family is not set yet in the data blob, INET is assumed. + * The value is stored in the data blob of the session. + * + * Returns 0 on success or a negative error code. + */ +int +ipset_parse_ipnet(struct ipset_session *session, + enum ipset_opt opt, const char *str) +{ + assert(session); + assert(opt == IPSET_OPT_IP || opt == IPSET_OPT_IP2); + assert(str); + + if (range_separator(str)) + return syntax_err("IP address or IP/cidr must be specified: %s", + str); + return parse_ip(session, opt, str, IPADDR_ANY); +} + +/** + * ipset_parse_iptimeout - parse IPv4|IPv6 address and timeout + * @session: session structure + * @opt: option kind of the data + * @str: string to parse + * + * Parse string as an IPv4|IPv6 address and timeout parameter. + * If family is not set yet in the data blob, INET is assumed. + * The value is stored in the data blob of the session. + * + * Compatibility parser. + * + * Returns 0 on success or a negative error code. + */ +int +ipset_parse_iptimeout(struct ipset_session *session, + enum ipset_opt opt, const char *str) +{ + char *tmp, *saved, *a; + int err; + + assert(session); + assert(opt == IPSET_OPT_IP); + assert(str); + + /* IP,timeout */ + if (ipset_data_flags_test(ipset_session_data(session), + IPSET_FLAG(IPSET_OPT_TIMEOUT))) + return syntax_err("mixed syntax, timeout already specified"); + + tmp = saved = strdup(str); + if (saved == NULL) + return ipset_err(session, + "Cannot allocate memory to duplicate %s.", + str); + + a = elem_separator(tmp); + if (a == NULL) { + free(saved); + return syntax_err("Missing separator from %s", str); + } + *a++ = '\0'; + err = parse_ip(session, opt, tmp, IPADDR_ANY); + if (!err) + err = ipset_parse_uint32(session, IPSET_OPT_TIMEOUT, a); + + free(saved); + return err; +} + #define check_setname(str, saved) \ do { \ if (strlen(str) > IPSET_MAXNAMELEN - 1) { \ @@ -584,7 +725,7 @@ do { \ /** - * ipset_parse_name - parse setname as element + * ipset_parse_name_compat - parse setname as element * @session: session structure * @opt: option kind of the data * @str: string to parse @@ -597,8 +738,8 @@ do { \ * Returns 0 on success or a negative error code. */ int -ipset_parse_name(struct ipset_session *session, - enum ipset_opt opt, const char *str) +ipset_parse_name_compat(struct ipset_session *session, + enum ipset_opt opt, const char *str) { char *saved; char *a = NULL, *b = NULL, *tmp; @@ -607,25 +748,22 @@ ipset_parse_name(struct ipset_session *session, struct ipset_data *data; assert(session); - assert(opt == IPSET_OPT_NAME || opt == IPSET_OPT_SETNAME2); + assert(opt == IPSET_OPT_NAME); assert(str); data = ipset_session_data(session); - if (opt == IPSET_OPT_SETNAME2) { - check_setname(str, NULL); - - return ipset_data_set(data, opt, str); - } + if (ipset_data_flags_test(data, IPSET_FLAG(IPSET_OPT_NAMEREF))) + syntax_err("mixed syntax, before|after option already used"); tmp = saved = strdup(str); if (saved == NULL) return ipset_err(session, "Cannot allocate memory to duplicate %s.", str); - if ((a = ipset_elem_separator(tmp)) != NULL) { + if ((a = elem_separator(tmp)) != NULL) { /* setname,[before|after,setname */ *a++ = '\0'; - if ((b = ipset_elem_separator(a)) != NULL) + if ((b = elem_separator(a)) != NULL) *b++ = '\0'; if (b == NULL || !(STREQ(a, "before") || STREQ(a, "after"))) { @@ -644,7 +782,7 @@ ipset_parse_name(struct ipset_session *session, if ((err = ipset_data_set(data, IPSET_OPT_NAMEREF, b)) != 0) goto out; - + if (before) err = ipset_data_set(data, IPSET_OPT_BEFORE, &before); @@ -654,12 +792,12 @@ out: } /** - * ipset_parse_setname - parse name as the name of the (current) set + * ipset_parse_setname - parse string as a setname * @session: session structure * @opt: option kind of the data * @str: string to parse * - * Parse string as the name of the (current) set. + * Parse string as a setname. * The value is stored in the data blob of the session. * * Returns 0 on success or a negative error code. @@ -669,7 +807,9 @@ ipset_parse_setname(struct ipset_session *session, enum ipset_opt opt, const char *str) { assert(session); - assert(opt == IPSET_SETNAME); + assert(opt == IPSET_SETNAME + || opt == IPSET_OPT_NAME + || opt == IPSET_OPT_SETNAME2); assert(str); check_setname(str, NULL); @@ -677,6 +817,67 @@ ipset_parse_setname(struct ipset_session *session, return ipset_session_data_set(session, opt, str); } +/** + * ipset_parse_before - parse string as "before" reference setname + * @session: session structure + * @opt: option kind of the data + * @str: string to parse + * + * Parse string as a "before" reference setname for list:set + * type of sets. The value is stored in the data blob of the session. + * + * Returns 0 on success or a negative error code. + */ +int +ipset_parse_before(struct ipset_session *session, + enum ipset_opt opt, const char *str) +{ + struct ipset_data *data; + + assert(session); + assert(opt == IPSET_OPT_NAMEREF); + assert(str); + + data = ipset_session_data(session); + if (ipset_data_flags_test(data, IPSET_FLAG(IPSET_OPT_NAMEREF))) + syntax_err("mixed syntax, before|after option already used"); + + check_setname(str, NULL); + ipset_data_set(data, IPSET_OPT_BEFORE, str); + + return ipset_data_set(data, opt, str); +} + +/** + * ipset_parse_after - parse string as "after" reference setname + * @session: session structure + * @opt: option kind of the data + * @str: string to parse + * + * Parse string as a "after" reference setname for list:set + * type of sets. The value is stored in the data blob of the session. + * + * Returns 0 on success or a negative error code. + */ +int +ipset_parse_after(struct ipset_session *session, + enum ipset_opt opt, const char *str) +{ + struct ipset_data *data; + + assert(session); + assert(opt == IPSET_OPT_NAMEREF); + assert(str); + + data = ipset_session_data(session); + if (ipset_data_flags_test(data, IPSET_FLAG(IPSET_OPT_NAMEREF))) + syntax_err("mixed syntax, before|after option already used"); + + check_setname(str, NULL); + + return ipset_data_set(data, opt, str); +} + /** * ipset_parse_uint32 - parse string as an unsigned integer * @session: session structure @@ -698,7 +899,7 @@ ipset_parse_uint32(struct ipset_session *session, assert(session); assert(str); - if ((err = string_to_number(session, str, 0, 0, &value)) == 0) + if ((err = string_to_u32(session, str, &value)) == 0) return ipset_session_data_set(session, opt, &value); return err; @@ -719,13 +920,13 @@ int ipset_parse_uint8(struct ipset_session *session, enum ipset_opt opt, const char *str) { - unsigned int value; + uint8_t value; int err; assert(session); assert(str); - if ((err = string_to_number(session, str, 0, 255, &value)) == 0) + if ((err = string_to_u8(session, str, &value)) == 0) return ipset_session_data_set(session, opt, &value); return err; @@ -747,7 +948,7 @@ int ipset_parse_netmask(struct ipset_session *session, enum ipset_opt opt, const char *str) { - unsigned int family, cidr; + uint8_t family, cidr; struct ipset_data *data; int err = 0; @@ -762,10 +963,10 @@ ipset_parse_netmask(struct ipset_session *session, ipset_data_set(data, IPSET_OPT_FAMILY, &family); } - err = string_to_number(session, str, - family == AF_INET ? 1 : 4, - family == AF_INET ? 31 : 124, - &cidr); + err = string_to_cidr(session, str, + family == AF_INET ? 1 : 4, + family == AF_INET ? 31 : 124, + &cidr); if (err) return syntax_err("netmask is out of the inclusive range " @@ -864,15 +1065,72 @@ ipset_parse_output(struct ipset_session *session, return syntax_err("unkown output mode '%s'", str); } +/** + * ipset_parse_ignored - "parse" ignored option + * @session: session structure + * @opt: option kind of the data + * @str: string to parse + * + * Ignore deprecated options. A single warning is generated + * for every ignored opton. + * + * Returns 0. + */ +int +ipset_parse_ignored(struct ipset_session *session, + enum ipset_opt opt, const char *str) +{ + assert(session); + assert(str); + + if (!ipset_data_ignored(ipset_session_data(session), opt)) + ipset_warn(session, + "Option %s is ignored. Please upgrade your syntax.", str); + + return 0; +} + +/** + * ipset_call_parser - call a parser function + * @session: session structure + * @parsefn: parser function + * @optstr: option name + * @opt: option kind of the data + * @str: string to parse + * + * Wrapper to call the parser functions so that ignored options + * are handled properly. + * + * Returns 0 on success or a negative error code. + */ +int +ipset_call_parser(struct ipset_session *session, + ipset_parsefn parse, const char *optstr, + enum ipset_opt opt, const char *str) +{ + if (ipset_data_flags_test(ipset_session_data(session), + IPSET_FLAG(opt))) + syntax_err("%s already specified", optstr); + + return parse(session, opt, parse == ipset_parse_ignored + ? optstr : str); +} + #define parse_elem(s, t, d, str) \ do { \ - if (!t->elem[d].parse) \ + if (!(t)->elem[d].parse) \ goto internal; \ - err = t->elem[d].parse(s, t->elem[d].opt, str); \ - if (err) \ + ret = (t)->elem[d].parse(s, (t)->elem[d].opt, str); \ + if (ret) \ goto out; \ } while (0) +#define elem_syntax_err(fmt, args...) \ +do { \ + free(saved); \ + return syntax_err(fmt , ## args);\ +} while (0) + /** * ipset_parse_elem - parse ADT elem, depending on settype * @session: session structure @@ -890,7 +1148,7 @@ ipset_parse_elem(struct ipset_session *session, { const struct ipset_type *type; char *a = NULL, *b = NULL, *tmp, *saved; - int err; + int ret; assert(session); assert(str); @@ -906,40 +1164,43 @@ ipset_parse_elem(struct ipset_session *session, "Cannot allocate memory to duplicate %s.", str); - a = ipset_elem_separator(tmp); + a = elem_separator(tmp); if (type->dimension > IPSET_DIM_ONE) { if (a != NULL) { /* elem,elem */ *a++ = '\0'; - } else if (type->dimension > IPSET_DIM_TWO && !optional) { - free(tmp); - return syntax_err("Second element is missing from %s.", - str); + } else if (!optional) + elem_syntax_err("Second element is missing from %s.", + str); + } else if (a != NULL) { + if (type->compat_parse_elem) { + ret = type->compat_parse_elem(session, + type->elem[IPSET_DIM_ONE].opt, + saved); + goto out; } - } else if (a != NULL) - return syntax_err("Elem separator in %s, " - "but settype %s supports none.", - str, type->name); + elem_syntax_err("Elem separator in %s, " + "but settype %s supports none.", + str, type->name); + } if (a) - b = ipset_elem_separator(a); + b = elem_separator(a); if (type->dimension > IPSET_DIM_TWO) { if (b != NULL) { /* elem,elem,elem */ *b++ = '\0'; - } else if (!optional) { - free(tmp); - return syntax_err("Third element is missing from %s.", - str); - } + } else if (!optional) + elem_syntax_err("Third element is missing from %s.", + str); } else if (b != NULL) - return syntax_err("Two elem separators in %s, " - "but settype %s supports one.", - str, type->name); - if (b != NULL && ipset_elem_separator(b)) - return syntax_err("Three elem separators in %s, " - "but settype %s supports two.", - str, type->name); + elem_syntax_err("Two elem separators in %s, " + "but settype %s supports one.", + str, type->name); + if (b != NULL && elem_separator(b)) + elem_syntax_err("Three elem separators in %s, " + "but settype %s supports two.", + str, type->name); D("parse elem part one: %s", tmp); parse_elem(session, type, IPSET_DIM_ONE, tmp); @@ -954,10 +1215,10 @@ ipset_parse_elem(struct ipset_session *session, goto out; internal: - err = ipset_err(session, + ret = ipset_err(session, "Internal error: missing parser function for %s", type->name); out: free(saved); - return err; + return ret; } diff --git a/lib/print.c b/lib/print.c index 4df0905..d96e643 100644 --- a/lib/print.c +++ b/lib/print.c @@ -13,6 +13,7 @@ #include /* inet_ntop */ #include /* ETH_ALEN */ +#include /* D() */ #include /* ipset_data_* */ #include /* IPSET_*_SEPARATOR */ #include /* ipset set types */ @@ -86,7 +87,7 @@ ipset_print_ether(char *buf, unsigned int len, */ int ipset_print_family(char *buf, unsigned int len, - const struct ipset_data *data, int opt, + const struct ipset_data *data, enum ipset_opt opt, uint8_t env UNUSED) { uint8_t family; @@ -172,10 +173,10 @@ snprintf_ipv##f(char *buf, unsigned int len, int flags, \ size = __getnameinfo##f(buf, len, flags, ip); \ SNPRINTF_FAILURE(size, len, offset); \ \ + D("cidr %u mask %u", cidr, mask); \ if (cidr == mask) \ return offset; \ - if ((unsigned int)(size + 5) < len) \ - return -1; \ + D("print cidr"); \ size = snprintf(buf + offset, len, \ "%s%u", IPSET_CIDR_SEPARATOR, cidr); \ SNPRINTF_FAILURE(size, len, offset); \ @@ -218,9 +219,10 @@ ipset_print_ip(char *buf, unsigned int len, D("len: %u", len); family = ipset_data_family(data); cidropt = opt == IPSET_OPT_IP ? IPSET_OPT_CIDR : IPSET_OPT_CIDR2; - if (ipset_data_test(data, cidropt)) + if (ipset_data_test(data, cidropt)) { cidr = *(uint8_t *) ipset_data_get(data, cidropt); - else + D("CIDR: %u", cidr); + } else cidr = family == AF_INET6 ? 128 : 32; flags = env & (1 << IPSET_ENV_RESOLVE) ? 0 : NI_NUMERICHOST; @@ -373,11 +375,14 @@ ipset_print_name(char *buf, unsigned int len, SNPRINTF_FAILURE(size, len, offset); if (ipset_data_test(data, IPSET_OPT_NAMEREF)) { - bool before = ipset_data_test(data, IPSET_OPT_BEFORE); + bool before = false; + if (ipset_data_flags_test(data, IPSET_FLAG(IPSET_OPT_FLAGS))) { + uint32_t *flags = + (uint32_t *)ipset_data_get(data, IPSET_OPT_FLAGS); + before = (*flags) & IPSET_FLAG_BEFORE; + } size = snprintf(buf + offset, len, - "%s%s%s%s", IPSET_ELEM_SEPARATOR, - before ? "before" : "after", - IPSET_ELEM_SEPARATOR, + " %s %s", before ? "before" : "after", (const char *) ipset_data_get(data, IPSET_OPT_NAMEREF)); SNPRINTF_FAILURE(size, len, offset); @@ -468,8 +473,8 @@ ipset_print_elem(char *buf, unsigned int len, size = type->elem[IPSET_DIM_ONE].print(buf, len, data, type->elem[IPSET_DIM_ONE].opt, env); SNPRINTF_FAILURE(size, len, offset); - if (ipset_data_test(data, type->elem[IPSET_DIM_TWO].opt)) - D("print second elem"); + IF_D(ipset_data_test(data, type->elem[IPSET_DIM_TWO].opt), + "print second elem"); if (type->dimension == IPSET_DIM_ONE || (type->last_elem_optional && !ipset_data_test(data, type->elem[IPSET_DIM_TWO].opt))) diff --git a/lib/session.c b/lib/session.c index 2c4e39a..2c85468 100644 --- a/lib/session.c +++ b/lib/session.c @@ -13,6 +13,7 @@ #include /* getpagesize */ #include /* ETH_ALEN */ +#include /* D() */ #include /* IPSET_OPT_* */ #include /* ipset_errcode */ #include /* ipset_print_* */ @@ -189,6 +190,8 @@ ipset_session_report(struct ipset_session *session, if (len >= IPSET_ERRORBUFLEN - 1 - offset) session->report[IPSET_ERRORBUFLEN - 1] = '\0'; + if (strlen(session->report) < IPSET_ERRORBUFLEN - 1) + strcat(session->report, "\n"); if (type == IPSET_ERROR) { session->errmsg = session->report; @@ -278,6 +281,10 @@ const struct ipset_attr_policy cmd_attrs[] = { .type = MNL_TYPE_U8, .opt = IPSET_OPT_FAMILY, }, + [IPSET_ATTR_FLAGS] = { + .type = MNL_TYPE_U32, + .opt = IPSET_OPT_FLAGS, + }, [IPSET_ATTR_DATA] = { .type = MNL_TYPE_NESTED, }, @@ -320,9 +327,9 @@ const struct ipset_attr_policy create_attrs[] = { .type = MNL_TYPE_U32, .opt = IPSET_OPT_TIMEOUT, }, - [IPSET_ATTR_FLAGS] = { + [IPSET_ATTR_CADT_FLAGS] = { .type = MNL_TYPE_U32, - .opt = IPSET_OPT_FLAGS, + .opt = IPSET_OPT_CADT_FLAGS, }, [IPSET_ATTR_GC] = { .type = MNL_TYPE_U32, @@ -391,9 +398,9 @@ const struct ipset_attr_policy adt_attrs[] = { .type = MNL_TYPE_U32, .opt = IPSET_OPT_TIMEOUT, }, - [IPSET_ATTR_FLAGS] = { + [IPSET_ATTR_CADT_FLAGS] = { .type = MNL_TYPE_U32, - .opt = IPSET_OPT_FLAGS, + .opt = IPSET_OPT_CADT_FLAGS, }, [IPSET_ATTR_LINENO] = { .type = MNL_TYPE_U32, @@ -434,6 +441,7 @@ attr2data(struct ipset_session *session, struct nlattr *nla[], struct ipset_data *data = session->data; const struct ipset_attr_policy *attr; const void *d; + int ret; attr = &attrs[type]; d = mnl_attr_get_payload(nla[type]); @@ -480,7 +488,14 @@ attr2data(struct ipset_session *session, struct nlattr *nla[], break; } } - return ipset_data_set(data, attr->opt, d); + if (type == IPSET_ATTR_TYPENAME) + D("nla typename %s", (char *) d); + ret = ipset_data_set(data, attr->opt, d); + if (type == IPSET_ATTR_TYPENAME) + D("nla typename %s", + (char *) ipset_data_get(data, IPSET_OPT_TYPENAME)); + + return ret; } #define ATTR2DATA(session, nla, type, attrs) \ @@ -579,8 +594,9 @@ list_adt(struct ipset_session *session, struct nlattr *nla[]) const struct ipset_type *type; const struct ipset_arg *arg; uint8_t family; - int i; - + int i, found = 0; + + D("enter"); /* Check and load type, family */ if (!ipset_data_test(data, IPSET_OPT_TYPE)) type = ipset_type_get(session, IPSET_CMD_ADD); @@ -591,6 +607,15 @@ list_adt(struct ipset_session *session, struct nlattr *nla[]) return MNL_CB_ERROR; family = ipset_data_family(data); + for (i = IPSET_ATTR_UNSPEC + 1; i <= IPSET_ATTR_ADT_MAX; i++) + if (nla[i]) { + found++; + ATTR2DATA(session, nla, i, adt_attrs); + } + D("attr found %u", found); + if (!found) + return MNL_CB_OK; + switch (session->mode) { case IPSET_LIST_SAVE: safe_snprintf(session, "add %s ", ipset_data_setname(data)); @@ -603,10 +628,6 @@ list_adt(struct ipset_session *session, struct nlattr *nla[]) break; } - for (i = IPSET_ATTR_UNSPEC + 1; i <= IPSET_ATTR_ADT_MAX; i++) - if (nla[i]) - ATTR2DATA(session, nla, i, adt_attrs); - safe_dprintf(session, ipset_print_elem, IPSET_OPT_ELEM); for (arg = type->args[IPSET_ADD]; arg != NULL && arg->print; arg++) { @@ -646,6 +667,10 @@ list_adt(struct ipset_session *session, struct nlattr *nla[]) return MNL_CB_OK; } +#define FAMILY_TO_STR(f) \ + ((f) == AF_INET ? "inet" : \ + (f) == AF_INET6 ? "inet6" : "any") + static int list_create(struct ipset_session *session, struct nlattr *nla[]) { @@ -671,33 +696,29 @@ list_create(struct ipset_session *session, struct nlattr *nla[]) safe_snprintf(session, "create %s %s ", ipset_data_setname(data), type->name); - if (family == AF_INET6) - sprintf(session->outbuf, "family inet6 "); break; case IPSET_LIST_PLAIN: - safe_snprintf(session, "Name: %s\nType: %s\n", + safe_snprintf(session, "Name: %s\n" + "Type: %s\nHeader: ", ipset_data_setname(data), type->name); - if (family == AF_INET6) - safe_snprintf(session, "Family: INET6\n"); - safe_snprintf(session, "Header: "); break; case IPSET_LIST_XML: safe_snprintf(session, "\n" - " %s\n", + " %s\n" + "
\n", ipset_data_setname(data), type->name); - if (family == AF_INET6) - safe_snprintf(session, " INET6\n"); - safe_snprintf(session, "
\n"); break; default: break; } - for (arg = type->args[IPSET_CREATE]; arg != NULL && arg->print; arg++) { - if (!ipset_data_test(data, arg->opt)) + for (arg = type->args[IPSET_CREATE]; arg != NULL && arg->opt; arg++) { + if (!arg->print + || !ipset_data_test(data, arg->opt) + || (arg->opt == IPSET_OPT_FAMILY && family == type->family)) continue; switch (session->mode) { case IPSET_LIST_SAVE: @@ -730,8 +751,6 @@ list_create(struct ipset_session *session, struct nlattr *nla[]) safe_snprintf(session, "\n"); break; case IPSET_LIST_PLAIN: - safe_snprintf(session, "\nElements: "); - safe_dprintf(session, ipset_print_number, IPSET_OPT_ELEMENTS); safe_snprintf(session, "\nSize in memory: "); safe_dprintf(session, ipset_print_number, IPSET_OPT_MEMSIZE); safe_snprintf(session, "\nReferences: "); @@ -739,8 +758,6 @@ list_create(struct ipset_session *session, struct nlattr *nla[]) safe_snprintf(session, "\nMembers:\n"); break; case IPSET_LIST_XML: - safe_snprintf(session, " "); - safe_dprintf(session, ipset_print_number, IPSET_OPT_ELEMENTS); safe_snprintf(session, "\n "); safe_dprintf(session, ipset_print_number, IPSET_OPT_MEMSIZE); safe_snprintf(session, "\n "); @@ -757,7 +774,8 @@ list_create(struct ipset_session *session, struct nlattr *nla[]) static int print_set_done(struct ipset_session *session) { - D("called"); + D("called for %s", session->saved_setname[0] == '\0' + ? "NONE" : session->saved_setname); switch (session->mode) { case IPSET_LIST_XML: if (session->saved_setname[0] == '\0') @@ -824,6 +842,7 @@ callback_list(struct ipset_session *session, struct nlattr *nla[], cmd2name[cmd]); ATTR2DATA(session, nla, IPSET_ATTR_SETNAME, cmd_attrs); + D("setname %s", ipset_data_setname(data)); if (STREQ(ipset_data_setname(data), session->saved_setname)) { /* Header part already seen */ if (ipset_data_test(data, IPSET_OPT_TYPE) @@ -851,12 +870,18 @@ callback_list(struct ipset_session *session, struct nlattr *nla[], !nla[IPSET_ATTR_TYPENAME] ? "typename" : !nla[IPSET_ATTR_FAMILY] ? "family" : "revision"); + /* Reset CREATE specific flags */ + ipset_data_flags_unset(data, IPSET_CREATE_FLAGS); + D("nla typename %s", + (char *) mnl_attr_get_payload(nla[IPSET_ATTR_TYPENAME])); + D("nla typename %s", + (char *) mnl_attr_get_payload(nla[IPSET_ATTR_TYPENAME])); ATTR2DATA(session, nla, IPSET_ATTR_FAMILY, cmd_attrs); ATTR2DATA(session, nla, IPSET_ATTR_TYPENAME, cmd_attrs); ATTR2DATA(session, nla, IPSET_ATTR_REVISION, cmd_attrs); - - /* Reset CREATE specific flags */ - ipset_data_flags_unset(data, IPSET_CREATE_FLAGS); + D("head: family %u, typename %s", + ipset_data_family(data), + (char *) ipset_data_get(data, IPSET_OPT_TYPENAME)); if (mnl_attr_parse_nested(nla[IPSET_ATTR_DATA], create_attr_cb, cattr) < 0) FAILURE("Broken %s kernel message: " @@ -869,8 +894,9 @@ callback_list(struct ipset_session *session, struct nlattr *nla[], if (nla[IPSET_ATTR_ADT] != NULL) { struct nlattr *tb, *adt[IPSET_ATTR_ADT_MAX+1]; - + mnl_attr_for_each_nested(tb, nla[IPSET_ATTR_ADT]) { + D("ADT attributes for %s", ipset_data_setname(data)); memset(adt, 0, sizeof(adt)); /* Reset ADT specific flags */ ipset_data_flags_unset(data, IPSET_ADT_FLAGS); @@ -950,6 +976,7 @@ callback_header(struct ipset_session *session, struct nlattr *nla[]) ATTR2DATA(session, nla, IPSET_ATTR_TYPENAME, cmd_attrs); ATTR2DATA(session, nla, IPSET_ATTR_REVISION, cmd_attrs); ATTR2DATA(session, nla, IPSET_ATTR_FAMILY, cmd_attrs); + D("got family: %u", ipset_data_family(session->data)); return MNL_CB_STOP; } @@ -1150,7 +1177,8 @@ callback_error(const struct nlmsghdr *nlh, void *cbdata) case IPSET_CMD_CREATE: /* Add successfully created set to the cache */ ipset_cache_add(ipset_data_setname(data), - ipset_data_get(data, IPSET_OPT_TYPE)); + ipset_data_get(data, IPSET_OPT_TYPE), + ipset_data_family(data)); break; case IPSET_CMD_DESTROY: /* Delete destroyed sets from the cache */ @@ -1750,7 +1778,7 @@ ipset_session_init(ipset_outfn outfn) if (session->data == NULL) goto free_session; - ipset_types_init(); + ipset_cache_init(); return session; free_session: @@ -1776,7 +1804,7 @@ ipset_session_fini(struct ipset_session *session) if (session->data) ipset_data_fini(session->data); - ipset_types_fini(); + ipset_cache_fini(); free(session); return 0; } diff --git a/lib/types.c b/lib/types.c index a6476ea..b39a04f 100644 --- a/lib/types.c +++ b/lib/types.c @@ -13,6 +13,7 @@ #include /* malloc, free */ #include /* FIXME: debug */ +#include /* D() */ #include /* ipset_data_* */ #include /* ipset_cmd */ #include /* STREQ */ @@ -23,6 +24,7 @@ struct ipset { char name[IPSET_MAXNAMELEN]; /* set name */ const struct ipset_type *type; /* set type */ + uint8_t family; /* family */ struct ipset *next; }; @@ -40,7 +42,8 @@ static struct ipset *setlist = NULL; /* cached sets */ * Returns 0 on success or a negative error code. */ int -ipset_cache_add(const char *name, const struct ipset_type *type) +ipset_cache_add(const char *name, const struct ipset_type *type, + uint8_t family) { struct ipset *s, *n; @@ -53,13 +56,14 @@ ipset_cache_add(const char *name, const struct ipset_type *type) ipset_strncpy(n->name, name, IPSET_MAXNAMELEN); n->type = type; + n->family = family; n->next = NULL; if (setlist == NULL) { setlist = n; return 0; } - for (s = setlist; s->next == NULL; s = s->next) { + for (s = setlist; s->next != NULL; s = s->next) { if (STREQ(name, s->name)) { free(n); return -EEXIST; @@ -171,6 +175,22 @@ ipset_cache_swap(const char *from, const char *to) #define MATCH_FAMILY(type, f) \ (f == AF_UNSPEC || type->family == f || type->family == AF_INET46) +bool +ipset_match_typename(const char *name, const struct ipset_type *type) +{ + const char * const * alias = type->alias; + + if (STREQ(name, type->name)) + return true; + + while (alias[0]) { + if (STREQ(name, alias[0])) + return true; + alias++; + } + return false; +} + static inline const struct ipset_type * create_type_get(struct ipset_session *session) { @@ -192,7 +212,7 @@ create_type_get(struct ipset_session *session) /* Skip revisions which are unsupported by the kernel */ if (t->kernel_check == IPSET_KERNEL_MISMATCH) continue; - if ((STREQ(typename, t->name) || STREQ(typename, t->alias)) + if (ipset_match_typename(typename, t) && MATCH_FAMILY(t, family)) { if (match == NULL) { match = t; @@ -390,8 +410,9 @@ ipset_type_check(struct ipset_session *session) for (t = typelist; t != NULL && match == NULL; t = t->next) { if (t->kernel_check == IPSET_KERNEL_MISMATCH) continue; - if ((STREQ(typename, t->name) || STREQ(typename, t->alias)) - && MATCH_FAMILY(t, family) && t->revision == revision) + if (ipset_match_typename(typename, t) + && MATCH_FAMILY(t, family) + && t->revision == revision) match = t; } if (!match) @@ -503,7 +524,7 @@ ipset_typename_resolve(const char *str) const struct ipset_type *t; for (t = typelist; t != NULL; t = t->next) - if (STREQ(str, t->name) || STREQ(str, t->alias)) + if (ipset_match_typename(str, t)) return t->name; return NULL; } @@ -523,38 +544,25 @@ ipset_types(void) } /** - * ipset_types_init - initialize known set types + * ipset_cache_init - initialize set cache * - * Initialize the type list with the known, supported set types. + * Initialize the set cache in userspace. * * Returns 0 on success or a negative error code. */ int -ipset_types_init(void) +ipset_cache_init(void) { - if (typelist != NULL) - return 0; - - ipset_type_add(&ipset_bitmap_ip0); - ipset_type_add(&ipset_bitmap_ipmac0); - ipset_type_add(&ipset_bitmap_port0); - ipset_type_add(&ipset_hash_ip0); - ipset_type_add(&ipset_hash_net0); - ipset_type_add(&ipset_hash_ipport0); - ipset_type_add(&ipset_hash_ipportip0); - ipset_type_add(&ipset_hash_ipportnet0); - ipset_type_add(&ipset_tree_ip0); - ipset_type_add(&ipset_list_set0); return 0; } /** - * ipset_types_fini - release initialized known set types + * ipset_cache_fini - release the set cache * - * Release initialized known set types and remove the set cache. + * Release the set cache. */ void -ipset_types_fini(void) +ipset_cache_fini(void) { struct ipset *set; diff --git a/lib/utils.c b/lib/utils.c deleted file mode 100644 index bddeb87..0000000 --- a/lib/utils.c +++ /dev/null @@ -1,103 +0,0 @@ -/* Copyright 2007-20010 Jozsef Kadlecsik (kadlec@blackhole.kfki.hu) - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - */ -#include /* assert */ -#include /* bool */ -#include /* malloc, free */ -#include /* memset, str* */ - -#include /* ipset_err */ -#include /* prototypes */ - -/** - * ipset_strchr - locate character(s) in string - * @str: string to locate the character(s) in - * @sep: string of characters to locate - * - * Return a pointer to the first occurence of any of the - * characters to be located in the string. NULL is returned - * if no character is found. - */ -char * -ipset_strchr(const char *str, const char *sep) -{ - char *match; - - assert(str); - assert(sep); - - for (; *sep != '\0'; sep++) - if ((match = strchr(str, (int)sep[0])) != NULL) - return match; - - return NULL; -} - -/** - * ipset_name_match - match a string against an array of strings - * @arg: string - * @name: array of strings, last one is a NULL pointer - * - * Return true if arg matches any of the strings in the array. - */ -bool -ipset_name_match(const char *arg, const char * const name[]) -{ - int i = 0; - - assert(arg); - assert(name); - - while (name[i]) { - if (STREQ(arg, name[i])) - return true; - i++; - } - - return false; -} - -/** - * ipset_shift_argv - shift off an argument - * @arc: argument count - * @argv: array of argument strings - * @from: from where shift off an argument - * - * Shift off the argument at "from" from the array of - * arguments argv of size argc. - */ -void -ipset_shift_argv(int *argc, char *argv[], int from) -{ - int i; - - assert(*argc >= from + 1); - - for (i = from + 1; i <= *argc; i++) { - argv[i-1] = argv[i]; - } - (*argc)--; - return; -} - -/** - * ipset_strncpy - copy the string from src to dst - * @dst: the target string buffer - * @src: the source string buffer - * @len: the length of bytes to copy, including the terminating null byte. - * - * Copy the string from src to destination, but at most len bytes are - * copied. The target is unconditionally terminated by the null byte. - */ -void -ipset_strncpy(char *dst, const char *src, size_t len) -{ - assert(dst); - assert(src); - - strncpy(dst, src, len); - dst[len - 1] = '\0'; -} diff --git a/netlink.patch b/netlink.patch index 685cd23..344cfc8 100644 --- a/netlink.patch +++ b/netlink.patch @@ -37,7 +37,7 @@ index ab5d312..ef8b229 100644 #define NL_NONROOT_RECV 0x1 #define NL_NONROOT_SEND 0x2 diff --git a/net/netlink/af_netlink.c b/net/netlink/af_netlink.c -index 19e9800..1b9dbe8 100644 +index 19e9800..7d85d45 100644 --- a/net/netlink/af_netlink.c +++ b/net/netlink/af_netlink.c @@ -1714,15 +1714,18 @@ errout: @@ -64,17 +64,17 @@ index 19e9800..1b9dbe8 100644 cb = kzalloc(sizeof(*cb), GFP_KERNEL); if (cb == NULL) -@@ -1748,6 +1751,10 @@ int netlink_dump_start(struct sock *ssk, struct sk_buff *skb, - sock_put(sk); - return -EBUSY; - } +@@ -1733,6 +1736,10 @@ int netlink_dump_start(struct sock *ssk, struct sk_buff *skb, + cb->nlh = nlh; + atomic_inc(&skb->users); + cb->skb = skb; + va_start(args, init); + for (i = 0; i < init; i++) -+ cb->args[i] = va_arg(args, unsigned long); ++ cb->args[i] = va_arg(args, long); + va_end(args); - nlk->cb = cb; - mutex_unlock(nlk->cb_mutex); + sk = netlink_lookup(sock_net(ssk), ssk->sk_protocol, NETLINK_CB(skb).pid); + if (sk == NULL) { @@ -1759,7 +1766,7 @@ int netlink_dump_start(struct sock *ssk, struct sk_buff *skb, */ return -EINTR; diff --git a/src/Makefile.am b/src/Makefile.am index 28b28fa..ebd08a3 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -12,7 +12,6 @@ ipset_SOURCES = ipset.c \ ipset_hash_ipportnet.c \ ipset_hash_net.c \ ipset_list_set.c \ - ipset_tree_ip.c \ ui.c ipset_LDADD = ../lib/libipset.la AM_LDFLAGS = -static diff --git a/src/errcode.c b/src/errcode.c index 34b87a3..c30ecb6 100644 --- a/src/errcode.c +++ b/src/errcode.c @@ -8,6 +8,7 @@ #include /* errno */ #include /* strerror */ +#include /* D() */ #include /* ipset_data_get */ #include /* ipset_err */ #include /* struct ipset_type */ @@ -15,6 +16,7 @@ #include /* prototypes */ #include /* bitmap specific errcodes */ #include /* hash specific errcodes */ +#include /* list specific errcodes */ /* Core kernel error codes */ static const struct ipset_errcode_table core_errcode_table[] = { @@ -45,6 +47,8 @@ static const struct ipset_errcode_table core_errcode_table[] = { /* RENAME specific error codes */ { IPSET_ERR_EXIST_SETNAME2, IPSET_CMD_RENAME, "Set cannot be renamed: a set with the new name already exists" }, + { IPSET_ERR_REFERENCED, IPSET_CMD_RENAME, + "Set cannot be renamed: it is in use by another system" }, /* SWAP specific error codes */ { IPSET_ERR_EXIST_SETNAME2, IPSET_CMD_SWAP, @@ -101,6 +105,24 @@ static const struct ipset_errcode_table hash_errcode_table[] = { { }, }; +/* List type-specific error codes */ +static const struct ipset_errcode_table list_errcode_table[] = { + /* Generic (CADT) error codes */ + { IPSET_ERR_NAME, 0, + "Set to be added/deleted/tested as element does not exist." }, + { IPSET_ERR_LOOP, 0, + "Sets with list:set type cannot be added to the set." }, + { IPSET_ERR_BEFORE, 0, + "No reference set specified." }, + { IPSET_ERR_NAMEREF, 0, + "The set to which you referred with 'before' or 'after' does not exist." }, + { IPSET_ERR_LIST_FULL, 0, + "The set is full, more elements cannot be added." }, + { IPSET_ERR_REF_EXIST, 0, + "The set to which you referred with 'before' or 'after' is not added to the set." }, + { }, +}; + #define MATCH_TYPENAME(a, b) STRNEQ(a, b, strlen(b)) /** @@ -126,8 +148,10 @@ ipset_errcode(struct ipset_session *session, enum ipset_cmd cmd, int errcode) if (type) { if (MATCH_TYPENAME(type->name, "bitmap:")) table = bitmap_errcode_table; - if (MATCH_TYPENAME(type->name, "hash:")) + else if (MATCH_TYPENAME(type->name, "hash:")) table = hash_errcode_table; + else if (MATCH_TYPENAME(type->name, "list:")) + table = list_errcode_table; } } diff --git a/src/ipset.8 b/src/ipset.8 index fa73298..661d1b4 100644 --- a/src/ipset.8 +++ b/src/ipset.8 @@ -1,537 +1,628 @@ -.TH IPSET 8 "Feb 05, 2004" "" "" -.\" .\" Man page written by Jozsef Kadlecsik -.\" -.\" This program is free software; you can redistribute it and/or modify -.\" it under the terms of the GNU General Public License as published by -.\" the Free Software Foundation; either version 2 of the License, or -.\" (at your option) any later version. -.\" -.\" This program is distributed in the hope that it will be useful, -.\" but WITHOUT ANY WARRANTY; without even the implied warranty of -.\" MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -.\" GNU General Public License for more details. -.\" -.\" You should have received a copy of the GNU General Public License -.\" along with this program; if not, write to the Free Software -.\" Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. -.\" -.\" -.SH NAME +.\" +.\" This program is free software; you can redistribute it and/or modify +.\" it under the terms of the GNU General Public License as published by +.\" the Free Software Foundation; either version 2 of the License, or +.\" (at your option) any later version. +.\" +.\" This program is distributed in the hope that it will be useful, +.\" but WITHOUT ANY WARRANTY; without even the implied warranty of +.\" MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +.\" GNU General Public License for more details. +.\" +.\" You should have received a copy of the GNU General Public License +.\" along with this program; if not, write to the Free Software +.\" Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +.TH "IPSET" "8" "Jun 11, 2010" "Jozsef Kadlecsik" "" +.SH "NAME" ipset \(em administration tool for IP sets -.SH SYNOPSIS -.PP -\fBipset \-N\fP \fIset\fP \fItype-specification\fP [\fIoptions\fP...] -.PP -\fBipset\fP {\fB\-F\fP|\fB\-H\fP|\fB\-L\fP|\fB\-S\fP|\fB\-X\fP} [\fIset\fP] -[\fIoptions\fP...] -.PP -\fBipset\fP {\fB\-E\fP|\fB\-W\fP} \fIfrom-set\fP \fIto-set\fP -.PP -\fBipset\fP {\fB\-A\fP|\fB\-D\fP|\fB\-T\fP} \fIset\fP \fIentry\fP -.PP -\fBipset \-R\fP -.PP -\fBipset\fP {\fB-V\fP|\fB\-v\fP} -.SH DESCRIPTION -.B ipset +.SH "SYNOPSIS" +\fBipset\fR [ \fIOPTIONS\fR ] \fICOMMAND\fR [ \fICOMMAND\-OPTIONS\fR ] +.PP +COMMANDS := { \fBcreate\fR | \fBadd\fR | \fBdel\fR | \fBtest\fR | \fBdestroy\fR | \fBlist\fR | \fBsave\fR | \fBrestore\fR | \fBflush\fR | \fBrename\fR | \fBswap\fR | \fBhelp\fR | \fBversion\fR | \fB\-\fR } +.PP +\fIOPTIONS\fR := { \fB\-exist\fR | \fB\-output\fR { \fBplain\fR | \fBsave\fR | \fBxml\fR } | \fB\-quiet\fR | \fB\-resolve\fR | \fB\-sorted\fR } +.PP +\fBipset\fR \fBcreate\fR \fISETNAME\fR \fITYPENAME\fR [ \fICREATE\-OPTIONS\fR ] +.PP +\fBipset\fR \fBadd\fR \fISETNAME\fR \fIADD\-ENTRY\fR [ \fIADD\-OPTIONS\fR ] +.PP +\fBipset\fR \fBdel\fR \fISETNAME\fR \fIDEL\-ENTRY\fR [ \fIDEL\-OPTIONS\fR ] +.PP +\fBipset\fR \fBtest\fR \fISETNAME\fR \fITEST\-ENTRY\fR [ \fITEST\-OPTIONS\fR ] +.PP +\fBipset\fR \fBdestroy\fR [ \fISETNAME\fR ] +.PP +\fBipset\fR \fBlist\fR [ \fISETNAME\fR ] +.PP +\fBipset\fR \fBsave\fR [ \fISETNAME\fR ] +.PP +\fBipset\fR \fBrestore\fR +.PP +\fBipset\fR \fBflush\fR [ \fISETNAME\fR ] +.PP +\fBipset\fR \fBrename\fR \fISETNAME\-FROM\fR \fISETNAME\-TO\fR +.PP +\fBipset\fR \fBswap\fR \fISETNAME\-FROM\fR \fISETNAME\-TO\fR +.PP +\fBipset\fR \fBhelp\fR [ \fITYPENAME\fR ] +.PP +\fBipset\fR \fBversion\fR +.PP +\fBipset\fR \fB\-\fR +.SH "DESCRIPTION" +\fBipset\fR is used to set up, maintain and inspect so called IP sets in the Linux -kernel. Depending on the type, an IP set may store IP addresses, (TCP/UDP) -port numbers or additional informations besides IP addresses: the word IP -means a general term here. See the set type definitions below. -.P -Iptables matches and targets referring to sets creates references, which -protects the given sets in the kernel. A set cannot be removed (destroyed) +kernel. Depending on the type of the set, an IP set may store IP(v4/v6) +addresses, (TCP/UDP) port numbers, IP and MAC address pairs, IP address +and port number pairs, etc. See the set type definitions below. +.PP +\fBIptables\fR +matches and targets referring to sets creates references, which +protect the given sets in the kernel. A set cannot be destroyed while there is a single reference pointing to it. -.SH OPTIONS +.SH "OPTIONS" The options that are recognized by -.B ipset +\fBipset\fR can be divided into several different groups. .SS COMMANDS -These options specify the specific action to perform. Only one of them -can be specified on the command line unless otherwise specified -below. For all the long versions of the command and option names, you -need to use only enough letters to ensure that -.B ipset -can differentiate it from all other options. -.TP -\fB\-N\fP, \fB\-\-create\fP \fIsetname\fP \fItype\fP \fItype-specific-options\fP -Create a set identified with setname and specified type. -Type-specific options must be supplied. -.TP -\fB\-X\fP, \fB\-\-destroy\fP [\fIsetname\fP] +These options specify the desired action to perform. Only one of them +can be specified on the command line unless otherwise specified below. +For all the long versions of the command names, you need to use only enough +letters to ensure that +\fBipset\fR +can differentiate it from all other options. The +\fBipset\fR +parser follows the order here when looking for the shortest match +in the long command names. +.TP +\fBn\fP, \fBcreate\fP \fISETNAME\fP \fITYPENAME\fP [ \fICREATE\-OPTIONS\fP ] +Create a set identified with setname and specified type. The type may require +type specific options. If the +\fB\-exist\fR +option is specified, +\fBipset\fR +ignores the error otherwise raised when the the same set (setname and create parameters +are identical) already exists. +.TP +\fBadd\fP \fISETNAME\fP \fIADD\-ENTRY\fP [ \fIADD\-OPTIONS\fP ] +Add a given entry to the set. If the +\fB\-exist\fR +option is specified, +\fBipset\fR +ignores if the entry already added to the set. +.TP +\fBdel\fP \fISETNAME\fP \fIDEL\-ENTRY\fP [ \fIDEL\-OPTIONS\fP ] +Delete an entry from a set. If the +\fB\-exist\fR +option is specified, +\fBipset\fR +ignores if the entry does not added (expired) to the set. +.TP +\fBtest\fP \fISETNAME\fP \fITEST\-ENTRY\fP [ \fITEST\-OPTIONS\fP ] +Test wether an entry is in a set or not. Exit status number is zero +if the tested entry is in the set and nonzero if it is missing from +the set. +.TP +\fBx\fP, \fBdestroy\fP [ \fISETNAME\fP ] Destroy the specified set or all the sets if none is given. -If the set has got references, nothing is done. -.TP -\fB\-F\fP, \fB\-\-flush\fP [\fIsetname\fP] -Delete all entries from the specified set or flush -all sets if none is given. -.TP -\fB\-E\fP, \fB\-\-rename\fP \fIfrom-setname\fP \fIto-setname\fP -Rename a set. Set identified by to-setname must not exist. -.TP -\fB\-W\fP, \fB\-\-swap\fP \fIfrom-setname\fP \fIto-setname\fP -Swap the content of two sets, or in another words, -exchange the name of two sets. The referred sets must exist and -identical type of sets can be swapped only. -.TP -\fB\-L\fP, \fB\-\-list\fP [\fIsetname\fP] -List the entries for the specified set, or for +If the set has got reference(s), nothing is done and no set destroyed. +.TP +\fBlist\fP [ \fISETNAME\fP ] +List the header data and the entries for the specified set, or for all sets if none is given. The -\fB\-r\fP/\fB\-\-resolve\fP +\fB\-\-resolve\fP option can be used to force name lookups (which may be slow). When the -\fB\-s\fP/\fB\-\-sorted\fP +\fB\-\-sorted\fP option is given, the entries are listed sorted (if the given set -type supports the operation). -.TP -\fB\-S\fP, \fB\-\-save\fP [\fIsetname\fP] +type supports the operation). The option +\fB\-\-output\fR +can be used to control the format of the listing: +\fBplain\fR, \fBsave\fR or \fBxml\fR. +The default is +\fBplain\fR. +.TP +\fBsave\fP [ \fISETNAME\fP ] Save the given set, or all sets if none is given -to stdout in a format that \fB\-\-restore\fP can read. -.TP -\fB\-R\fP, \fB\-\-restore\fP -Restore a saved session generated by \fB\-\-save\fP. The saved session -can be fed from stdin. - -When generating a session file please note that the supported commands -(create set and add element) must appear in a strict order: first create -the set, then add all elements. Then create the next set, add all its elements -and so on. Also, it is a restore operation, so the sets being restored must -not exist. -.TP -\fB\-A\fP, \fB\-\-add\fP \fIsetname\fP \fIentry\fP -Add an entry to a set. -.TP -\fB\-D\fP, \fB\-\-del\fP \fIsetname\fP \fIentry\fP -Delete an entry from a set. -.TP -\fB-T\fP, \fB\-\-test\fP \fIsetname\fP \fIentry\fP -Test wether an entry is in a set or not. Exit status number is zero -if the tested entry is in the set and nonzero if it is missing from -the set. -.TP -\fB\-H\fP, \fB\-\-help\fP [\fIsettype\fP] -Print help and settype specific help if settype specified. -.TP -\fB\-V\fP, \fB\-v\fP, \fB\-\-version\fP -Print program version and protocol version. +to stdout in a format that +\fBrestore\fP +can read. +.TP +\fBrestore\fP +Restore a saved session generated by +\fBsave\fP. +The saved session can be fed from stdin. +.TP +\fBflush\fP [ \fISETNAME\fP ] +Flush all entries from the specified set or flush +all sets if none is given. +.TP +\fBe\fP, \fBrename\fP \fISETNAME\-FROM\fP \fISETNAME\-TO\fP +Rename a set. Set identified by +\fISETNAME\-TO\fR +must not exist. +.TP +\fBw\fP, \fBswap\fP \fISETNAME\-FROM\fP \fISETNAME\-TO\fP +Swap the content of two sets, or in another words, +exchange the name of two sets. The referred sets must exist and +identical type of sets can be swapped only. +.TP +\fBhelp\fP [ \fITYPENAME\fP ] +Print help and set type specific help if +\fITYPENAME\fR +is specified. +.TP +\fBversion\fP +Print program version. +.TP +\fB\-\fP +If a dash is specified as command, then +\fBipset\fR +enters a simple interactive mode and the commands are read from the standard input. +The interactive mode can be finished by entering the pseudo\-command +\fBquit\fR. .P .SS "OTHER OPTIONS" -The following additional options can be specified: -.TP -\fB\-r\fP, \fB\-\-resolve\fP +The following additional options can be specified. The long option names +cannot be abbreviated. +.TP +\fB\-!\fP, \fB\-exist\fP +Ignore errors when the exactly the same set is to be created or already +added entry is added or missing entry is deleted. +.TP +\fB\-o\fP, \fB\-output\fP { \fBplain\fR | \fBsave\fR | \fBxml\fR } +Select the output format to the +\fBlist\fR +command. +.TP +\fB\-q\fP, \fB\-quiet\fP +Suppress any output to stdout and stderr. +\fBipset\fR +will still exit with error if it cannot continue. +.TP +\fB\-r\fP, \fB\-resolve\fP When listing sets, enforce name lookup. The program will try to display the IP entries resolved to -host names or services (whenever applicable), which can trigger -.B -slow -DNS -lookups. -.TP -\fB\-s\fP, \fB\-\-sorted\fP +host names which requires +\fBslow\fR +DNS lookups. +.TP +\fB\-s\fP, \fB\-sorted\fP Sorted output. When listing sets, entries are listed sorted. -.TP -\fB\-n\fP, \fB\-\-numeric\fP -Numeric output. When listing sets, IP addresses and -port numbers will be printed in numeric format. This is the default. -.TP -\fB\-q\fP, \fB\-\-quiet\fP -Suppress any output to stdout and stderr. ipset will still return -possible errors. -.SH SET TYPES -ipset supports the following set types: -.SS ipmap -The ipmap set type uses a memory range, where each bit represents -one IP address. An ipmap set can store up to 65536 (B-class network) -IP addresses. The ipmap set type is very fast and memory cheap, great -for use when one want to match certain IPs in a range. If the optional -\fB\-\-netmask\fP -parameter is specified with a CIDR netmask value between 1-31 then -network addresses are stored in the given set: i.e an -IP address will be in the set if the network address, which is resulted -by masking the address with the specified netmask, can be found in the set. -.P -Options to use when creating an ipmap set: -.TP -\fB\-\-from\fP \fIfrom-addr\fP -.TP -\fB\-\-to\fP \fIto-addr\fP -Create an ipmap set from the specified address range. -.TP -\fB\-\-network\fP \fIaddr\fP\fB/\fP\fImask\fP -Create an ipmap set from the specified network. -.TP -\fB\-\-netmask\fP \fIprefixlen\fP -When the optional -\fB\-\-netmask\fP -parameter specified, network addresses will be -stored in the set instead of IP addresses, and the \fIfrom-addr\fP parameter -must be a network address. The \fIprefixlen\fP value must be between 1-31. -.PP -Example: -.IP -ipset \-N test ipmap \-\-network 192.168.0.0/16 -.SS macipmap -The macipmap set type uses a memory range, where each 8 bytes -represents one IP and a MAC addresses. A macipmap set type can store -up to 65536 (B-class network) IP addresses with MAC. -When adding an entry to a macipmap set, you must specify the entry as -"\fIaddress\fP\fB,\fP\fImac\fP". -When deleting or testing macipmap entries, the -"\fB,\fP\fImac\fP" -part is not mandatory. -.P -Options to use when creating an macipmap set: -.TP -\fB\-\-from\fP \fIfrom-addr\fP -.TP -\fB\-\-to\fP \fIto-addr\fP -Create a macipmap set from the specified address range. -.TP -\fB\-\-network\fP \fIaddr\fP\fB/\fP\fImask\fP -Create a macipmap set from the specified network. -.TP -\fB\-\-matchunset\fP -When the optional -\fB\-\-matchunset\fP -parameter specified, IP addresses which could be stored -in the set but not set yet, will always match. -.P -Please note, the -"set" -and -"SET" -netfilter kernel modules -.B -always -use the source MAC address from the packet to match, add or delete -entries from a macipmap type of set. -.SS portmap -The portmap set type uses a memory range, where each bit represents -one port. A portmap set type can store up to 65536 ports. -The portmap set type is very fast and memory cheap. -.P -Options to use when creating an portmap set: -.TP -\fB\-\-from\fP \fIfrom-port\fP -.TP -\fB\-\-to\fP \fIto-port\fP -Create a portmap set from the specified port range. -.SS iphash -The iphash set type uses a hash to store IP addresses. -In order to avoid clashes in the hash double-hashing, and as a last -resort, dynamic growing of the hash performed. The iphash set type is -great to store random addresses. If the optional -\fB\-\-netmask\fP -parameter is specified with a CIDR prefix length value between 1-31 then -network addresses are stored in the given set: i.e an -IP address will be in the set if the network address, which is resulted -by masking the address with the specified netmask, can be found in the set. -.P -Options to use when creating an iphash set: -.TP -\fB\-\-hashsize\fP \fIhashsize\fP -The initial hash size (default 1024) -.TP -\fB\-\-probes\fP \fIprobes\fP -How many times try to resolve clashing at adding an IP to the hash -by double-hashing (default 8). -.TP -\fB\-\-resize\fP \fIpercent\fP -Increase the hash size by this many percent (default 50) when adding -an IP to the hash could not be performed after -\fIprobes\fP -number of double-hashing. -.TP -\fB\-\-netmask\fP \fIprefixlen\fP -When the optional -\fB\-\-netmask\fP -parameter specified, network addresses will be -stored in the set instead of IP addresses. The \fIprefixlen\fP value must -be between 1-31. -.P -The iphash type of sets can store up to 65536 entries. If a set is full, -no new entries can be added to it. -.P -Sets created by zero valued resize parameter won't be resized at all. -The lookup time in an iphash type of set grows approximately linearly with -the value of the -\fIprobes\fP -parameter. In general higher -\fIprobes\fP -value results better utilized hash while smaller value -produces larger, sparser hash. -.PP -Example: -.IP -ipset \-N test iphash \-\-probes 2 -.SS nethash -The nethash set type uses a hash to store different size of -network addresses. The -.I -entry -used in the ipset commands must be in the form -"\fIaddress\fP\fB/\fP\fIprefixlen\fP" -where prefixlen must be in the inclusive range of 1-31. -In order to avoid clashes in the hash -double-hashing, and as a last resort, dynamic growing of the hash performed. -.P -Options to use when creating an nethash set: -.TP -\fB\-\-hashsize\fP \fIhashsize\fP -The initial hash size (default 1024) -.TP -\fB\-\-probes\fP \fIprobes\fP -How many times try to resolve clashing at adding an IP to the hash -by double-hashing (default 4). -.TP -\fB\-\-resize\fP \fIpercent\fP -Increase the hash size by this many percent (default 50) when adding -an IP to the hash could not be performed after -.P -The nethash type of sets can store up to 65536 entries. If a set is full, -no new entries can be added to it. -.P -An IP address will be in a nethash type of set if it belongs to any of the -netblocks added to the set. The matching always start from the smallest -size of netblock (most specific netmask) to the largest ones (least -specific netmasks). When adding/deleting IP addresses -to a nethash set by the -"SET" -netfilter kernel module, it will be added/deleted by the smallest -netblock size which can be found in the set, or by /31 if the set is empty. -.P -The lookup time in a nethash type of set grows approximately linearly -with the times of the -\fIprobes\fP -parameter and the number of different mask parameters in the hash. -Otherwise the same speed and memory efficiency comments applies here -as at the iphash type. -.SS ipporthash -The ipporthash set type uses a hash to store IP address and port pairs. -In order to avoid clashes in the hash double-hashing, and as a last -resort, dynamic growing of the hash performed. An ipporthash set can -store up to 65536 (B-class network) IP addresses with all possible port -values. When adding, deleting and testing values in an ipporthash type of -set, the entries must be specified as -"\fIaddress\fP\fB,\fP\fIport\fP". -.P -The ipporthash types of sets evaluates two src/dst parameters of the -"set" -match and -"SET" -target. -.P -Options to use when creating an ipporthash set: -.TP -\fB\-\-from\fP \fIfrom-addr\fP -.TP -\fB\-\-to\fP \fIto-addr\fP -Create an ipporthash set from the specified address range. -.TP -\fB\-\-network\fP \fIaddr\fP\fB/\fP\fImask\fP -Create an ipporthash set from the specified network. -.TP -\fB\-\-hashsize\fP \fIhashsize\fP -The initial hash size (default 1024) -.TP -\fB\-\-probes\fP \fIprobes\fP -How many times try to resolve clashing at adding an IP to the hash -by double-hashing (default 8). -.TP -\fB\-\-resize\fP \fIpercent\fP -Increase the hash size by this many percent (default 50) when adding -an IP to the hash could not be performed after -\fIprobes\fP -number of double-hashing. -.P -The same resizing, speed and memory efficiency comments applies here -as at the iphash type. -.SS ipportiphash -The ipportiphash set type uses a hash to store IP address,port and IP -address triples. The first IP address must come form a maximum /16 -sized network or range while the port number and the second IP address -parameters are arbitrary. When adding, deleting and testing values in an -ipportiphash type of set, the entries must be specified as -"\fIaddress\fP\fB,\fP\fIport\fP\fB,\fP\fIaddress\fP". -.P -The ipportiphash types of sets evaluates three src/dst parameters of the -"set" -match and -"SET" -target. -.P -Options to use when creating an ipportiphash set: -.TP -\fB\-\-from\fP \fIfrom-addr\fP -.TP -\fB\-\-to\fP \fIto-addr\fP -Create an ipportiphash set from the specified address range. -.TP -\fB\-\-network\fP \fIaddr\fP\fB/\fP\fImask\fP -Create an ipportiphash set from the specified network. -.TP -\fB\-\-hashsize\fP \fIhashsize\fP -The initial hash size (default 1024) -.TP -\fB\-\-probes\fP \fIprobes\fP -How many times try to resolve clashing at adding an IP to the hash -by double-hashing (default 8). -.TP -\fB\-\-resize\fP \fIpercent\fP -Increase the hash size by this many percent (default 50) when adding -an IP to the hash could not be performed after -\fIprobes\fP -number of double-hashing. -.P -The same resizing, speed and memory efficiency comments applies here -as at the iphash type. -.SS ipportnethash -The ipportnethash set type uses a hash to store IP address, port, and -network address triples. The IP address must come form a maximum /16 -sized network or range while the port number and the network address -parameters are arbitrary, but the size of the network address must be -between /1-/31. When adding, deleting -and testing values in an ipportnethash type of set, the entries must be -specified as -"\fIaddress\fP\fB,\fP\fIport\fP\fB,\fP\fIaddress\fP\fB/\fP\fIprefixlen\fP". -.P -The ipportnethash types of sets evaluates three src/dst parameters of the -"set" -match and -"SET" -target. -.P -Options to use when creating an ipportnethash set: -.TP -\fB\-\-from\fP \fIfrom-address\fP -.TP -\fB\-\-to\fP \fIto-address\fP -Create an ipporthash set from the specified range. -.TP -\fB\-\-network\fP \fIaddress\fP\fB/\fP\fImask\fP -Create an ipporthash set from the specified network. -.TP -\fB\-\-hashsize\fP \fIhashsize\fP -The initial hash size (default 1024) -.TP -\fB\-\-probes\fP \fIprobes\fP -How many times try to resolve clashing at adding an IP to the hash -by double-hashing (default 8). -.TP -\fB\-\-resize\fP \fIpercent\fP -Increase the hash size by this many percent (default 50) when adding -an IP to the hash could not be performed after -\fIprobes\fP -number of double-hashing. -.P -The same resizing, speed and memory efficiency comments applies here -as at the iphash type. -.SS iptree -The iptree set type uses a tree to store IP addresses, optionally -with timeout values. -.P -Options to use when creating an iptree set: -.TP -\fB\-\-timeout\fP \fIvalue\fP -The timeout value for the entries in seconds (default 0) -.P -If a set was created with a nonzero valued -\fB\-\-timeout\fP -parameter then one may add IP addresses to the set with a specific -timeout value using the syntax -"\fIaddress\fP\fB,\fP\fItimeout-value\fP". -Similarly to the hash types, the iptree type of sets can store up to 65536 -entries. -.SS iptreemap -The iptreemap set type uses a tree to store IP addresses or networks, -where the last octet of an IP address are stored in a bitmap. -As input entry, you can add IP addresses, CIDR blocks or network ranges -to the set. Network ranges can be specified in the format -"\fIaddress1\fP\fB-\fP\fIaddress2\fP". -.P -Options to use when creating an iptreemap set: -.TP -\fB\-\-gc\fP \fIvalue\fP -How often the garbage collection should be called, in seconds (default 300) -.SS setlist -The setlist type uses a simple list in which you can store sets. By the -ipset -command you can add, delete and test sets in a setlist type of set. -You can specify the sets as -"\fIsetname\fP[\fB,\fP{\fBafter\fP|\fBbefore\fP},\fIsetname\fP]". -By default new sets are added after (appended to) the existing -elements. Setlist type of sets cannot be added to a setlist type of set. -.P -Options to use when creating a setlist type of set: -.TP -\fB\-\-size\fP \fIsize\fP -Create a setlist type of set with the given size (default 8). -.PP -By the -"set" -match or -"SET" -target of -\fBiptables\fP(8) -you can test, add or delete entries in the sets. The match -will try to find a matching IP address/port in the sets and -the target will try to add the IP address/port to the first set -to which it can be added. The number of src,dst options of -the match and target are important: sets which eats more src,dst -parameters than specified are skipped, while sets with equal -or less parameters are checked, elements added. For example -if -.I -a -and -.I -b -are setlist type of sets then in the command -.IP -iptables \-m set \-\-match\-set a src,dst \-j SET \-\-add-set b src,dst -.PP -the match and target will skip any set in -.I a -and -.I b -which stores -data triples, but will check all sets with single or double -data storage in -.I a +.SH "SET TYPES" +A set type comprises of the storage method by which the data is stored and +the data type(s) which are stored in the set. Therefore the +\fITYPENAME\fR +parameter of the +\fBcreate\fR +command follows the syntax + +\fITYPENAME\fR := \fImethod\fR\fB:\fR\fItype\fR[\fB,\fR\fItype\fR[\fB,\fR\fItype\fR]] + +where the current list of the methods are +\fBbitmap\fR, \fBhash\fR, \fBlist\fR and the possible data types are \fBip\fR, +\fBmac\fR and \fBport\fR. + +When adding, deleting or testing entries in a set, the same comma separated +data syntax must be used for the entry parameter of the commands, i.e + +ipset add foo ipaddr,portnum,ipaddr + +All set types support the optional + +\fBtimeout\fR \fIvalue\fR + +parameter when creating a set and adding entries. The value of the \fBtimeout\fR +parameter for the \fBcreate\fR command means the default timeout value (in seconds) +for new entries. If a set is created with timeout support, then the same +\fBtimeout\fR option can be used to specify non\-default timeout values +when adding entries. Zero timeout value means the entry is added permanent to the set. +.SS bitmap:ip +The \fBbitmap:ip\fR set type uses a memory range to store either IPv4 host +(default) or IPv4 network addresses. A \fBbitmap:ip\fR type of set can store up +to 65536 entries. +.PP +\fICREATE\-OPTIONS\fR := \fBrange\fP \fIfrom\-ip\fP\-\fIto\-ip\fR|\fIip\fR/\fIcidr\fR [ \fBnetmask\fP \fIcidr\fP ] [ \fBtimeout\fR \fIvalue\fR ] +.PP +\fIADD\-ENTRY\fR := { \fIipaddr\fR | \fIfromaddr\fR\-\fItoaddr\fR | \fIipaddr\fR/\fIcidr\fR } +.PP +\fIADD\-OPTIONS\fR := [ \fBtimeout\fR \fIvalue\fR ] +.PP +\fIDEL\-ENTRY\fR := { \fIipaddr\fR | \fIfromaddr\fR\-\fItoaddr\fR | \fIipaddr\fR/\fIcidr\fR } +.PP +\fITEST\-ENTRY\fR := { \fIipaddr\fR } +.PP +Mandatory \fBcreate\fR options: +.TP +\fBrange\fP \fIfrom\-ip\fP\-\fIto\-ip\fR|\fIip\fR/\fIcidr\fR +Create the set from the specified inclusive address range expressed in an +IPv4 address range or network. The size of the range (in entries) cannot exceed +the limit of maximum 65536 elements. +.PP +Optional \fBcreate\fR options: +.TP +\fBnetmask\fP \fIcidr\fP +When the optional \fBnetmask\fP parameter specified, network addresses will be +stored in the set instead of IP host addresses. The \fIcidr\fR value must be +between 1\-32. +An IP address will be in the set if the network address, which is resulted by +masking the address with the specified netmask calculated from the cidr value, +can be found in the set. +.PP +Examples: +.IP +ipset create foo bitmap:ip range 192.168.0.0/16 +.IP +ipset add foo 192.168.1/24 +.IP +ipset test foo 192.168.1.1 +.SS bitmap:ip,mac +The \fBbitmap:ip,mac\fR set type uses a memory range to store IPv4 and a MAC address pairs. A \fBbitmap:ip,mac\fR type of set can store up to 65536 entries. +.PP +\fICREATE\-OPTIONS\fR := \fBrange\fP \fIfrom\-ip\fP\-\fIto\-ip\fR|\fIip\fR/\fIcidr\fR [ \fBtimeout\fR \fIvalue\fR ] +.PP +\fIADD\-ENTRY\fR := { \fIipaddr\fR[,\fImac\-addr\fR] } +.PP +\fIADD\-OPTIONS\fR := [ \fBtimeout\fR \fIvalue\fR ] +.PP +\fIDEL\-ENTRY\fR := { \fIipaddr\fR[,\fImac\-addr\fR] } +.PP +\fITEST\-ENTRY\fR := { \fIipaddr\fR[,\fImac\-addr\fR] } +.PP +Mandatory options to use when creating a \fBbitmap:ip,mac\fR type of set: +.TP +\fBrange\fP \fIfrom\-ip\fP\-\fIto\-ip\fR|\fIip\fR/\fIcidr\fR +Create the set from the specified inclusive address range expressed in an +IPv4 address range or network. The size of the range cannot exceed the limit +of maximum 65536 entries. +.PP +The \fBbitmap:ip,mac\fR type is exceptional in the sense that the MAC part can +be left out when adding/deleting/testing entries in the set. If +we add an entry without the MAC address specified, when the first time the entry is +matched by the kernel, it will automatically fill out the missing part with the +source MAC address from the packet. If the entry was specified with a timeout value, +the timer starts off when the IP and MAC address pair is complete. +.PP +Please note, the \fBset\fR match and \fBSET\fR target netfilter kernel modules +\fBalways\fR use the source MAC address from the packet to match, add or delete +entries from a \fBbitmap:ip,mac\fR type of set. +.PP +Examples: +.IP +ipset create foo bitmap:ip,mac range 192.168.0.0/16 +.IP +ipset add foo 192.168.1,12:34:56:78:9A:BC +.IP +ipset test foo 192.168.1.1 +.SS bitmap:port +The \fBbitmap:port\fR set type uses a memory range to store port numbers +and such a set can store up to 65536 ports. +.PP +\fICREATE\-OPTIONS\fR := \fBrange\fP \fIfrom\-port\fP\-\fIto\-port [ \fBtimeout\fR \fIvalue\fR ] +.PP +\fIADD\-ENTRY\fR := { \fIport\fR | \fIfrom\-port\fR\-\fIto\-port\fR } +.PP +\fIADD\-OPTIONS\fR := [ \fBtimeout\fR \fIvalue\fR ] +.PP +\fIDEL\-ENTRY\fR := {\fIport\fR | \fIfrom\-port\fR\-\fIto\-port\fR } +.PP +\fITEST\-ENTRY\fR := { \fIport\fR } +.PP +Mandatory options to use when creating a \fBbitmap:port\fR type of set: +.TP +\fBrange\fP \fIfrom\-port\fP\-\fIto\-port\fR +Create the set from the specified inclusive port range. +.PP +Examples: +.IP +ipset create foo bitmap:port range 0\-1024 +.IP +ipset add foo 80 +.IP +ipset test foo 80 +.SS hash:ip +The \fBhash:ip\fR set type uses a hash to store IP addresses. +In order to avoid clashes in the hash a limited number of chaining, and then +if that is exhausted, the doubling of the hash is performed. +.PP +\fICREATE\-OPTIONS\fR := [ \fBfamily\fR { \fBinet\fR|\fBinet6\fR } ] | [ \fBhashsize\fR \fIvalue\fR ] [ \fBmaxelem\fR \fIvalue\fR ] [ \fBnetmask\fP \fIcidr\fP ] [ \fBtimeout\fR \fIvalue\fR ] +.PP +\fIADD\-ENTRY\fR := { \fIipaddr\fR } +.PP +\fIADD\-OPTIONS\fR := [ \fBtimeout\fR \fIvalue\fR ] +.PP +\fIDEL\-ENTRY\fR := { \fIipaddr\fR } +.PP +\fITEST\-ENTRY\fR := { \fIipaddr\fR } +.PP +For the \fBinet\fR family one can add or delete multiple entries by specifying +a range or a network: +.PP +\fIADD\-ENTRY\fR := { \fIipaddr\fR | \fIfromaddr\fR\-\fItoaddr\fR | \fIipaddr\fR/\fIcidr\fR } +.PP +\fIDEL\-ENTRY\fR := { \fIipaddr\fR | \fIfromaddr\fR\-\fItoaddr\fR | \fIipaddr\fR/\fIcidr\fR } +.PP +Optional \fBcreate\fR options: +.TP +\fBfamily\fR { \fBinet\fR|\fBinet6\fR } +The protocol family of the IP addresses to be stored in the set. The default is +\fBinet\fR, i.e IPv4. +.TP +\fBhashsize\fR \fIvalue\fR +The initial hash size for the set, default is 1024. The hash size must be a power +of two, the kernel automatically rounds up non power of two hash sizes to the first +correct value. +.TP +\fBmaxelem\fR \fIvalue\fR +The maximal number of elements which can be stored in the set, default 65536. +.TP +\fBnetmask\fP \fIcidr\fP +When the optional \fBnetmask\fP parameter specified, network addresses will be +stored in the set instead of IP host addresses. The \fIcidr\fP value must be +between 1\-32 for IPv4 and between 1\-128 for IPv6. An IP address will be in the set +if the network address, which is resulted by masking the address with the netmask +calculated from the cidr, can be found in the set. +.PP +Examples: +.IP +ipset create foo hash:ip netmask 24 +.IP +ipset add foo 192.168.1.1 +.IP +ipset test foo 192.168.1.2 +.SS hash:net +The \fBhash:net\fR set type uses a hash to store different sized of IP networks. +In order to avoid clashes in the hash a limited number of chaining, and then +if that is exhausted, the doubling of the hash is performed. +.PP +\fICREATE\-OPTIONS\fR := [ \fBfamily\fR { \fBinet\fR|\fBinet6\fR } ] | [ \fBhashsize\fR \fIvalue\fR ] [ \fBmaxelem\fR \fIvalue\fR ] [ \fBtimeout\fR \fIvalue\fR ] +.PP +\fIADD\-ENTRY\fR := { \fIipaddr\fR[/\fIcidr\fR] } +.PP +\fIADD\-OPTIONS\fR := [ \fBtimeout\fR \fIvalue\fR ] +.PP +\fIDEL\-ENTRY\fR := { \fIipaddr\fR[/\fIcidr\fR] } +.PP +\fITEST\-ENTRY\fR := { \fIipaddr\fR[/\fIcidr\fR] } +.PP +Optional \fBcreate\fR options: +.TP +\fBfamily\fR { \fBinet\fR|\fBinet6\fR } +The protocol family of the IP addresses to be stored in the set. The default is +\fBinet\fR, i.e IPv4. +.TP +\fBhashsize\fR \fIvalue\fR +The initial hash size for the set, default is 1024. The hash size must be a power +of two, the kernel automatically rounds up non power of two hash sizes to the first +correct value. +.TP +\fBmaxelem\fR \fIvalue\fR +The maximal number of elements which can be stored in the set, default 65536. +.PP +When adding/deleting/testing entries, if the cidr parameter is not specified, +then the host cidr value is assumed. +.PP +From the \fBset\fR netfilter match point of view an IP address will be in a \fBhash:net\fR type of set if it belongs to any of the netblocks added to the set. +The matching always start from the smallest size of netblock (most specific +cidr) to the largest ones (least specific cidr). When adding/deleting IP +addresses to the set by the \fBSET\fR netfilter target, it will be +added/deleted by the most specific cidr which can be found in the +set, or by the host cidr value if the set is empty. +.PP +The lookup time grows linearly with the number of the different \fIcidr\fR +values added to the set. +.PP +Examples: +.IP +ipset create foo hash:net +.IP +ipset add foo 192.168.0/24 +.IP +ipset add foo 10.1.0.0/16 +.IP +ipset test foo 192.168.0/24 +.SS hash:ip,port +The \fBhash:ip,port\fR set type uses a hash to store IP address and port pairs. +In order to avoid clashes in the hash a limited number of chaining, and then +if that is exhausted, the doubling of the hash is performed. +.PP +\fICREATE\-OPTIONS\fR := [ \fBfamily\fR { \fBinet\fR|\fBinet6\fR } ] | [ \fBhashsize\fR \fIvalue\fR ] [ \fBmaxelem\fR \fIvalue\fR ] [ \fBtimeout\fR \fIvalue\fR ] +.PP +\fIADD\-ENTRY\fR := { \fIipaddr\fR,\fIport\fR } +.PP +\fIADD\-OPTIONS\fR := [ \fBtimeout\fR \fIvalue\fR ] +.PP +\fIDEL\-ENTRY\fR := { \fIipaddr\fR,\fIport\fR } +.PP +\fITEST\-ENTRY\fR := { \fIipaddr\fR,\fIport\fR } +.PP +Optional \fBcreate\fR options: +.TP +\fBfamily\fR { \fBinet\fR|\fBinet6\fR } +The protocol family of the IP addresses to be stored in the set. The default is +\fBinet\fR, i.e IPv4. +.TP +\fBhashsize\fR \fIvalue\fR +The initial hash size for the set, default is 1024. The hash size must be a power +of two, the kernel automatically rounds up non power of two hash sizes to the first +correct value. +.TP +\fBmaxelem\fR \fIvalue\fR +The maximal number of elements which can be stored in the set, default 65536. +.PP +The \fBhash:ip,port\fR type of sets require two \fBsrc\fR/\fBdst\fR parameters of +the \fBset\fR match and \fBSET\fR target kernel modules. +.PP +Examples: +.IP +ipset create foo hash:ip,port +.IP +ipset add foo 192.168.1.1,80 +.IP +ipset test foo 192.168.1.1,80 +.SS hash:ip,port,ip +The \fBhash:ip,port,ip\fR set type uses a hash to store IP address, port and +IP address triples. In order to avoid clashes in the hash a limited number of +chaining, and then if that is exhausted, the doubling of the hash is performed. +.PP +\fICREATE\-OPTIONS\fR := [ \fBfamily\fR { \fBinet\fR|\fBinet6\fR } ] | [ \fBhashsize\fR \fIvalue\fR ] [ \fBmaxelem\fR \fIvalue\fR ] [ \fBtimeout\fR \fIvalue\fR ] +.PP +\fIADD\-ENTRY\fR := { \fIipaddr\fR,\fIport\fR,\fIipaddr\fR } +.PP +\fIADD\-OPTIONS\fR := [ \fBtimeout\fR \fIvalue\fR ] +.PP +\fIDEL\-ENTRY\fR := { \fIipaddr\fR,\fIport\fR,\fIipaddr\fR } +.PP +\fITEST\-ENTRY\fR := { \fIipaddr\fR,\fIport\fR,\fIipaddr\fR } +.PP +Optional \fBcreate\fR options: +.TP +\fBfamily\fR { \fBinet\fR|\fBinet6\fR } +The protocol family of the IP addresses to be stored in the set. The default is +\fBinet\fR, i.e IPv4. +.TP +\fBhashsize\fR \fIvalue\fR +The initial hash size for the set, default is 1024. The hash size must be a power +of two, the kernel automatically rounds up non power of two hash sizes to the first +correct value. +.TP +\fBmaxelem\fR \fIvalue\fR +The maximal number of elements which can be stored in the set, default 65536. +.PP +The \fBhash:ip,port,ip\fR type of sets require three \fBsrc\fR/\fBdst\fR parameters of +the \fBset\fR match and \fBSET\fR target kernel modules. +.PP +Examples: +.IP +ipset create foo hash:ip,port,ip +.IP +ipset add foo 192.168.1.1,80,10.0.0.1 +.IP +ipset test foo 192.168.1.1,80,10.0.0.1 +.SS hash:ip,port,net +The \fBhash:ip,port,net\fR set type uses a hash to store IP address, port and +IP network triples. +In order to avoid clashes in the hash a limited number of chaining, and then +if that is exhausted, the doubling of the hash is performed. +.PP +\fICREATE\-OPTIONS\fR := [ \fBfamily\fR { \fBinet\fR|\fBinet6\fR } ] | [ \fBhashsize\fR \fIvalue\fR ] [ \fBmaxelem\fR \fIvalue\fR ] [ \fBtimeout\fR \fIvalue\fR ] +.PP +\fIADD\-ENTRY\fR := { \fIipaddr\fR,\fIport\fR,\fIipaddr\fR[/\fIcidr\fR] } +.PP +\fIADD\-OPTIONS\fR := [ \fBtimeout\fR \fIvalue\fR ] +.PP +\fIDEL\-ENTRY\fR := { \fIipaddr\fR,\fIport\fR,\fIipaddr\fR[/\fIcidr\fR] } +.PP +\fITEST\-ENTRY\fR := { \fIipaddr\fR,\fIport\fR,\fIipaddr\fR[/\fIcidr\fR] } +.PP +Optional \fBcreate\fR options: +.TP +\fBfamily\fR { \fBinet\fR|\fBinet6\fR } +The protocol family of the IP addresses to be stored in the set. The default is +\fBinet\fR, i.e IPv4. +.TP +\fBhashsize\fR \fIvalue\fR +The initial hash size for the set, default is 1024. The hash size must be a power +of two, the kernel automatically rounds up non power of two hash sizes to the first +correct value. +.TP +\fBmaxelem\fR \fIvalue\fR +The maximal number of elements which can be stored in the set, default 65536. +.PP +When adding/deleting/testing entries, if the cidr parameter is not specified, +then the host cidr value is assumed. +.PP +From the \fBset\fR netfilter match point of view a triple will be in a \fBhash:ip,port,net\fR type of set (when the first IP and the port match) +if the second IP belongs to any of the netblocks added to the set. +The matching always start from the smallest size of netblock (most specific +cidr) to the largest ones (least specific cidr). When adding/deleting triples +to the set by the \fBSET\fR netfilter target, it will be +added/deleted by the most specific cidr which can be found in the +set, or by the host cidr value if the set is empty. +.PP +The lookup time grows linearly with the number of the different \fIcidr\fR +values added to the set. +.PP +The \fBhash:ip,port,net\fR type of sets require three \fBsrc\fR/\fBdst\fR parameters of +the \fBset\fR match and \fBSET\fR target kernel modules. +.PP +Examples: +.IP +ipset create foo hash:ip,port,net +.IP +ipset add foo 192.168.1,80,10.0.0/24 +.IP +ipset add foo 192.168.2,25,10.1.0.0/16 +.IP +ipset test foo 192.168.1,80.10.0.0/24 +.SS list:set +The \fBlist:set\fR type uses a simple list in which you can store +sets. +.PP +\fICREATE\-OPTIONS\fR := [ \fBsize\fR \fIvalue\fR ] [ \fBtimeout\fR \fIvalue\fR ] +.PP +\fIADD\-ENTRY\fR := \fIsetname\fR [ \fBbefore\fR|\fBafter\fR \fIsetname\fR ] +.PP +\fIADD\-OPTIONS\fR := [ \fBtimeout\fR \fIvalue\fR ] +.PP +\fIDEL\-ENTRY\fR := \fIsetname\fR [ \fBbefore\fR|\fBafter\fR \fIsetname\fR ] +.PP +\fITEST\-ENTRY\fR := \fIsetname\fR [ \fBbefore\fR|\fBafter\fR \fIsetname\fR ] +.PP +Optional \fBcreate\fR options: +.TP +\fBsize\fR \fIvalue\fR +The size of the list, the default is 8. +.PP +By the \fBipset\fR commad you can add, delete and test sets in a +\fBlist:set\fR type of set. +.PP +By the \fBset\fR match or \fBSET\fR target of netfiler +you can test, add or delete entries in the sets added to the \fBlist:set\fR +type of set. The match will try to find a matching entry in the sets and +the target will try to add an entry to the first set to which it can be added. +The number of src,dst options of the match and target are important: sets which +eats more src,dst parameters than specified are skipped, while sets with equal +or less parameters are checked, elements added. For example if \fIa\fR and +\fIb\fR are \fBlist:set\fR type of sets then in the command +.IP +iptables \-m set \-\-match\-set a src,dst \-j SET \-\-add\-set b src,dst +.PP +the match and target will skip any set in \fIa\fR and \fIb\fR +which stores data triples, but will check all sets with single or double +data storage in \fIa\fR set and add src to the first single or src,dst to the first double -data storage set in -\fIb\fP. -.P +data storage set in \fIb\fR. +.PP You can imagine a setlist type of set as an ordered union of the set elements. -.SH GENERAL RESTRICTIONS -Setnames starting with colon (:) cannot be defined. Zero valued set -entries cannot be used with hash type of sets. -.SH COMMENTS +.SH "GENERAL RESTRICTIONS" +Zero valued set entries cannot be used with hash methods. +.SH "COMMENTS" If you want to store same size subnets from a given network -(say /24 blocks from a /8 network), use the ipmap set type. +(say /24 blocks from a /8 network), use the \fBbitmap:ip\fR set type. If you want to store random same size networks (say random /24 blocks), -use the iphash set type. If you have got random size of netblocks, -use nethash. -.P -Old separator tokens (':' and '%") are still accepted. -.P -Binding support is removed. -.SH DIAGNOSTICS +use the \fBhash:ip\fR set type. If you have got random size of netblocks, +use \fBhash:net\fR. +.PP +Backward compatibility is maintained and old \fBipset\fR syntax is still supported. +.PP +The \fBiptree\fR and \fBiptreemap\fR set types are removed: if you refer to them, +they are automatically replaced by \fBhash:ip\fR type of sets. +.SH "DIAGNOSTICS" Various error messages are printed to standard error. The exit code -is 0 for correct functioning. Errors which appear to be caused by -invalid or abused command line parameters cause an exit code of 2, and -other errors cause an exit code of 1. -.SH BUGS -Bugs? No, just funny features. :-) +is 0 for correct functioning. +.SH "BUGS" +Bugs? No, just funny features. :\-) OK, just kidding... -.SH SEE ALSO -.BR iptables (8), -.SH AUTHORS +.SH "SEE ALSO" +\fBiptables\fR(8), +\fBip6tables\fR(8) +.SH "AUTHORS" Jozsef Kadlecsik wrote ipset, which is based on ippool by Joakim Axelsson, Patrick Schaaf and Martin Josefsson. -.P +.br Sven Wegener wrote the iptreemap type. -.SH LAST REMARK -.BR "I stand on the shoulders of giants." +.SH "LAST REMARK" +\fBI stand on the shoulders of giants.\fR diff --git a/src/ipset.c b/src/ipset.c index d29042d..69fcd09 100644 --- a/src/ipset.c +++ b/src/ipset.c @@ -15,11 +15,12 @@ #include +#include /* D() */ #include /* ipset_parse_* */ #include /* ipset_session_* */ #include /* struct ipset_type */ #include /* core options, commands */ -#include /* ipset_name_match */ +#include /* STREQ */ static char program_name[] = PACKAGE; static char program_version[] = PACKAGE_VERSION; @@ -31,6 +32,18 @@ static char cmdline[1024]; static char *newargv[255]; static int newargc = 0; +/* The known set types: (typename, revision, family) is unique */ +extern struct ipset_type ipset_bitmap_ip0; +extern struct ipset_type ipset_bitmap_ipmac0; +extern struct ipset_type ipset_bitmap_port0; +extern struct ipset_type ipset_hash_ip0; +extern struct ipset_type ipset_hash_net0; +extern struct ipset_type ipset_hash_ipport0; +extern struct ipset_type ipset_hash_ipportip0; +extern struct ipset_type ipset_hash_ipportnet0; +extern struct ipset_type ipset_tree_ip0; +extern struct ipset_type ipset_list_set0; + enum exittype { NO_PROBLEM = 0, OTHER_PROBLEM, @@ -38,7 +51,7 @@ enum exittype { VERSION_PROBLEM, }; -static void __attribute__((format(printf,2,3))) +static int __attribute__((format(printf,2,3))) exit_error(int status, const char *msg, ...) { bool quiet = !interactive @@ -52,7 +65,8 @@ exit_error(int status, const char *msg, ...) va_start(args, msg); vfprintf(stderr, msg, args); va_end(args); - fprintf(stderr, "\n"); + if (msg[strlen(msg) - 1] != '\n') + fprintf(stderr, "\n"); if (status == PARAMETER_PROBLEM) fprintf(stderr, @@ -63,7 +77,7 @@ exit_error(int status, const char *msg, ...) if (status && interactive) { if (session) ipset_session_report_reset(session); - return; + return -1; } if (session) @@ -71,6 +85,8 @@ exit_error(int status, const char *msg, ...) D("status: %u", status); exit(status); + /* Unreached */ + return -1; } static int @@ -81,8 +97,8 @@ handle_error(void) fprintf(stderr, "Warning: %s\n", ipset_session_warning(session)); if (ipset_session_error(session)) - exit_error(OTHER_PROBLEM, "%s", - ipset_session_error(session)); + return exit_error(OTHER_PROBLEM, "%s", + ipset_session_error(session)); if (!interactive) { ipset_session_fini(session); @@ -96,19 +112,15 @@ handle_error(void) static void help(void) { - enum ipset_cmd cmd; + const struct ipset_commands *c; const struct ipset_envopts *opt = ipset_envopts; printf("%s v%s\n\n" "Usage: %s [options] COMMAND\n\nCommands:\n", program_name, program_version, program_name); - for (cmd = IPSET_CMD_NONE + 1; cmd < IPSET_CMD_MAX; cmd++) { - if (!ipset_commands[cmd-1].name[0]) - continue; - printf("%s %s\n", - ipset_commands[cmd-1].name[0], - ipset_commands[cmd-1].help); + for (c = ipset_commands; c->cmd; c++) { + printf("%s %s\n", c->name[0], c->help); } printf("\nOptions:\n"); @@ -136,9 +148,11 @@ build_argv(char *buffer) while ((ptr = strtok(NULL, " \t\n")) != NULL) { if ((newargc + 1) < (int)(sizeof(newargv)/sizeof(char *))) newargv[newargc++] = ptr; - else + else { exit_error(PARAMETER_PROBLEM, "Line is too long to parse."); + return; + } } } @@ -188,57 +202,61 @@ restore(char *argv0) } static int -call_parser(int argc, char *argv[], const struct ipset_arg *args) +call_parser(int argc, char *argv[], const struct ipset_arg *args) { int i = 1, ret = 0; const struct ipset_arg *arg; + const char *optstr; - /* Currently CREATE and ADD may have got additional arguments */ + /* Currently CREATE and ADT may have got additional arguments */ if (!args) goto done; for (arg = args; arg->opt; arg++) { for (i = 1; i < argc; ) { - D("argc: %u, i: %u", argc, i); - if (!(ipset_name_match(argv[i], arg->name))) { + D("argc: %u, i: %u: %s vs %s", argc, i, argv[i], arg->name[0]); + if (!(ipset_match_option(argv[i], arg->name))) { i++; continue; } + optstr = argv[i]; /* Shift off matched option */ D("match %s", arg->name[0]); ipset_shift_argv(&argc, argv, i); D("argc: %u, i: %u", argc, i); switch (arg->has_arg) { case IPSET_MANDATORY_ARG: - if (i + 1 > argc) { - exit_error(PARAMETER_PROBLEM, - "Missing mandatory argument of option `%s'", - arg->name[0]); - return 1; - } + if (i + 1 > argc) + return exit_error(PARAMETER_PROBLEM, + "Missing mandatory argument of option `%s'", + arg->name[0]); /* Fall through */ case IPSET_OPTIONAL_ARG: if (i + 1 <= argc) { - ret = arg->parse(session, arg->opt, - argv[i]); + ret = ipset_call_parser(session, + arg->parse, + optstr, arg->opt, + argv[i]); if (ret < 0) return ret; ipset_shift_argv(&argc, argv, i); + break; } - break; + /* Fall through */ default: - ret = ipset_data_set(ipset_session_data(session), - arg->opt, arg->name[0]); + ret = ipset_call_parser(session, + arg->parse, + optstr, arg->opt, + optstr); if (ret < 0) return ret; } } } done: - if (i < argc) { - exit_error(PARAMETER_PROBLEM, "Unknown argument: `%s'", - argv[i]); - return 1; - } + if (i < argc) + return exit_error(PARAMETER_PROBLEM, + "Unknown argument: `%s'", + argv[i]); return ret; } @@ -256,12 +274,20 @@ check_mandatory(const struct ipset_type *type, int cmd) mandatory &= ~flags; if (!mandatory) return; + if (!arg) { + exit_error(OTHER_PROBLEM, + "There are missing mandatory flags but can't check them. " + "It's a bug, please report the problem."); + return; + } for (; arg->opt; arg++) - if (mandatory & IPSET_FLAG(arg->opt)) + if (mandatory & IPSET_FLAG(arg->opt)) { exit_error(PARAMETER_PROBLEM, "Mandatory option `%s' is missing", arg->name[0]); + return; + } } static const struct ipset_type * @@ -270,7 +296,7 @@ type_find(const char *name) const struct ipset_type *t = ipset_types(); while (t) { - if (STREQ(t->name, name) || STREQ(t->alias, name)) + if (ipset_match_typename(name, t)) return t; t = t->next; } @@ -299,7 +325,7 @@ parse_commandline(int argc, char *argv[]) { int ret = 0; enum ipset_cmd cmd = IPSET_CMD_NONE; - int i = 0, j; + int i; char *arg0 = NULL, *arg1 = NULL, *c; const struct ipset_envopts *opt; const struct ipset_commands *command; @@ -309,16 +335,16 @@ parse_commandline(int argc, char *argv[]) if (session == NULL) { session = ipset_session_init(printf); if (session == NULL) - exit_error(OTHER_PROBLEM, - "Cannot initialize ipset session, aborting."); + return exit_error(OTHER_PROBLEM, + "Cannot initialize ipset session, aborting."); } /* Commandline parsing, somewhat similar to that of 'ip' */ /* First: parse core options */ - for (opt = ipset_envopts; opt->flag ; opt++) { + for (opt = ipset_envopts; opt->flag; opt++) { for (i = 1; i < argc; ) { - if (!ipset_name_match(argv[i], opt->name)) { + if (!ipset_match_envopt(argv[i], opt->name)) { i++; continue; } @@ -327,9 +353,9 @@ parse_commandline(int argc, char *argv[]) switch (opt->has_arg) { case IPSET_MANDATORY_ARG: if (i + 1 > argc) - exit_error(PARAMETER_PROBLEM, - "Missing mandatory argument to option %s", - opt->name[0]); + return exit_error(PARAMETER_PROBLEM, + "Missing mandatory argument to option %s", + opt->name[0]); /* Fall through */ case IPSET_OPTIONAL_ARG: if (i + 1 <= argc) { @@ -340,53 +366,49 @@ parse_commandline(int argc, char *argv[]) ipset_shift_argv(&argc, argv, i); } break; - default: - ret = opt->parse(session, opt->flag, argv[i]); + case IPSET_NO_ARG: + ret = opt->parse(session, opt->flag, + opt->name[0]); if (ret < 0) return handle_error(); break; + default: + break; } } } /* Second: parse command */ - for (j = IPSET_CMD_NONE + 1; j < IPSET_CMD_MAX; j++) { - command = &ipset_commands[j - 1]; - if (!command->name[0]) - continue; + for (command = ipset_commands; + command->cmd && cmd == IPSET_CMD_NONE; + command++) { for (i = 1; i < argc; ) { - if (!ipset_name_match(argv[i], command->name)) { + if (!ipset_match_cmd(argv[1], command->name)) { i++; continue; } - if (cmd != IPSET_CMD_NONE) - exit_error(PARAMETER_PROBLEM, - "Commands `%s' and `%s'" - "cannot be specified together.", - ipset_commands[cmd - 1].name[0], - command->name[0]); if (restore_line != 0 - && (j == IPSET_CMD_RESTORE - || j == IPSET_CMD_VERSION - || j == IPSET_CMD_HELP)) - exit_error(PARAMETER_PROBLEM, - "Command `%s' is invalid in restore mode.", - command->name[0]); - if (interactive && j == IPSET_CMD_RESTORE) { - printf("Restore command ignored in interactive mode\n"); + && (command->cmd == IPSET_CMD_RESTORE + || command->cmd == IPSET_CMD_VERSION + || command->cmd == IPSET_CMD_HELP)) + return exit_error(PARAMETER_PROBLEM, + "Command `%s' is invalid in restore mode.", + command->name[0]); + if (interactive && command->cmd == IPSET_CMD_RESTORE) { + printf("Restore command ignored in interactive mode\n"); return 0; } /* Shift off matched command arg */ ipset_shift_argv(&argc, argv, i); - cmd = j; + cmd = command->cmd; switch (command->has_arg) { case IPSET_MANDATORY_ARG: case IPSET_MANDATORY_ARG2: if (i + 1 > argc) - exit_error(PARAMETER_PROBLEM, - "Missing mandatory argument to command %s", - command->name[0]); + return exit_error(PARAMETER_PROBLEM, + "Missing mandatory argument to command %s", + command->name[0]); /* Fall through */ case IPSET_OPTIONAL_ARG: arg0 = argv[i]; @@ -399,13 +421,14 @@ parse_commandline(int argc, char *argv[]) } if (command->has_arg == IPSET_MANDATORY_ARG2) { if (i + 1 > argc) - exit_error(PARAMETER_PROBLEM, - "Missing second mandatory argument to command %s", - command->name[0]); + return exit_error(PARAMETER_PROBLEM, + "Missing second mandatory argument to command %s", + command->name[0]); arg1 = argv[i]; /* Shift off second arg */ ipset_shift_argv(&argc, argv, i); } + break; } } @@ -433,14 +456,14 @@ parse_commandline(int argc, char *argv[]) parse_commandline(newargc, newargv); printf("%s> ", program_name); } - exit_error(NO_PROBLEM, NULL); + return exit_error(NO_PROBLEM, NULL); } - exit_error(PARAMETER_PROBLEM, "No command specified."); + return exit_error(PARAMETER_PROBLEM, "No command specified."); case IPSET_CMD_VERSION: printf("%s v%s.\n", program_name, program_version); if (interactive) return 0; - exit_error(NO_PROBLEM, NULL); + return exit_error(NO_PROBLEM, NULL); case IPSET_CMD_HELP: help(); @@ -450,8 +473,8 @@ parse_commandline(int argc, char *argv[]) /* Type-specific help, without kernel checking */ type = type_find(arg0); if (!type) - exit_error(PARAMETER_PROBLEM, - "Unknown settype: `%s'", arg0); + return exit_error(PARAMETER_PROBLEM, + "Unknown settype: `%s'", arg0); printf("\n%s type specific options:\n\n%s", type->name, type->usage); if (type->family == AF_UNSPEC) @@ -475,7 +498,9 @@ parse_commandline(int argc, char *argv[]) } if (interactive) return 0; - exit_error(NO_PROBLEM, NULL); + return exit_error(NO_PROBLEM, NULL); + case IPSET_CMD_QUIT: + return exit_error(NO_PROBLEM, NULL); default: break; } @@ -525,7 +550,7 @@ parse_commandline(int argc, char *argv[]) ret = ipset_parse_setname(session, IPSET_SETNAME, arg0); if (ret < 0) return handle_error(); - ret = ipset_parse_name(session, IPSET_OPT_SETNAME2, arg1); + ret = ipset_parse_setname(session, IPSET_OPT_SETNAME2, arg1); if (ret < 0) return handle_error(); break; @@ -582,5 +607,16 @@ parse_commandline(int argc, char *argv[]) int main(int argc, char *argv[]) { + /* Register types */ + ipset_type_add(&ipset_bitmap_ip0); + ipset_type_add(&ipset_bitmap_ipmac0); + ipset_type_add(&ipset_bitmap_port0); + ipset_type_add(&ipset_hash_ip0); + ipset_type_add(&ipset_hash_net0); + ipset_type_add(&ipset_hash_ipport0); + ipset_type_add(&ipset_hash_ipportip0); + ipset_type_add(&ipset_hash_ipportnet0); + ipset_type_add(&ipset_list_set0); + return parse_commandline(argc, argv); } diff --git a/src/ipset_bitmap_ip.c b/src/ipset_bitmap_ip.c index 471cc96..e28432b 100644 --- a/src/ipset_bitmap_ip.c +++ b/src/ipset_bitmap_ip.c @@ -11,28 +11,28 @@ /* Parse commandline arguments */ static const struct ipset_arg bitmap_ip_create_args[] = { - { .name = { "range", "--range", NULL }, + { .name = { "range", NULL }, .has_arg = IPSET_MANDATORY_ARG, .opt = IPSET_OPT_IP, .parse = ipset_parse_netrange, .print = ipset_print_ip, }, - { .name = { "netmask", "--netmask", NULL }, + { .name = { "netmask", NULL }, .has_arg = IPSET_MANDATORY_ARG, .opt = IPSET_OPT_NETMASK, .parse = ipset_parse_netmask, .print = ipset_print_number, }, - { .name = { "timeout", "--timeout", NULL }, + { .name = { "timeout", NULL }, .has_arg = IPSET_MANDATORY_ARG, .opt = IPSET_OPT_TIMEOUT, .parse = ipset_parse_uint32, .print = ipset_print_number, }, /* Backward compatibility */ - { .name = { "--from", NULL }, + { .name = { "from", NULL }, .has_arg = IPSET_MANDATORY_ARG, .opt = IPSET_OPT_IP, .parse = ipset_parse_single_ip, }, - { .name = { "--to", NULL }, + { .name = { "to", NULL }, .has_arg = IPSET_MANDATORY_ARG, .opt = IPSET_OPT_IP_TO, .parse = ipset_parse_single_ip, }, - { .name = { "--network", NULL }, + { .name = { "network", NULL }, .has_arg = IPSET_MANDATORY_ARG, .opt = IPSET_OPT_IP, .parse = ipset_parse_net, }, @@ -40,7 +40,7 @@ static const struct ipset_arg bitmap_ip_create_args[] = { }; static const struct ipset_arg bitmap_ip_add_args[] = { - { .name = { "timeout", "--timeout", NULL }, + { .name = { "timeout", NULL }, .has_arg = IPSET_MANDATORY_ARG, .opt = IPSET_OPT_TIMEOUT, .parse = ipset_parse_uint32, .print = ipset_print_number, }, @@ -56,7 +56,7 @@ static const char bitmap_ip_usage[] = struct ipset_type ipset_bitmap_ip0 = { .name = "bitmap:ip", - .alias = "ipmap", + .alias = { "ipmap", NULL }, .revision = 0, .family = AF_INET, .dimension = IPSET_DIM_ONE, diff --git a/src/ipset_bitmap_ipmac.c b/src/ipset_bitmap_ipmac.c index 4985fc4..382ebb5 100644 --- a/src/ipset_bitmap_ipmac.c +++ b/src/ipset_bitmap_ipmac.c @@ -11,24 +11,24 @@ /* Parse commandline arguments */ static const struct ipset_arg bitmap_ipmac_create_args[] = { - { .name = { "range", "--range", NULL }, + { .name = { "range", NULL }, .has_arg = IPSET_MANDATORY_ARG, .opt = IPSET_OPT_IP, .parse = ipset_parse_netrange, .print = ipset_print_ip, }, - { .name = { "timeout", "--timeout", NULL }, + { .name = { "timeout", NULL }, .has_arg = IPSET_MANDATORY_ARG, .opt = IPSET_OPT_TIMEOUT, .parse = ipset_parse_uint32, .print = ipset_print_number, }, /* Backward compatibility */ - { .name = { "--from", NULL }, + { .name = { "from", NULL }, .has_arg = IPSET_MANDATORY_ARG, .opt = IPSET_OPT_IP, .parse = ipset_parse_single_ip, }, - { .name = { "--to", NULL }, + { .name = { "to", NULL }, .has_arg = IPSET_MANDATORY_ARG, .opt = IPSET_OPT_IP_TO, .parse = ipset_parse_single_ip, }, - { .name = { "--network", NULL }, + { .name = { "network", NULL }, .has_arg = IPSET_MANDATORY_ARG, .opt = IPSET_OPT_IP, .parse = ipset_parse_net, }, @@ -36,7 +36,7 @@ static const struct ipset_arg bitmap_ipmac_create_args[] = { }; static const struct ipset_arg bitmap_ipmac_add_args[] = { - { .name = { "timeout", "--timeout", NULL }, + { .name = { "timeout", NULL }, .has_arg = IPSET_MANDATORY_ARG, .opt = IPSET_OPT_TIMEOUT, .parse = ipset_parse_uint32, .print = ipset_print_number, }, @@ -52,7 +52,7 @@ static const char bitmap_ipmac_usage[] = struct ipset_type ipset_bitmap_ipmac0 = { .name = "bitmap:ip,mac", - .alias = "macipmap", + .alias = { "macipmap", NULL }, .revision = 0, .family = AF_INET, .dimension = IPSET_DIM_TWO, diff --git a/src/ipset_bitmap_port.c b/src/ipset_bitmap_port.c index 11566ce..7871891 100644 --- a/src/ipset_bitmap_port.c +++ b/src/ipset_bitmap_port.c @@ -11,20 +11,20 @@ /* Parse commandline arguments */ static const struct ipset_arg bitmap_port_create_args[] = { - { .name = { "range", "--range", NULL }, + { .name = { "range", NULL }, .has_arg = IPSET_MANDATORY_ARG, .opt = IPSET_OPT_PORT, .parse = ipset_parse_port, .print = ipset_print_port, }, - { .name = { "timeout", "--timeout", NULL }, + { .name = { "timeout", NULL }, .has_arg = IPSET_MANDATORY_ARG, .opt = IPSET_OPT_TIMEOUT, .parse = ipset_parse_uint32, .print = ipset_print_number, }, /* Backward compatibility */ - { .name = { "--from", NULL }, + { .name = { "from", NULL }, .has_arg = IPSET_MANDATORY_ARG, .opt = IPSET_OPT_PORT, .parse = ipset_parse_single_port, }, - { .name = { "--to", NULL }, + { .name = { "to", NULL }, .has_arg = IPSET_MANDATORY_ARG, .opt = IPSET_OPT_PORT_TO, .parse = ipset_parse_single_port, }, @@ -32,7 +32,7 @@ static const struct ipset_arg bitmap_port_create_args[] = { }; static const struct ipset_arg bitmap_port_add_args[] = { - { .name = { "timeout", "--timeout", NULL }, + { .name = { "timeout", NULL }, .has_arg = IPSET_MANDATORY_ARG, .opt = IPSET_OPT_TIMEOUT, .parse = ipset_parse_uint32, .print = ipset_print_number, }, @@ -48,7 +48,7 @@ static const char bitmap_port_usage[] = struct ipset_type ipset_bitmap_port0 = { .name = "bitmap:port", - .alias = "portmap", + .alias = { "portmap", NULL }, .revision = 0, .family = AF_UNSPEC, .dimension = IPSET_DIM_ONE, diff --git a/src/ipset_hash_ip.c b/src/ipset_hash_ip.c index 212cb22..6609eea 100644 --- a/src/ipset_hash_ip.c +++ b/src/ipset_hash_ip.c @@ -11,35 +11,54 @@ /* Parse commandline arguments */ static const struct ipset_arg hash_ip_create_args[] = { - { .name = { "hashsize", "--hashsize", NULL }, + { .name = { "family", NULL }, + .has_arg = IPSET_MANDATORY_ARG, .opt = IPSET_OPT_FAMILY, + .parse = ipset_parse_family, .print = ipset_print_family, + }, + /* Alias: family inet */ + { .name = { "-4", NULL }, + .has_arg = IPSET_NO_ARG, .opt = IPSET_OPT_FAMILY, + .parse = ipset_parse_family, + }, + /* Alias: family inet6 */ + { .name = { "-6", NULL }, + .has_arg = IPSET_NO_ARG, .opt = IPSET_OPT_FAMILY, + .parse = ipset_parse_family, + }, + { .name = { "hashsize", NULL }, .has_arg = IPSET_MANDATORY_ARG, .opt = IPSET_OPT_HASHSIZE, .parse = ipset_parse_uint32, .print = ipset_print_number, }, - { .name = { "maxelem", "--maxleme", NULL }, + { .name = { "maxelem", NULL }, .has_arg = IPSET_MANDATORY_ARG, .opt = IPSET_OPT_MAXELEM, .parse = ipset_parse_uint32, .print = ipset_print_number, }, - { .name = { "probes", "--probes", NULL }, - .has_arg = IPSET_MANDATORY_ARG, .opt = IPSET_OPT_PROBES, - .parse = ipset_parse_uint8, .print = ipset_print_number, - }, - { .name = { "resize", "--resize", NULL }, - .has_arg = IPSET_MANDATORY_ARG, .opt = IPSET_OPT_RESIZE, - .parse = ipset_parse_uint8, .print = ipset_print_number, - }, - { .name = { "netmask", "--netmask", NULL }, + { .name = { "netmask", NULL }, .has_arg = IPSET_MANDATORY_ARG, .opt = IPSET_OPT_NETMASK, .parse = ipset_parse_netmask, .print = ipset_print_number, }, - { .name = { "timeout", "--timeout", NULL }, + { .name = { "timeout", NULL }, .has_arg = IPSET_MANDATORY_ARG, .opt = IPSET_OPT_TIMEOUT, .parse = ipset_parse_uint32, .print = ipset_print_number, }, + /* Ignored options: backward compatibilty */ + { .name = { "probes", NULL }, + .has_arg = IPSET_MANDATORY_ARG, .opt = IPSET_OPT_PROBES, + .parse = ipset_parse_ignored, .print = ipset_print_number, + }, + { .name = { "resize", NULL }, + .has_arg = IPSET_MANDATORY_ARG, .opt = IPSET_OPT_RESIZE, + .parse = ipset_parse_ignored, .print = ipset_print_number, + }, + { .name = { "gc", NULL }, + .has_arg = IPSET_MANDATORY_ARG, .opt = IPSET_OPT_GC, + .parse = ipset_parse_ignored, .print = ipset_print_number, + }, { }, }; static const struct ipset_arg hash_ip_add_args[] = { - { .name = { "timeout", "--timeout", NULL }, + { .name = { "timeout", NULL }, .has_arg = IPSET_MANDATORY_ARG, .opt = IPSET_OPT_TIMEOUT, .parse = ipset_parse_uint32, .print = ipset_print_number, }, @@ -48,8 +67,8 @@ static const struct ipset_arg hash_ip_add_args[] = { static const char hash_ip_usage[] = "create SETNAME hash:ip\n" +" [family inet|inet6]\n" " [hashsize VALUE] [maxelem VALUE]\n" -" [probes VALUE] [resize VALUE]\n" " [netmask CIDR] [timeout VALUE]\n" "add SETNAME IP|IP/CIDR|FROM-TO [timeout VALUE]\n" "del SETNAME IP|IP/CIDR|FROM-TO\n" @@ -57,7 +76,7 @@ static const char hash_ip_usage[] = struct ipset_type ipset_hash_ip0 = { .name = "hash:ip", - .alias = "iphash", + .alias = { "iphash", "iptree", "iptreemap", NULL }, .revision = 0, .family = AF_INET46, .dimension = IPSET_DIM_ONE, @@ -68,6 +87,7 @@ struct ipset_type ipset_hash_ip0 = { .opt = IPSET_OPT_IP }, }, + .compat_parse_elem = ipset_parse_iptimeout, .args = { [IPSET_CREATE] = hash_ip_create_args, [IPSET_ADD] = hash_ip_add_args, @@ -81,8 +101,6 @@ struct ipset_type ipset_hash_ip0 = { .full = { [IPSET_CREATE] = IPSET_FLAG(IPSET_OPT_HASHSIZE) | IPSET_FLAG(IPSET_OPT_MAXELEM) - | IPSET_FLAG(IPSET_OPT_PROBES) - | IPSET_FLAG(IPSET_OPT_RESIZE) | IPSET_FLAG(IPSET_OPT_NETMASK) | IPSET_FLAG(IPSET_OPT_TIMEOUT), [IPSET_ADD] = IPSET_FLAG(IPSET_OPT_IP) diff --git a/src/ipset_hash_ipport.c b/src/ipset_hash_ipport.c index a8cc7ad..4a9b8cf 100644 --- a/src/ipset_hash_ipport.c +++ b/src/ipset_hash_ipport.c @@ -11,48 +11,58 @@ /* Parse commandline arguments */ static const struct ipset_arg hash_ipport_create_args[] = { - { .name = { "range", "--range", NULL }, - .has_arg = IPSET_MANDATORY_ARG, .opt = IPSET_OPT_IP, - .parse = ipset_parse_netrange, .print = ipset_print_ip, + { .name = { "family", NULL }, + .has_arg = IPSET_MANDATORY_ARG, .opt = IPSET_OPT_FAMILY, + .parse = ipset_parse_family, .print = ipset_print_family, + }, + /* Alias: family inet */ + { .name = { "-4", NULL }, + .has_arg = IPSET_NO_ARG, .opt = IPSET_OPT_FAMILY, + .parse = ipset_parse_family, + }, + /* Alias: family inet6 */ + { .name = { "-6", NULL }, + .has_arg = IPSET_NO_ARG, .opt = IPSET_OPT_FAMILY, + .parse = ipset_parse_family, }, - { .name = { "hashsize", "--hashsize", NULL }, + { .name = { "hashsize", NULL }, .has_arg = IPSET_MANDATORY_ARG, .opt = IPSET_OPT_HASHSIZE, .parse = ipset_parse_uint32, .print = ipset_print_number, }, - { .name = { "maxelem", "--maxleme", NULL }, + { .name = { "maxelem", NULL }, .has_arg = IPSET_MANDATORY_ARG, .opt = IPSET_OPT_MAXELEM, .parse = ipset_parse_uint32, .print = ipset_print_number, }, - { .name = { "probes", "--probes", NULL }, - .has_arg = IPSET_MANDATORY_ARG, .opt = IPSET_OPT_PROBES, - .parse = ipset_parse_uint8, .print = ipset_print_number, - }, - { .name = { "resize", "--resize", NULL }, - .has_arg = IPSET_MANDATORY_ARG, .opt = IPSET_OPT_RESIZE, - .parse = ipset_parse_uint8, .print = ipset_print_number, - }, - { .name = { "timeout", "--timeout", NULL }, + { .name = { "timeout", NULL }, .has_arg = IPSET_MANDATORY_ARG, .opt = IPSET_OPT_TIMEOUT, .parse = ipset_parse_uint32, .print = ipset_print_number, }, /* Backward compatibility */ - { .name = { "--from", NULL }, + { .name = { "probes", NULL }, + .has_arg = IPSET_MANDATORY_ARG, .opt = IPSET_OPT_PROBES, + .parse = ipset_parse_ignored, .print = ipset_print_number, + }, + { .name = { "resize", NULL }, + .has_arg = IPSET_MANDATORY_ARG, .opt = IPSET_OPT_RESIZE, + .parse = ipset_parse_ignored, .print = ipset_print_number, + }, + { .name = { "from", NULL }, .has_arg = IPSET_MANDATORY_ARG, .opt = IPSET_OPT_IP, - .parse = ipset_parse_single_ip, + .parse = ipset_parse_ignored, }, - { .name = { "--to", NULL }, + { .name = { "to", NULL }, .has_arg = IPSET_MANDATORY_ARG, .opt = IPSET_OPT_IP_TO, - .parse = ipset_parse_single_ip, + .parse = ipset_parse_ignored, }, - { .name = { "--network", NULL }, + { .name = { "network", NULL }, .has_arg = IPSET_MANDATORY_ARG, .opt = IPSET_OPT_IP, - .parse = ipset_parse_net, + .parse = ipset_parse_ignored, }, { }, }; static const struct ipset_arg hash_ipport_add_args[] = { - { .name = { "timeout", "--timeout", NULL }, + { .name = { "timeout", NULL }, .has_arg = IPSET_MANDATORY_ARG, .opt = IPSET_OPT_TIMEOUT, .parse = ipset_parse_uint32, .print = ipset_print_number, }, @@ -60,10 +70,9 @@ static const struct ipset_arg hash_ipport_add_args[] = { }; static const char hash_ipport_usage[] = -"create SETNAME hash:ip,port range IP/CIDR|FROM-TO\n" +"create SETNAME hash:ip,port\n" " [family inet|inet6]\n" " [hashsize VALUE] [maxelem VALUE]\n" -" [probes VALUE] [resize VALUE]\n" " [timeout VALUE]\n" "add SETNAME IP,PORT [timeout VALUE]\n" "del SETNAME IP,PORT\n" @@ -71,7 +80,7 @@ static const char hash_ipport_usage[] = struct ipset_type ipset_hash_ipport0 = { .name = "hash:ip,port", - .alias = "ipporthash", + .alias = { "ipporthash", NULL }, .revision = 0, .family = AF_INET46, .dimension = IPSET_DIM_TWO, @@ -92,8 +101,7 @@ struct ipset_type ipset_hash_ipport0 = { [IPSET_ADD] = hash_ipport_add_args, }, .mandatory = { - [IPSET_CREATE] = IPSET_FLAG(IPSET_OPT_IP) - | IPSET_FLAG(IPSET_OPT_IP_TO), + [IPSET_CREATE] = 0, [IPSET_ADD] = IPSET_FLAG(IPSET_OPT_IP) | IPSET_FLAG(IPSET_OPT_PORT), [IPSET_DEL] = IPSET_FLAG(IPSET_OPT_IP) @@ -102,12 +110,8 @@ struct ipset_type ipset_hash_ipport0 = { | IPSET_FLAG(IPSET_OPT_PORT), }, .full = { - [IPSET_CREATE] = IPSET_FLAG(IPSET_OPT_IP) - | IPSET_FLAG(IPSET_OPT_IP_TO) - | IPSET_FLAG(IPSET_OPT_HASHSIZE) + [IPSET_CREATE] = IPSET_FLAG(IPSET_OPT_HASHSIZE) | IPSET_FLAG(IPSET_OPT_MAXELEM) - | IPSET_FLAG(IPSET_OPT_PROBES) - | IPSET_FLAG(IPSET_OPT_RESIZE) | IPSET_FLAG(IPSET_OPT_TIMEOUT), [IPSET_ADD] = IPSET_FLAG(IPSET_OPT_IP) | IPSET_FLAG(IPSET_OPT_PORT) diff --git a/src/ipset_hash_ipportip.c b/src/ipset_hash_ipportip.c index 71ed3c5..299e362 100644 --- a/src/ipset_hash_ipportip.c +++ b/src/ipset_hash_ipportip.c @@ -11,48 +11,58 @@ /* Parse commandline arguments */ static const struct ipset_arg hash_ipportip_create_args[] = { - { .name = { "range", "--range", NULL }, - .has_arg = IPSET_MANDATORY_ARG, .opt = IPSET_OPT_IP, - .parse = ipset_parse_netrange, .print = ipset_print_ip, + { .name = { "family", NULL }, + .has_arg = IPSET_MANDATORY_ARG, .opt = IPSET_OPT_FAMILY, + .parse = ipset_parse_family, .print = ipset_print_family, + }, + /* Alias: family inet */ + { .name = { "-4", NULL }, + .has_arg = IPSET_NO_ARG, .opt = IPSET_OPT_FAMILY, + .parse = ipset_parse_family, + }, + /* Alias: family inet6 */ + { .name = { "-6", NULL }, + .has_arg = IPSET_NO_ARG, .opt = IPSET_OPT_FAMILY, + .parse = ipset_parse_family, }, - { .name = { "hashsize", "--hashsize", NULL }, + { .name = { "hashsize", NULL }, .has_arg = IPSET_MANDATORY_ARG, .opt = IPSET_OPT_HASHSIZE, .parse = ipset_parse_uint32, .print = ipset_print_number, }, - { .name = { "maxelem", "--maxleme", NULL }, + { .name = { "maxelem", NULL }, .has_arg = IPSET_MANDATORY_ARG, .opt = IPSET_OPT_MAXELEM, .parse = ipset_parse_uint32, .print = ipset_print_number, }, - { .name = { "probes", "--probes", NULL }, - .has_arg = IPSET_MANDATORY_ARG, .opt = IPSET_OPT_PROBES, - .parse = ipset_parse_uint8, .print = ipset_print_number, - }, - { .name = { "resize", "--resize", NULL }, - .has_arg = IPSET_MANDATORY_ARG, .opt = IPSET_OPT_RESIZE, - .parse = ipset_parse_uint8, .print = ipset_print_number, - }, - { .name = { "timeout", "--timeout", NULL }, + { .name = { "timeout", NULL }, .has_arg = IPSET_MANDATORY_ARG, .opt = IPSET_OPT_TIMEOUT, .parse = ipset_parse_uint32, .print = ipset_print_number, }, /* Backward compatibility */ - { .name = { "--from", NULL }, + { .name = { "probes", NULL }, + .has_arg = IPSET_MANDATORY_ARG, .opt = IPSET_OPT_PROBES, + .parse = ipset_parse_ignored, .print = ipset_print_number, + }, + { .name = { "resize", NULL }, + .has_arg = IPSET_MANDATORY_ARG, .opt = IPSET_OPT_RESIZE, + .parse = ipset_parse_ignored, .print = ipset_print_number, + }, + { .name = { "from", NULL }, .has_arg = IPSET_MANDATORY_ARG, .opt = IPSET_OPT_IP, - .parse = ipset_parse_single_ip, + .parse = ipset_parse_ignored, }, - { .name = { "--to", NULL }, + { .name = { "to", NULL }, .has_arg = IPSET_MANDATORY_ARG, .opt = IPSET_OPT_IP_TO, - .parse = ipset_parse_single_ip, + .parse = ipset_parse_ignored, }, - { .name = { "--network", NULL }, + { .name = { "network", NULL }, .has_arg = IPSET_MANDATORY_ARG, .opt = IPSET_OPT_IP, - .parse = ipset_parse_net, + .parse = ipset_parse_ignored, }, { }, }; static const struct ipset_arg hash_ipportip_add_args[] = { - { .name = { "timeout", "--timeout", NULL }, + { .name = { "timeout", NULL }, .has_arg = IPSET_MANDATORY_ARG, .opt = IPSET_OPT_TIMEOUT, .parse = ipset_parse_uint32, .print = ipset_print_number, }, @@ -60,10 +70,9 @@ static const struct ipset_arg hash_ipportip_add_args[] = { }; static const char hash_ipportip_usage[] = -"create SETNAME hash:ip,port,ip range IP/CIDR|FROM-TO\n" +"create SETNAME hash:ip,port,ip\n" " [family inet|inet6]\n" " [hashsize VALUE] [maxelem VALUE]\n" -" [probes VALUE] [resize VALUE]\n" " [timeout VALUE]\n" "add SETNAME IP,PORT,IP [timeout VALUE]\n" "del SETNAME IP,PORT,IP\n" @@ -71,7 +80,7 @@ static const char hash_ipportip_usage[] = struct ipset_type ipset_hash_ipportip0 = { .name = "hash:ip,port,ip", - .alias = "ipportiphash", + .alias = { "ipportiphash", NULL }, .revision = 0, .family = AF_INET46, .dimension = IPSET_DIM_THREE, @@ -97,8 +106,7 @@ struct ipset_type ipset_hash_ipportip0 = { [IPSET_ADD] = hash_ipportip_add_args, }, .mandatory = { - [IPSET_CREATE] = IPSET_FLAG(IPSET_OPT_IP) - | IPSET_FLAG(IPSET_OPT_IP_TO), + [IPSET_CREATE] = 0, [IPSET_ADD] = IPSET_FLAG(IPSET_OPT_IP) | IPSET_FLAG(IPSET_OPT_PORT) | IPSET_FLAG(IPSET_OPT_IP2), @@ -110,12 +118,8 @@ struct ipset_type ipset_hash_ipportip0 = { | IPSET_FLAG(IPSET_OPT_IP2), }, .full = { - [IPSET_CREATE] = IPSET_FLAG(IPSET_OPT_IP) - | IPSET_FLAG(IPSET_OPT_IP_TO) - | IPSET_FLAG(IPSET_OPT_HASHSIZE) + [IPSET_CREATE] = IPSET_FLAG(IPSET_OPT_HASHSIZE) | IPSET_FLAG(IPSET_OPT_MAXELEM) - | IPSET_FLAG(IPSET_OPT_PROBES) - | IPSET_FLAG(IPSET_OPT_RESIZE) | IPSET_FLAG(IPSET_OPT_TIMEOUT), [IPSET_ADD] = IPSET_FLAG(IPSET_OPT_IP) | IPSET_FLAG(IPSET_OPT_PORT) diff --git a/src/ipset_hash_ipportnet.c b/src/ipset_hash_ipportnet.c index 769304d..13a0487 100644 --- a/src/ipset_hash_ipportnet.c +++ b/src/ipset_hash_ipportnet.c @@ -11,48 +11,58 @@ /* Parse commandline arguments */ static const struct ipset_arg hash_ipportnet_create_args[] = { - { .name = { "range", "--range", NULL }, - .has_arg = IPSET_MANDATORY_ARG, .opt = IPSET_OPT_IP, - .parse = ipset_parse_netrange, .print = ipset_print_ip, + { .name = { "family", NULL }, + .has_arg = IPSET_MANDATORY_ARG, .opt = IPSET_OPT_FAMILY, + .parse = ipset_parse_family, .print = ipset_print_family, + }, + /* Alias: family inet */ + { .name = { "-4", NULL }, + .has_arg = IPSET_NO_ARG, .opt = IPSET_OPT_FAMILY, + .parse = ipset_parse_family, + }, + /* Alias: family inet6 */ + { .name = { "-6", NULL }, + .has_arg = IPSET_NO_ARG, .opt = IPSET_OPT_FAMILY, + .parse = ipset_parse_family, }, - { .name = { "hashsize", "--hashsize", NULL }, + { .name = { "hashsize", NULL }, .has_arg = IPSET_MANDATORY_ARG, .opt = IPSET_OPT_HASHSIZE, .parse = ipset_parse_uint32, .print = ipset_print_number, }, - { .name = { "maxelem", "--maxleme", NULL }, + { .name = { "maxelem", NULL }, .has_arg = IPSET_MANDATORY_ARG, .opt = IPSET_OPT_MAXELEM, .parse = ipset_parse_uint32, .print = ipset_print_number, }, - { .name = { "probes", "--probes", NULL }, - .has_arg = IPSET_MANDATORY_ARG, .opt = IPSET_OPT_PROBES, - .parse = ipset_parse_uint8, .print = ipset_print_number, - }, - { .name = { "resize", "--resize", NULL }, - .has_arg = IPSET_MANDATORY_ARG, .opt = IPSET_OPT_RESIZE, - .parse = ipset_parse_uint8, .print = ipset_print_number, - }, - { .name = { "timeout", "--timeout", NULL }, + { .name = { "timeout", NULL }, .has_arg = IPSET_MANDATORY_ARG, .opt = IPSET_OPT_TIMEOUT, .parse = ipset_parse_uint32, .print = ipset_print_number, }, /* Backward compatibility */ - { .name = { "--from", NULL }, + { .name = { "probes", NULL }, + .has_arg = IPSET_MANDATORY_ARG, .opt = IPSET_OPT_PROBES, + .parse = ipset_parse_ignored, .print = ipset_print_number, + }, + { .name = { "resize", NULL }, + .has_arg = IPSET_MANDATORY_ARG, .opt = IPSET_OPT_RESIZE, + .parse = ipset_parse_ignored, .print = ipset_print_number, + }, + { .name = { "from", NULL }, .has_arg = IPSET_MANDATORY_ARG, .opt = IPSET_OPT_IP, - .parse = ipset_parse_single_ip, + .parse = ipset_parse_ignored, }, - { .name = { "--to", NULL }, + { .name = { "to", NULL }, .has_arg = IPSET_MANDATORY_ARG, .opt = IPSET_OPT_IP_TO, - .parse = ipset_parse_single_ip, + .parse = ipset_parse_ignored, }, - { .name = { "--network", NULL }, + { .name = { "network", NULL }, .has_arg = IPSET_MANDATORY_ARG, .opt = IPSET_OPT_IP, - .parse = ipset_parse_net, + .parse = ipset_parse_ignored, }, { }, }; static const struct ipset_arg hash_ipportnet_add_args[] = { - { .name = { "timeout", "--timeout", NULL }, + { .name = { "timeout", NULL }, .has_arg = IPSET_MANDATORY_ARG, .opt = IPSET_OPT_TIMEOUT, .parse = ipset_parse_uint32, .print = ipset_print_number, }, @@ -60,18 +70,17 @@ static const struct ipset_arg hash_ipportnet_add_args[] = { }; static const char hash_ipportnet_usage[] = -"create SETNAME hash:ip,port,net range IP/CIDR|FROM-TO\n" +"create SETNAME hash:ip,port,net\n" " [family inet|inet6]\n" " [hashsize VALUE] [maxelem VALUE]\n" -" [probes VALUE] [resize VALUE]\n" " [timeout VALUE]\n" -"add SETNAME IP,PORT,IP/CIDR [timeout VALUE]\n" -"del SETNAME IP,PORT,IP/CIDR\n" -"test SETNAME IP,PORT,IP/CIDR\n"; +"add SETNAME IP,PORT,IP[/CIDR] [timeout VALUE]\n" +"del SETNAME IP,PORT,IP[/CIDR]\n" +"test SETNAME IP,PORT,IP[/CIDR]\n"; struct ipset_type ipset_hash_ipportnet0 = { .name = "hash:ip,port,net", - .alias = "ipportnethash", + .alias = { "ipportnethash", NULL }, .revision = 0, .family = AF_INET46, .dimension = IPSET_DIM_THREE, @@ -87,7 +96,7 @@ struct ipset_type ipset_hash_ipportnet0 = { .opt = IPSET_OPT_PORT }, [IPSET_DIM_THREE] = { - .parse = ipset_parse_net, + .parse = ipset_parse_ipnet, .print = ipset_print_ip, .opt = IPSET_OPT_IP2 }, @@ -97,28 +106,20 @@ struct ipset_type ipset_hash_ipportnet0 = { [IPSET_ADD] = hash_ipportnet_add_args, }, .mandatory = { - [IPSET_CREATE] = IPSET_FLAG(IPSET_OPT_IP) - | IPSET_FLAG(IPSET_OPT_IP_TO), + [IPSET_CREATE] = 0, [IPSET_ADD] = IPSET_FLAG(IPSET_OPT_IP) | IPSET_FLAG(IPSET_OPT_PORT) - | IPSET_FLAG(IPSET_OPT_IP2) - | IPSET_FLAG(IPSET_OPT_CIDR2), + | IPSET_FLAG(IPSET_OPT_IP2), [IPSET_DEL] = IPSET_FLAG(IPSET_OPT_IP) | IPSET_FLAG(IPSET_OPT_PORT) - | IPSET_FLAG(IPSET_OPT_IP2) - | IPSET_FLAG(IPSET_OPT_CIDR2), + | IPSET_FLAG(IPSET_OPT_IP2), [IPSET_TEST] = IPSET_FLAG(IPSET_OPT_IP) | IPSET_FLAG(IPSET_OPT_PORT) - | IPSET_FLAG(IPSET_OPT_IP2) - | IPSET_FLAG(IPSET_OPT_CIDR2), + | IPSET_FLAG(IPSET_OPT_IP2), }, .full = { - [IPSET_CREATE] = IPSET_FLAG(IPSET_OPT_IP) - | IPSET_FLAG(IPSET_OPT_IP_TO) - | IPSET_FLAG(IPSET_OPT_HASHSIZE) + [IPSET_CREATE] = IPSET_FLAG(IPSET_OPT_HASHSIZE) | IPSET_FLAG(IPSET_OPT_MAXELEM) - | IPSET_FLAG(IPSET_OPT_PROBES) - | IPSET_FLAG(IPSET_OPT_RESIZE) | IPSET_FLAG(IPSET_OPT_TIMEOUT), [IPSET_ADD] = IPSET_FLAG(IPSET_OPT_IP) | IPSET_FLAG(IPSET_OPT_PORT) diff --git a/src/ipset_hash_net.c b/src/ipset_hash_net.c index 1d70900..c14652d 100644 --- a/src/ipset_hash_net.c +++ b/src/ipset_hash_net.c @@ -11,31 +11,46 @@ /* Parse commandline arguments */ static const struct ipset_arg hash_net_create_args[] = { - { .name = { "hashsize", "--hashsize", NULL }, + { .name = { "family", NULL }, + .has_arg = IPSET_MANDATORY_ARG, .opt = IPSET_OPT_FAMILY, + .parse = ipset_parse_family, .print = ipset_print_family, + }, + /* Alias: family inet */ + { .name = { "-4", NULL }, + .has_arg = IPSET_NO_ARG, .opt = IPSET_OPT_FAMILY, + .parse = ipset_parse_family, + }, + /* Alias: family inet6 */ + { .name = { "-6", NULL }, + .has_arg = IPSET_NO_ARG, .opt = IPSET_OPT_FAMILY, + .parse = ipset_parse_family, + }, + { .name = { "hashsize", NULL }, .has_arg = IPSET_MANDATORY_ARG, .opt = IPSET_OPT_HASHSIZE, .parse = ipset_parse_uint32, .print = ipset_print_number, }, - { .name = { "maxelem", "--maxleme", NULL }, + { .name = { "maxelem", NULL }, .has_arg = IPSET_MANDATORY_ARG, .opt = IPSET_OPT_MAXELEM, .parse = ipset_parse_uint32, .print = ipset_print_number, }, - { .name = { "probes", "--probes", NULL }, + { .name = { "timeout", NULL }, + .has_arg = IPSET_MANDATORY_ARG, .opt = IPSET_OPT_TIMEOUT, + .parse = ipset_parse_uint32, .print = ipset_print_number, + }, + /* Ignored options: backward compatibilty */ + { .name = { "probes", NULL }, .has_arg = IPSET_MANDATORY_ARG, .opt = IPSET_OPT_PROBES, - .parse = ipset_parse_uint8, .print = ipset_print_number, + .parse = ipset_parse_ignored, .print = ipset_print_number, }, - { .name = { "resize", "--resize", NULL }, + { .name = { "resize", NULL }, .has_arg = IPSET_MANDATORY_ARG, .opt = IPSET_OPT_RESIZE, - .parse = ipset_parse_uint8, .print = ipset_print_number, - }, - { .name = { "timeout", "--timeout", NULL }, - .has_arg = IPSET_MANDATORY_ARG, .opt = IPSET_OPT_TIMEOUT, - .parse = ipset_parse_uint32, .print = ipset_print_number, + .parse = ipset_parse_ignored, .print = ipset_print_number, }, { }, }; static const struct ipset_arg hash_net_add_args[] = { - { .name = { "timeout", "--timeout", NULL }, + { .name = { "timeout", NULL }, .has_arg = IPSET_MANDATORY_ARG, .opt = IPSET_OPT_TIMEOUT, .parse = ipset_parse_uint32, .print = ipset_print_number, }, @@ -46,21 +61,20 @@ static const char hash_net_usage[] = "create SETNAME hash:net\n" " [family inet|inet6]\n" " [hashsize VALUE] [maxelem VALUE]\n" -" [probes VALUE] [resize VALUE]\n" " [timeout VALUE]\n" -"add SETNAME IP/CIDR [timeout VALUE]\n" -"del SETNAME IP/CIDR\n" -"test SETNAME IP/CIDR\n"; +"add SETNAME IP[/CIDR] [timeout VALUE]\n" +"del SETNAME IP[/CIDR]\n" +"test SETNAME IP[/CIDR]\n"; struct ipset_type ipset_hash_net0 = { .name = "hash:net", - .alias = "nethash", + .alias = { "nethash", NULL }, .revision = 0, .family = AF_INET46, .dimension = IPSET_DIM_ONE, .elem = { [IPSET_DIM_ONE] = { - .parse = ipset_parse_net, + .parse = ipset_parse_ipnet, .print = ipset_print_ip, .opt = IPSET_OPT_IP }, @@ -71,18 +85,13 @@ struct ipset_type ipset_hash_net0 = { }, .mandatory = { [IPSET_CREATE] = 0, - [IPSET_ADD] = IPSET_FLAG(IPSET_OPT_IP) - | IPSET_FLAG(IPSET_OPT_CIDR), - [IPSET_DEL] = IPSET_FLAG(IPSET_OPT_IP) - | IPSET_FLAG(IPSET_OPT_CIDR), - [IPSET_TEST] = IPSET_FLAG(IPSET_OPT_IP) - | IPSET_FLAG(IPSET_OPT_CIDR), + [IPSET_ADD] = IPSET_FLAG(IPSET_OPT_IP), + [IPSET_DEL] = IPSET_FLAG(IPSET_OPT_IP), + [IPSET_TEST] = IPSET_FLAG(IPSET_OPT_IP), }, .full = { [IPSET_CREATE] = IPSET_FLAG(IPSET_OPT_HASHSIZE) | IPSET_FLAG(IPSET_OPT_MAXELEM) - | IPSET_FLAG(IPSET_OPT_PROBES) - | IPSET_FLAG(IPSET_OPT_RESIZE) | IPSET_FLAG(IPSET_OPT_TIMEOUT), [IPSET_ADD] = IPSET_FLAG(IPSET_OPT_IP) | IPSET_FLAG(IPSET_OPT_CIDR) diff --git a/src/ipset_iptreemap.c b/src/ipset_iptreemap.c deleted file mode 100644 index 22bdcb3..0000000 --- a/src/ipset_iptreemap.c +++ /dev/null @@ -1,208 +0,0 @@ -/* Copyright 2007 Sven Wegener - * - * This program is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License as published by the Free - * Software Foundation; either version 2 of the License, or (at your option) - * any later version. - * - * This program is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for - * more details. - * - * You should have received a copy of the GNU General Public License along with - * this program; if not, write to the Free Software Foundation, Inc., 59 Temple - * Place, Suite 330, Boston, MA 02111-1307 USA - */ - -#include /* UINT_MAX */ -#include /* *printf */ -#include /* mem* */ - -#include "ipset.h" - -#include - -#define OPT_CREATE_GC 0x1 - -static void -iptreemap_create_init(void *data) -{ - struct ip_set_req_iptreemap_create *mydata = data; - - mydata->gc_interval = 0; -} - -static int -iptreemap_create_parse(int c, char *argv[] UNUSED, void *data, - unsigned int *flags) -{ - struct ip_set_req_iptreemap_create *mydata = data; - - switch (c) { - case 'g': - string_to_number(optarg, 0, UINT_MAX, &mydata->gc_interval); - - *flags |= OPT_CREATE_GC; - break; - default: - return 0; - break; - } - - return 1; -} - -static void -iptreemap_create_final(void *data UNUSED, unsigned int flags UNUSED) -{ -} - -static const struct option create_opts[] = { - {.name = "gc", .has_arg = required_argument, .val = 'g'}, - {NULL}, -}; - -static ip_set_ip_t -iptreemap_adt_parser(int cmd UNUSED, const char *arg, void *data) -{ - struct ip_set_req_iptreemap *mydata = data; - ip_set_ip_t mask; - - char *saved = ipset_strdup(arg); - char *ptr, *tmp = saved; - - if (strchr(tmp, '/')) { - parse_ipandmask(tmp, &mydata->ip, &mask); - mydata->end = mydata->ip | ~mask; - } else { - if ((ptr = strchr(tmp, ':')) != NULL && ++warn_once == 1) - fprintf(stderr, "Warning: please use '-' separator token between IP range.\n" - "Next release won't support old separator token.\n"); - ptr = strsep(&tmp, "-:"); - parse_ip(ptr, &mydata->ip); - - if (tmp) { - parse_ip(tmp, &mydata->end); - } else { - mydata->end = mydata->ip; - } - } - - ipset_free(saved); - - return 1; -} - -static void -iptreemap_initheader(struct set *set, const void *data) -{ - const struct ip_set_req_iptreemap_create *header = data; - struct ip_set_iptreemap *map = set->settype->header; - - map->gc_interval = header->gc_interval; -} - -static void -iptreemap_printheader(struct set *set, unsigned int options UNUSED) -{ - struct ip_set_iptreemap *mysetdata = set->settype->header; - - if (mysetdata->gc_interval) - printf(" gc: %u", mysetdata->gc_interval); - - printf("\n"); -} - -static void -iptreemap_printips_sorted(struct set *set UNUSED, void *data, - u_int32_t len, unsigned int options, char dont_align) -{ - struct ip_set_req_iptreemap *req; - size_t offset = 0; - - while (len >= offset + sizeof(struct ip_set_req_iptreemap)) { - req = data + offset; - - printf("%s", ip_tostring(req->ip, options)); - if (req->ip != req->end) - printf("-%s", ip_tostring(req->end, options)); - printf("\n"); - - offset += IPSET_VALIGN(sizeof(struct ip_set_req_iptreemap), dont_align); - } -} - -static void -iptreemap_saveheader(struct set *set, unsigned int options UNUSED) -{ - struct ip_set_iptreemap *mysetdata = set->settype->header; - - printf("-N %s %s", set->name, set->settype->typename); - - if (mysetdata->gc_interval) - printf(" --gc %u", mysetdata->gc_interval); - - printf("\n"); -} - -static void -iptreemap_saveips(struct set *set UNUSED, void *data, - u_int32_t len, unsigned int options, char dont_align) -{ - struct ip_set_req_iptreemap *req; - size_t offset = 0; - - while (len >= offset + sizeof(struct ip_set_req_iptreemap)) { - req = data + offset; - - printf("-A %s %s", set->name, ip_tostring(req->ip, options)); - - if (req->ip != req->end) - printf("-%s", ip_tostring(req->end, options)); - - printf("\n"); - - offset += IPSET_VALIGN(sizeof(struct ip_set_req_iptreemap), dont_align); - } -} - -static void -iptreemap_usage(void) -{ - printf( - "-N set iptreemap --gc interval\n" - "-A set IP\n" - "-D set IP\n" - "-T set IP\n" - ); -} - -static struct settype settype_iptreemap = { - .typename = SETTYPE_NAME, - .protocol_version = IP_SET_PROTOCOL_VERSION, - - .create_size = sizeof(struct ip_set_req_iptreemap_create), - .create_init = iptreemap_create_init, - .create_parse = iptreemap_create_parse, - .create_final = iptreemap_create_final, - .create_opts = create_opts, - - .adt_size = sizeof(struct ip_set_req_iptreemap), - .adt_parser = iptreemap_adt_parser, - - .header_size = sizeof(struct ip_set_iptreemap), - .initheader = iptreemap_initheader, - .printheader = iptreemap_printheader, - .printips = iptreemap_printips_sorted, - .printips_sorted = iptreemap_printips_sorted, - .saveheader = iptreemap_saveheader, - .saveips = iptreemap_saveips, - - .usage = iptreemap_usage, -}; - -CONSTRUCTOR(iptreemap) -{ - settype_register(&settype_iptreemap); -} diff --git a/src/ipset_list_set.c b/src/ipset_list_set.c index 11b2523..76cf9b2 100644 --- a/src/ipset_list_set.c +++ b/src/ipset_list_set.c @@ -11,48 +11,59 @@ /* Parse commandline arguments */ static const struct ipset_arg list_set_create_args[] = { - { .name = { "size", "--size", NULL }, - .has_arg = IPSET_MANDATORY_ARG, .opt = IPSET_OPT_MAXELEM, - .parse = ipset_parse_uint32, .print = ipset_print_ip, + { .name = { "size", NULL }, + .has_arg = IPSET_MANDATORY_ARG, .opt = IPSET_OPT_SIZE, + .parse = ipset_parse_uint32, .print = ipset_print_number, }, - { .name = { "timeout", "--timeout", NULL }, + { .name = { "timeout", NULL }, .has_arg = IPSET_MANDATORY_ARG, .opt = IPSET_OPT_TIMEOUT, .parse = ipset_parse_uint32, .print = ipset_print_number, }, { }, }; -static const struct ipset_arg list_set_add_args[] = { - { .name = { "timeout", "--timeout", NULL }, +static const struct ipset_arg list_set_adt_args[] = { + { .name = { "timeout", NULL }, .has_arg = IPSET_MANDATORY_ARG, .opt = IPSET_OPT_TIMEOUT, .parse = ipset_parse_uint32, .print = ipset_print_number, }, + { .name = { "before", NULL }, + .has_arg = IPSET_MANDATORY_ARG, .opt = IPSET_OPT_NAMEREF, + .parse = ipset_parse_before, + }, + { .name = { "after", NULL }, + .has_arg = IPSET_MANDATORY_ARG, .opt = IPSET_OPT_NAMEREF, + .parse = ipset_parse_after, + }, { }, }; static const char list_set_usage[] = "create SETNAME list:set\n" " [size VALUE] [timeout VALUE]\n" -"add SETNAME NAME[,before|after,NAME] [timeout VALUE]\n" +"add SETNAME NAME [before|after NAME] [timeout VALUE]\n" "del SETNAME NAME\n" "test SETNAME NAME\n"; struct ipset_type ipset_list_set0 = { .name = "list:set", - .alias = "setlist", + .alias = { "setlist", NULL }, .revision = 0, .family = AF_UNSPEC, .dimension = IPSET_DIM_ONE, .elem = { [IPSET_DIM_ONE] = { - .parse = ipset_parse_name, + .parse = ipset_parse_setname, .print = ipset_print_name, .opt = IPSET_OPT_NAME }, }, + .compat_parse_elem = ipset_parse_name_compat, .args = { [IPSET_CREATE] = list_set_create_args, - [IPSET_ADD] = list_set_add_args, + [IPSET_ADD] = list_set_adt_args, + [IPSET_DEL] = list_set_adt_args, + [IPSET_TEST] = list_set_adt_args, }, .mandatory = { [IPSET_CREATE] = 0, diff --git a/src/ipset_tree_ip.c b/src/ipset_tree_ip.c deleted file mode 100644 index 7f7d842..0000000 --- a/src/ipset_tree_ip.c +++ /dev/null @@ -1,75 +0,0 @@ -/* Copyright 2007-2010 Jozsef Kadlecsik (kadlec@blackhole.kfki.hu) - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - */ -#include /* IPSET_OPT_* */ -#include /* parser functions */ -#include /* printing functions */ -#include /* prototypes */ - -/* Parse commandline arguments */ -static const struct ipset_arg tree_ip_create_args[] = { - { .name = { "gc", "--gc", NULL }, - .has_arg = IPSET_MANDATORY_ARG, .opt = IPSET_OPT_GC, - .parse = ipset_parse_uint32, .print = ipset_print_number, - }, - { .name = { "timeout", "--timeout", NULL }, - .has_arg = IPSET_MANDATORY_ARG, .opt = IPSET_OPT_TIMEOUT, - .parse = ipset_parse_uint32, .print = ipset_print_number, - }, - { }, -}; - -static const struct ipset_arg tree_ip_add_args[] = { - { .name = { "timeout", "--timeout", NULL }, - .has_arg = IPSET_MANDATORY_ARG, .opt = IPSET_OPT_TIMEOUT, - .parse = ipset_parse_uint32, .print = ipset_print_number, - }, - { }, -}; - -static const char tree_ip_usage[] = -"create SETNAME tree:ip\n" -" [gc VALUE] [timeout VALUE]\n" -"add SETNAME IP|IP/CIDR|FROM-TO [timeout VALUE]\n" -"del SETNAME IP|IP/CIDR|FROM-TO\n" -"test SETNAME IP\n"; - -struct ipset_type ipset_tree_ip0 = { - .name = "tree:ip", - .alias = "iptree", - .revision = 0, - .family = AF_INET, - .dimension = IPSET_DIM_ONE, - .elem = { - [IPSET_DIM_ONE] = { - .parse = ipset_parse_ip, - .print = ipset_print_ip, - .opt = IPSET_OPT_IP - }, - }, - .args = { - [IPSET_CREATE] = tree_ip_create_args, - [IPSET_ADD] = tree_ip_add_args, - }, - .mandatory = { - [IPSET_CREATE] = 0, - [IPSET_ADD] = IPSET_FLAG(IPSET_OPT_IP), - [IPSET_DEL] = IPSET_FLAG(IPSET_OPT_IP), - [IPSET_TEST] = IPSET_FLAG(IPSET_OPT_IP), - }, - .full = { - [IPSET_CREATE] = IPSET_FLAG(IPSET_OPT_GC) - | IPSET_FLAG(IPSET_OPT_TIMEOUT), - [IPSET_ADD] = IPSET_FLAG(IPSET_OPT_IP) - | IPSET_FLAG(IPSET_OPT_IP_TO) - | IPSET_FLAG(IPSET_OPT_TIMEOUT), - [IPSET_DEL] = IPSET_FLAG(IPSET_OPT_IP) - | IPSET_FLAG(IPSET_OPT_IP_TO), - [IPSET_TEST] = IPSET_FLAG(IPSET_OPT_IP), - }, - - .usage = tree_ip_usage, -}; diff --git a/src/ui.c b/src/ui.c index bc01e61..d8face5 100644 --- a/src/ui.c +++ b/src/ui.c @@ -4,107 +4,155 @@ * it under the terms of the GNU General Public License version 2 as * published by the Free Software Foundation. */ +#include /* assert */ +#include /* tolower */ +#include /* memcmp, str* */ + #include /* IPSET_CMD_* */ #include /* IPSET_*_ARG */ #include /* ipset_envopt_parse */ #include /* ipset_parse_family */ #include /* ipset_print_family */ +#include /* STREQ */ #include /* prototypes */ /* Commands and environment options */ const struct ipset_commands ipset_commands[] = { - [IPSET_CMD_CREATE - 1] = { - .name = { "create", "c", "-N", "--create", NULL }, + /* Order is important */ + + { /* c[reate], --create, n, -N */ + .cmd = IPSET_CMD_CREATE, + .name = { "create", "n" }, .has_arg = IPSET_MANDATORY_ARG2, .help = "SETNAME TYPENAME [type-specific-options]\n" " Create a new set", }, - [IPSET_CMD_DESTROY - 1] = { - .name = { "destroy", "x", "-X", "--destroy", NULL }, - .has_arg = IPSET_OPTIONAL_ARG, - .help = "[SETNAME]\n" - " Destroy a named set or all sets", - }, - [IPSET_CMD_FLUSH - 1] = { - .name = { "flush", "f", "-F", "--flush", NULL }, - .has_arg = IPSET_OPTIONAL_ARG, - .help = "[SETNAME]\n" - " Flush a named set or all sets", + { /* a[dd], --add, -A */ + .cmd = IPSET_CMD_ADD, + .name = { "add", NULL }, + .has_arg = IPSET_MANDATORY_ARG2, + .help = "SETNAME ENTRY\n" + " Add entry to the named set", }, - [IPSET_CMD_RENAME - 1] = { - .name = { "rename", "e", "-E", "--rename", NULL }, + { /* d[el], --del, -D */ + .cmd = IPSET_CMD_DEL, + .name = { "del", NULL }, .has_arg = IPSET_MANDATORY_ARG2, - .help = "FROM-SETNAME TO-SETNAME\n" - " Rename two sets", + .help = "SETNAME ENTRY\n" + " Delete entry from the named set", }, - [IPSET_CMD_SWAP - 1] = { - .name = { "swap", "w", "-W", "--swap", NULL }, + { /* t[est], --test, -T */ + .cmd = IPSET_CMD_TEST, + .name = { "test", NULL }, .has_arg = IPSET_MANDATORY_ARG2, - .help = "FROM-SETNAME TO-SETNAME\n" - " Swap the contect of two existing sets", + .help = "SETNAME ENTRY\n" + " Test entry in the named set", + }, + { /* des[troy], --destroy, x, -X */ + .cmd = IPSET_CMD_DESTROY, + .name = { "destroy", "x" }, + .has_arg = IPSET_OPTIONAL_ARG, + .help = "[SETNAME]\n" + " Destroy a named set or all sets", }, - [IPSET_CMD_LIST - 1] = { - .name = { "list", "l", "-L", "--list", NULL }, + { /* l[ist], --list, -L */ + .cmd = IPSET_CMD_LIST, + .name = { "list", NULL }, .has_arg = IPSET_OPTIONAL_ARG, .help = "[SETNAME]\n" " List the entries of a named set or all sets", }, - [IPSET_CMD_SAVE - 1] = { - .name = { "save", "s", "-S", "--save", NULL }, + { /* s[save], --save, -S */ + .cmd = IPSET_CMD_SAVE, + .name = { "save", NULL }, .has_arg = IPSET_OPTIONAL_ARG, .help = "[SETNAME]\n" " Save the named set or all sets to stdout", }, - [IPSET_CMD_ADD - 1] = { - .name = { "add", "a", "-A", "--add", NULL }, - .has_arg = IPSET_MANDATORY_ARG2, - .help = "SETNAME ENTRY\n" - " Add entry to a named set", + { /* r[estore], --restore, -R */ + .cmd = IPSET_CMD_RESTORE, + .name = { "restore", NULL }, + .has_arg = IPSET_NO_ARG, + .help = "\n" + " Restore a saved state", + }, + { /* f[lush], --flush, -F */ + .cmd = IPSET_CMD_FLUSH, + .name = { "flush", NULL }, + .has_arg = IPSET_OPTIONAL_ARG, + .help = "[SETNAME]\n" + " Flush a named set or all sets", }, - [IPSET_CMD_DEL - 1] = { - .name = { "del", "d", "-D", "--del", NULL }, + { /* ren[ame], --rename, e, -E */ + .cmd = IPSET_CMD_RENAME, + .name = { "rename", "e" }, .has_arg = IPSET_MANDATORY_ARG2, - .help = "SETNAME ENTRY\n" - " Delete entry from a named set", + .help = "FROM-SETNAME TO-SETNAME\n" + " Rename two sets", }, - [IPSET_CMD_TEST - 1] = { - .name = { "test", "t", "-T", "--test", NULL }, + { /* sw[ap], --swap, w, -W */ + .cmd = IPSET_CMD_SWAP, + .name = { "swap", "w" }, .has_arg = IPSET_MANDATORY_ARG2, - .help = "SETNAME ENTRY\n" - " Test if entry exists in the named set", + .help = "FROM-SETNAME TO-SETNAME\n" + " Swap the contect of two existing sets", }, - [IPSET_CMD_HELP - 1] = { - .name = { "help", "h", "-H", "-h", "--help", NULL }, + { /* h[elp, --help, -H */ + .cmd = IPSET_CMD_HELP, + .name = { "help", NULL }, .has_arg = IPSET_OPTIONAL_ARG, .help = "[TYPENAME]\n" " Print help, and settype specific help", }, - [IPSET_CMD_RESTORE - 1] = { - .name = { "restore", "r", "-R", "--restore", NULL }, + { /* v[ersion], --version, -V */ + .cmd = IPSET_CMD_VERSION, + .name = { "version", NULL }, .has_arg = IPSET_NO_ARG, .help = "\n" - " Restore a saved state", + " Print version information", }, - [IPSET_CMD_VERSION - 1] = { - .name = { "version", "v", "-V", "-v", "--version", NULL }, + { /* q[uit] */ + .cmd = IPSET_CMD_QUIT, + .name = { "quit", NULL }, .has_arg = IPSET_NO_ARG, .help = "\n" - " Print version information", + " Quit interactive mode", }, - [IPSET_CMD_MAX - 1] = { }, + { }, }; +/* Match a command: try to match as a prefix or letter-command */ +bool +ipset_match_cmd(const char *arg, const char * const name[]) +{ + size_t len; + + assert(arg); + assert(name && name[0]); + + /* Ignore (two) leading dashes */ + if (arg[0] == '-') + arg++; + if (arg[0] == '-') + arg++; + + len = strlen(arg); + + if (len > strlen(name[0]) || !len) + return false; + else if (memcmp(arg, name[0], len) == 0) + return true; + else if (len != 1) + return false; + else if (name[1] == NULL) + return tolower(arg[0]) == name[0][0]; + else + return tolower(arg[0]) == name[1][0]; +} + const struct ipset_envopts ipset_envopts[] = { - { .name = { "family", "--family", NULL }, - .has_arg = IPSET_MANDATORY_ARG, .flag = IPSET_OPT_FAMILY, - .parse = ipset_parse_family, .print = ipset_print_family, - .help = "inet|inet6\n" - " Specify family when creating a set\n" - " which supports multiple families.\n" - " The default family is INET.", - }, - { .name = { "-o", "--output", NULL }, + { .name = { "-o", "-output" }, .has_arg = IPSET_MANDATORY_ARG, .flag = IPSET_OPT_MAX, .parse = ipset_parse_output, .help = "plain|save|xml\n" @@ -112,25 +160,25 @@ const struct ipset_envopts ipset_envopts[] = { " Default value for \"list\" command is mode \"plain\"\n" " and for \"save\" command is mode \"save\".", }, - { .name = { "-s", "--sorted", NULL }, + { .name = { "-s", "-sorted" }, .parse = ipset_envopt_parse, .has_arg = IPSET_NO_ARG, .flag = IPSET_ENV_SORTED, .help = "\n" " Print elements sorted (if supported by the set type).", }, - { .name = { "-q", "--quiet", NULL }, + { .name = { "-q", "-quiet" }, .parse = ipset_envopt_parse, .has_arg = IPSET_NO_ARG, .flag = IPSET_ENV_QUIET, .help = "\n" " Suppress any notice or warning message.", }, - { .name = { "-r", "--resolve", NULL }, + { .name = { "-r", "-resolve" }, .parse = ipset_envopt_parse, .has_arg = IPSET_NO_ARG, .flag = IPSET_ENV_RESOLVE, .help = "\n" " Try to resolve IP addresses in the output (slow!)", }, - { .name = { "-x", "--exist", NULL }, + { .name = { "-!", "-exist" }, .parse = ipset_envopt_parse, .has_arg = IPSET_NO_ARG, .flag = IPSET_ENV_EXIST, .help = "\n" @@ -138,14 +186,59 @@ const struct ipset_envopts ipset_envopts[] = { " when adding already existing elements\n" " or when deleting non-existing elements.", }, - /* Aliases */ - { .name = { "-4", NULL }, - .has_arg = IPSET_NO_ARG, .flag = IPSET_OPT_FAMILY, - .parse = ipset_parse_family, - }, - { .name = { "-6", NULL }, - .has_arg = IPSET_NO_ARG, .flag = IPSET_OPT_FAMILY, - .parse = ipset_parse_family, - }, { }, }; + +/* Strict option matching */ +bool +ipset_match_option(const char *arg, const char * const name[]) +{ + assert(arg); + assert(name && name[0]); + + /* Skip two leading dashes */ + if (arg[0] == '-' && arg[1] == '-') + arg++, arg++; + + return STREQ(arg, name[0]) + || (name[1] != NULL && STREQ(arg, name[1])); +} + +/* Strict envopt matching */ +bool +ipset_match_envopt(const char *arg, const char * const name[]) +{ + assert(arg); + assert(name && name[0]); + + /* Skip one leading dash */ + if (arg[0] == '-' && arg[1] == '-') + arg++; + + return STREQ(arg, name[0]) + || (name[1] != NULL && STREQ(arg, name[1])); +} + +/** + * ipset_shift_argv - shift off an argument + * @arc: argument count + * @argv: array of argument strings + * @from: from where shift off an argument + * + * Shift off the argument at "from" from the array of + * arguments argv of size argc. + */ +void +ipset_shift_argv(int *argc, char *argv[], int from) +{ + int i; + + assert(*argc >= from + 1); + + for (i = from + 1; i <= *argc; i++) { + argv[i-1] = argv[i]; + } + (*argc)--; + return; +} + diff --git a/tests/Makefile b/tests/Makefile new file mode 100644 index 0000000..c9fcefc --- /dev/null +++ b/tests/Makefile @@ -0,0 +1,450 @@ +# Makefile.in generated by automake 1.10.1 from Makefile.am. +# tests/Makefile. Generated from Makefile.in by configure. + +# Copyright (C) 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002, +# 2003, 2004, 2005, 2006, 2007, 2008 Free Software Foundation, Inc. +# This Makefile.in is free software; the Free Software Foundation +# gives unlimited permission to copy and/or distribute it, +# with or without modifications, as long as this notice is preserved. + +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY, to the extent permitted by law; without +# even the implied warranty of MERCHANTABILITY or FITNESS FOR A +# PARTICULAR PURPOSE. + + + +pkgdatadir = $(datadir)/ipset +pkglibdir = $(libdir)/ipset +pkgincludedir = $(includedir)/ipset +am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd +install_sh_DATA = $(install_sh) -c -m 644 +install_sh_PROGRAM = $(install_sh) -c +install_sh_SCRIPT = $(install_sh) -c +INSTALL_HEADER = $(INSTALL_DATA) +transform = $(program_transform_name) +NORMAL_INSTALL = : +PRE_INSTALL = : +POST_INSTALL = : +NORMAL_UNINSTALL = : +PRE_UNINSTALL = : +POST_UNINSTALL = : +build_triplet = x86_64-unknown-linux-gnu +host_triplet = x86_64-unknown-linux-gnu +target_triplet = x86_64-unknown-linux-gnu +DIST_COMMON = $(srcdir)/Makefile.am $(srcdir)/Makefile.in \ + $(top_srcdir)/Make_global.am +subdir = tests +ACLOCAL_M4 = $(top_srcdir)/aclocal.m4 +am__aclocal_m4_deps = $(top_srcdir)/configure.ac +am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \ + $(ACLOCAL_M4) +mkinstalldirs = $(install_sh) -d +CONFIG_HEADER = $(top_builddir)/config.h +CONFIG_CLEAN_FILES = +SOURCES = +DIST_SOURCES = +DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST) +ACLOCAL = ${SHELL} /root/ipset/missing --run aclocal-1.10 +AMTAR = ${SHELL} /root/ipset/missing --run tar +AR = ar +AUTOCONF = ${SHELL} /root/ipset/missing --run autoconf +AUTOHEADER = ${SHELL} /root/ipset/missing --run autoheader +AUTOMAKE = ${SHELL} /root/ipset/missing --run automake-1.10 +AWK = mawk +CC = gcc +CCDEPMODE = depmode=gcc3 +CFLAGS = -g -O2 +CPP = gcc -E +CPPFLAGS = +CXX = g++ +CXXCPP = +CXXDEPMODE = depmode=none +CXXFLAGS = +CYGPATH_W = echo +DEFS = -DHAVE_CONFIG_H +DEPDIR = .deps +DSYMUTIL = +ECHO = echo +ECHO_C = +ECHO_N = -n +ECHO_T = +EGREP = /bin/grep -E +EXEEXT = +F77 = +FFLAGS = +GREP = /bin/grep +INSTALL = /usr/bin/install -c +INSTALL_DATA = ${INSTALL} -m 644 +INSTALL_PROGRAM = ${INSTALL} +INSTALL_SCRIPT = ${INSTALL} +INSTALL_STRIP_PROGRAM = $(install_sh) -c -s +LDFLAGS = +LIBOBJS = +LIBS = -lmnl +LIBTOOL = $(SHELL) $(top_builddir)/libtool +LN_S = ln -s +LTLIBOBJS = +MAKEINFO = ${SHELL} /root/ipset/missing --run makeinfo +MKDIR_P = /bin/mkdir -p +NMEDIT = +OBJEXT = o +PACKAGE = ipset +PACKAGE_BUGREPORT = kadlec@blackhole.kfki.hu +PACKAGE_NAME = ipset +PACKAGE_STRING = ipset 5.0 +PACKAGE_TARNAME = ipset +PACKAGE_VERSION = 5.0 +PATH_SEPARATOR = : +RANLIB = ranlib +SED = /bin/sed +SET_MAKE = +SHELL = /bin/bash +STRIP = strip +VERSION = 5.0 +abs_builddir = /root/ipset/tests +abs_srcdir = /root/ipset/tests +abs_top_builddir = /root/ipset +abs_top_srcdir = /root/ipset +ac_ct_CC = gcc +ac_ct_CXX = +ac_ct_F77 = +am__include = include +am__leading_dot = . +am__quote = +am__tar = ${AMTAR} chof - "$$tardir" +am__untar = ${AMTAR} xf - +bindir = ${exec_prefix}/bin +build = x86_64-unknown-linux-gnu +build_alias = +build_cpu = x86_64 +build_os = linux-gnu +build_vendor = unknown +builddir = . +datadir = ${datarootdir} +datarootdir = ${prefix}/share +docdir = ${datarootdir}/doc/${PACKAGE_TARNAME} +dvidir = ${docdir} +exec_prefix = ${prefix} +host = x86_64-unknown-linux-gnu +host_alias = +host_cpu = x86_64 +host_os = linux-gnu +host_vendor = unknown +htmldir = ${docdir} +includedir = ${prefix}/include +infodir = ${datarootdir}/info +install_sh = $(SHELL) /root/ipset/install-sh +libdir = ${exec_prefix}/lib +libexecdir = ${exec_prefix}/libexec +localedir = ${datarootdir}/locale +localstatedir = ${prefix}/var +mandir = ${datarootdir}/man +mkdir_p = /bin/mkdir -p +oldincludedir = /usr/include +pdfdir = ${docdir} +prefix = /usr/local +program_transform_name = s,x,x, +psdir = ${docdir} +sbindir = ${exec_prefix}/sbin +sharedstatedir = ${prefix}/com +srcdir = . +sysconfdir = ${prefix}/etc +target = x86_64-unknown-linux-gnu +target_alias = +target_cpu = x86_64 +target_os = linux-gnu +target_vendor = unknown +top_builddir = .. +top_srcdir = .. + +# This is _NOT_ the library release version, it's an API version. +# Please read Chapter 6 "Library interface versions" of the libtool +# documentation before making any modification +# http://sources.redhat.com/autobook/autobook/autobook_91.html +LIBVERSION = 1:0:0 +AM_CPPFLAGS = $(kinclude_CFLAGS) $(all_includes) -I$(top_srcdir)/include \ + -I/usr/local/include + +# -Wconversion \ -> false warnings +# -Wcast-qual \ -> false warnings +# -Wpointer-arith \ -> we need it +# -std=gnu99 + +# -std=gnu99 ULLONG_MAX requires +AM_CFLAGS = -std=gnu99 \ + -Wall \ + -Wextra \ + -Waggregate-return \ + -Wbad-function-cast \ + -Wcast-align \ + -Wfloat-equal \ + -Winit-self \ + -Winline \ + -Wmissing-declarations \ + -Wmissing-format-attribute \ + -Wmissing-prototypes \ + -Wnested-externs \ + -Wold-style-definition \ + -Wpacked \ + -Wredundant-decls \ + -Wshadow \ + -Wsign-compare \ + -Wstrict-prototypes \ + -Wswitch-default \ + -Wundef \ + -Wwrite-strings \ + -Wno-missing-field-initializers \ + -Werror \ + -g -ggdb + + +# fails with ntoh*: +# -Wunreachable-code +AM_VERBOSE_CC = @echo " CC " $@; +AM_VERBOSE_CCLD = @echo " CCLD " $@; +TESTS = ./runtest.sh +all: all-am + +.SUFFIXES: +$(srcdir)/Makefile.in: $(srcdir)/Makefile.am $(top_srcdir)/Make_global.am $(am__configure_deps) + @for dep in $?; do \ + case '$(am__configure_deps)' in \ + *$$dep*) \ + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh \ + && exit 0; \ + exit 1;; \ + esac; \ + done; \ + echo ' cd $(top_srcdir) && $(AUTOMAKE) --foreign tests/Makefile'; \ + cd $(top_srcdir) && \ + $(AUTOMAKE) --foreign tests/Makefile +.PRECIOUS: Makefile +Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status + @case '$?' in \ + *config.status*) \ + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \ + *) \ + echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe)'; \ + cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe);; \ + esac; + +$(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh + +$(top_srcdir)/configure: $(am__configure_deps) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh +$(ACLOCAL_M4): $(am__aclocal_m4_deps) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh + +mostlyclean-libtool: + -rm -f *.lo + +clean-libtool: + -rm -rf .libs _libs +tags: TAGS +TAGS: + +ctags: CTAGS +CTAGS: + + +check-TESTS: $(TESTS) + @failed=0; all=0; xfail=0; xpass=0; skip=0; ws='[ ]'; \ + srcdir=$(srcdir); export srcdir; \ + list=' $(TESTS) '; \ + if test -n "$$list"; then \ + for tst in $$list; do \ + if test -f ./$$tst; then dir=./; \ + elif test -f $$tst; then dir=; \ + else dir="$(srcdir)/"; fi; \ + if $(TESTS_ENVIRONMENT) $${dir}$$tst; then \ + all=`expr $$all + 1`; \ + case " $(XFAIL_TESTS) " in \ + *$$ws$$tst$$ws*) \ + xpass=`expr $$xpass + 1`; \ + failed=`expr $$failed + 1`; \ + echo "XPASS: $$tst"; \ + ;; \ + *) \ + echo "PASS: $$tst"; \ + ;; \ + esac; \ + elif test $$? -ne 77; then \ + all=`expr $$all + 1`; \ + case " $(XFAIL_TESTS) " in \ + *$$ws$$tst$$ws*) \ + xfail=`expr $$xfail + 1`; \ + echo "XFAIL: $$tst"; \ + ;; \ + *) \ + failed=`expr $$failed + 1`; \ + echo "FAIL: $$tst"; \ + ;; \ + esac; \ + else \ + skip=`expr $$skip + 1`; \ + echo "SKIP: $$tst"; \ + fi; \ + done; \ + if test "$$failed" -eq 0; then \ + if test "$$xfail" -eq 0; then \ + banner="All $$all tests passed"; \ + else \ + banner="All $$all tests behaved as expected ($$xfail expected failures)"; \ + fi; \ + else \ + if test "$$xpass" -eq 0; then \ + banner="$$failed of $$all tests failed"; \ + else \ + banner="$$failed of $$all tests did not behave as expected ($$xpass unexpected passes)"; \ + fi; \ + fi; \ + dashes="$$banner"; \ + skipped=""; \ + if test "$$skip" -ne 0; then \ + skipped="($$skip tests were not run)"; \ + test `echo "$$skipped" | wc -c` -le `echo "$$banner" | wc -c` || \ + dashes="$$skipped"; \ + fi; \ + report=""; \ + if test "$$failed" -ne 0 && test -n "$(PACKAGE_BUGREPORT)"; then \ + report="Please report to $(PACKAGE_BUGREPORT)"; \ + test `echo "$$report" | wc -c` -le `echo "$$banner" | wc -c` || \ + dashes="$$report"; \ + fi; \ + dashes=`echo "$$dashes" | sed s/./=/g`; \ + echo "$$dashes"; \ + echo "$$banner"; \ + test -z "$$skipped" || echo "$$skipped"; \ + test -z "$$report" || echo "$$report"; \ + echo "$$dashes"; \ + test "$$failed" -eq 0; \ + else :; fi + +distdir: $(DISTFILES) + @srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ + topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ + list='$(DISTFILES)'; \ + dist_files=`for file in $$list; do echo $$file; done | \ + sed -e "s|^$$srcdirstrip/||;t" \ + -e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \ + case $$dist_files in \ + */*) $(MKDIR_P) `echo "$$dist_files" | \ + sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \ + sort -u` ;; \ + esac; \ + for file in $$dist_files; do \ + if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \ + if test -d $$d/$$file; then \ + dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \ + if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \ + cp -pR $(srcdir)/$$file $(distdir)$$dir || exit 1; \ + fi; \ + cp -pR $$d/$$file $(distdir)$$dir || exit 1; \ + else \ + test -f $(distdir)/$$file \ + || cp -p $$d/$$file $(distdir)/$$file \ + || exit 1; \ + fi; \ + done +check-am: all-am + $(MAKE) $(AM_MAKEFLAGS) check-TESTS +check: check-am +all-am: Makefile +installdirs: +install: install-am +install-exec: install-exec-am +install-data: install-data-am +uninstall: uninstall-am + +install-am: all-am + @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am + +installcheck: installcheck-am +install-strip: + $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ + install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ + `test -z '$(STRIP)' || \ + echo "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'"` install +mostlyclean-generic: + +clean-generic: + +distclean-generic: + -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES) + +maintainer-clean-generic: + @echo "This command is intended for maintainers to use" + @echo "it deletes files that may require special tools to rebuild." +clean: clean-am + +clean-am: clean-generic clean-libtool mostlyclean-am + +distclean: distclean-am + -rm -f Makefile +distclean-am: clean-am distclean-generic + +dvi: dvi-am + +dvi-am: + +html: html-am + +info: info-am + +info-am: + +install-data-am: + +install-dvi: install-dvi-am + +install-exec-am: + +install-html: install-html-am + +install-info: install-info-am + +install-man: + +install-pdf: install-pdf-am + +install-ps: install-ps-am + +installcheck-am: + +maintainer-clean: maintainer-clean-am + -rm -f Makefile +maintainer-clean-am: distclean-am maintainer-clean-generic + +mostlyclean: mostlyclean-am + +mostlyclean-am: mostlyclean-generic mostlyclean-libtool + +pdf: pdf-am + +pdf-am: + +ps: ps-am + +ps-am: + +uninstall-am: + +.MAKE: install-am install-strip + +.PHONY: all all-am check check-TESTS check-am clean clean-generic \ + clean-libtool distclean distclean-generic distclean-libtool \ + distdir dvi dvi-am html html-am info info-am install \ + install-am install-data install-data-am install-dvi \ + install-dvi-am install-exec install-exec-am install-html \ + install-html-am install-info install-info-am install-man \ + install-pdf install-pdf-am install-ps install-ps-am \ + install-strip installcheck installcheck-am installdirs \ + maintainer-clean maintainer-clean-generic mostlyclean \ + mostlyclean-generic mostlyclean-libtool pdf pdf-am ps ps-am \ + uninstall uninstall-am + +# Tell versions [3.59,3.63) of GNU make to not export all variables. +# Otherwise a system limit (for SysV at least) may be exceeded. +.NOEXPORT: diff --git a/tests/Makefile.am b/tests/Makefile.am new file mode 100644 index 0000000..9b62557 --- /dev/null +++ b/tests/Makefile.am @@ -0,0 +1,5 @@ +## Process this file with automake to produce Makefile.in + +include $(top_srcdir)/Make_global.am + +TESTS = ./runtest.sh diff --git a/tests/Makefile.in b/tests/Makefile.in new file mode 100644 index 0000000..5c47bc5 --- /dev/null +++ b/tests/Makefile.in @@ -0,0 +1,450 @@ +# Makefile.in generated by automake 1.10.1 from Makefile.am. +# @configure_input@ + +# Copyright (C) 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002, +# 2003, 2004, 2005, 2006, 2007, 2008 Free Software Foundation, Inc. +# This Makefile.in is free software; the Free Software Foundation +# gives unlimited permission to copy and/or distribute it, +# with or without modifications, as long as this notice is preserved. + +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY, to the extent permitted by law; without +# even the implied warranty of MERCHANTABILITY or FITNESS FOR A +# PARTICULAR PURPOSE. + +@SET_MAKE@ +VPATH = @srcdir@ +pkgdatadir = $(datadir)/@PACKAGE@ +pkglibdir = $(libdir)/@PACKAGE@ +pkgincludedir = $(includedir)/@PACKAGE@ +am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd +install_sh_DATA = $(install_sh) -c -m 644 +install_sh_PROGRAM = $(install_sh) -c +install_sh_SCRIPT = $(install_sh) -c +INSTALL_HEADER = $(INSTALL_DATA) +transform = $(program_transform_name) +NORMAL_INSTALL = : +PRE_INSTALL = : +POST_INSTALL = : +NORMAL_UNINSTALL = : +PRE_UNINSTALL = : +POST_UNINSTALL = : +build_triplet = @build@ +host_triplet = @host@ +target_triplet = @target@ +DIST_COMMON = $(srcdir)/Makefile.am $(srcdir)/Makefile.in \ + $(top_srcdir)/Make_global.am +subdir = tests +ACLOCAL_M4 = $(top_srcdir)/aclocal.m4 +am__aclocal_m4_deps = $(top_srcdir)/configure.ac +am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \ + $(ACLOCAL_M4) +mkinstalldirs = $(install_sh) -d +CONFIG_HEADER = $(top_builddir)/config.h +CONFIG_CLEAN_FILES = +SOURCES = +DIST_SOURCES = +DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST) +ACLOCAL = @ACLOCAL@ +AMTAR = @AMTAR@ +AR = @AR@ +AUTOCONF = @AUTOCONF@ +AUTOHEADER = @AUTOHEADER@ +AUTOMAKE = @AUTOMAKE@ +AWK = @AWK@ +CC = @CC@ +CCDEPMODE = @CCDEPMODE@ +CFLAGS = @CFLAGS@ +CPP = @CPP@ +CPPFLAGS = @CPPFLAGS@ +CXX = @CXX@ +CXXCPP = @CXXCPP@ +CXXDEPMODE = @CXXDEPMODE@ +CXXFLAGS = @CXXFLAGS@ +CYGPATH_W = @CYGPATH_W@ +DEFS = @DEFS@ +DEPDIR = @DEPDIR@ +DSYMUTIL = @DSYMUTIL@ +ECHO = @ECHO@ +ECHO_C = @ECHO_C@ +ECHO_N = @ECHO_N@ +ECHO_T = @ECHO_T@ +EGREP = @EGREP@ +EXEEXT = @EXEEXT@ +F77 = @F77@ +FFLAGS = @FFLAGS@ +GREP = @GREP@ +INSTALL = @INSTALL@ +INSTALL_DATA = @INSTALL_DATA@ +INSTALL_PROGRAM = @INSTALL_PROGRAM@ +INSTALL_SCRIPT = @INSTALL_SCRIPT@ +INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@ +LDFLAGS = @LDFLAGS@ +LIBOBJS = @LIBOBJS@ +LIBS = @LIBS@ +LIBTOOL = @LIBTOOL@ +LN_S = @LN_S@ +LTLIBOBJS = @LTLIBOBJS@ +MAKEINFO = @MAKEINFO@ +MKDIR_P = @MKDIR_P@ +NMEDIT = @NMEDIT@ +OBJEXT = @OBJEXT@ +PACKAGE = @PACKAGE@ +PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@ +PACKAGE_NAME = @PACKAGE_NAME@ +PACKAGE_STRING = @PACKAGE_STRING@ +PACKAGE_TARNAME = @PACKAGE_TARNAME@ +PACKAGE_VERSION = @PACKAGE_VERSION@ +PATH_SEPARATOR = @PATH_SEPARATOR@ +RANLIB = @RANLIB@ +SED = @SED@ +SET_MAKE = @SET_MAKE@ +SHELL = @SHELL@ +STRIP = @STRIP@ +VERSION = @VERSION@ +abs_builddir = @abs_builddir@ +abs_srcdir = @abs_srcdir@ +abs_top_builddir = @abs_top_builddir@ +abs_top_srcdir = @abs_top_srcdir@ +ac_ct_CC = @ac_ct_CC@ +ac_ct_CXX = @ac_ct_CXX@ +ac_ct_F77 = @ac_ct_F77@ +am__include = @am__include@ +am__leading_dot = @am__leading_dot@ +am__quote = @am__quote@ +am__tar = @am__tar@ +am__untar = @am__untar@ +bindir = @bindir@ +build = @build@ +build_alias = @build_alias@ +build_cpu = @build_cpu@ +build_os = @build_os@ +build_vendor = @build_vendor@ +builddir = @builddir@ +datadir = @datadir@ +datarootdir = @datarootdir@ +docdir = @docdir@ +dvidir = @dvidir@ +exec_prefix = @exec_prefix@ +host = @host@ +host_alias = @host_alias@ +host_cpu = @host_cpu@ +host_os = @host_os@ +host_vendor = @host_vendor@ +htmldir = @htmldir@ +includedir = @includedir@ +infodir = @infodir@ +install_sh = @install_sh@ +libdir = @libdir@ +libexecdir = @libexecdir@ +localedir = @localedir@ +localstatedir = @localstatedir@ +mandir = @mandir@ +mkdir_p = @mkdir_p@ +oldincludedir = @oldincludedir@ +pdfdir = @pdfdir@ +prefix = @prefix@ +program_transform_name = @program_transform_name@ +psdir = @psdir@ +sbindir = @sbindir@ +sharedstatedir = @sharedstatedir@ +srcdir = @srcdir@ +sysconfdir = @sysconfdir@ +target = @target@ +target_alias = @target_alias@ +target_cpu = @target_cpu@ +target_os = @target_os@ +target_vendor = @target_vendor@ +top_builddir = @top_builddir@ +top_srcdir = @top_srcdir@ + +# This is _NOT_ the library release version, it's an API version. +# Please read Chapter 6 "Library interface versions" of the libtool +# documentation before making any modification +# http://sources.redhat.com/autobook/autobook/autobook_91.html +LIBVERSION = 1:0:0 +AM_CPPFLAGS = $(kinclude_CFLAGS) $(all_includes) -I$(top_srcdir)/include \ + -I/usr/local/include + +# -Wconversion \ -> false warnings +# -Wcast-qual \ -> false warnings +# -Wpointer-arith \ -> we need it +# -std=gnu99 + +# -std=gnu99 ULLONG_MAX requires +AM_CFLAGS = -std=gnu99 \ + -Wall \ + -Wextra \ + -Waggregate-return \ + -Wbad-function-cast \ + -Wcast-align \ + -Wfloat-equal \ + -Winit-self \ + -Winline \ + -Wmissing-declarations \ + -Wmissing-format-attribute \ + -Wmissing-prototypes \ + -Wnested-externs \ + -Wold-style-definition \ + -Wpacked \ + -Wredundant-decls \ + -Wshadow \ + -Wsign-compare \ + -Wstrict-prototypes \ + -Wswitch-default \ + -Wundef \ + -Wwrite-strings \ + -Wno-missing-field-initializers \ + -Werror \ + -g -ggdb + + +# fails with ntoh*: +# -Wunreachable-code +@VERBOSE_FALSE@AM_VERBOSE_CC = @echo " CC " $@; +@VERBOSE_FALSE@AM_VERBOSE_CCLD = @echo " CCLD " $@; +TESTS = ./runtest.sh +all: all-am + +.SUFFIXES: +$(srcdir)/Makefile.in: $(srcdir)/Makefile.am $(top_srcdir)/Make_global.am $(am__configure_deps) + @for dep in $?; do \ + case '$(am__configure_deps)' in \ + *$$dep*) \ + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh \ + && exit 0; \ + exit 1;; \ + esac; \ + done; \ + echo ' cd $(top_srcdir) && $(AUTOMAKE) --foreign tests/Makefile'; \ + cd $(top_srcdir) && \ + $(AUTOMAKE) --foreign tests/Makefile +.PRECIOUS: Makefile +Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status + @case '$?' in \ + *config.status*) \ + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \ + *) \ + echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe)'; \ + cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe);; \ + esac; + +$(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh + +$(top_srcdir)/configure: $(am__configure_deps) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh +$(ACLOCAL_M4): $(am__aclocal_m4_deps) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh + +mostlyclean-libtool: + -rm -f *.lo + +clean-libtool: + -rm -rf .libs _libs +tags: TAGS +TAGS: + +ctags: CTAGS +CTAGS: + + +check-TESTS: $(TESTS) + @failed=0; all=0; xfail=0; xpass=0; skip=0; ws='[ ]'; \ + srcdir=$(srcdir); export srcdir; \ + list=' $(TESTS) '; \ + if test -n "$$list"; then \ + for tst in $$list; do \ + if test -f ./$$tst; then dir=./; \ + elif test -f $$tst; then dir=; \ + else dir="$(srcdir)/"; fi; \ + if $(TESTS_ENVIRONMENT) $${dir}$$tst; then \ + all=`expr $$all + 1`; \ + case " $(XFAIL_TESTS) " in \ + *$$ws$$tst$$ws*) \ + xpass=`expr $$xpass + 1`; \ + failed=`expr $$failed + 1`; \ + echo "XPASS: $$tst"; \ + ;; \ + *) \ + echo "PASS: $$tst"; \ + ;; \ + esac; \ + elif test $$? -ne 77; then \ + all=`expr $$all + 1`; \ + case " $(XFAIL_TESTS) " in \ + *$$ws$$tst$$ws*) \ + xfail=`expr $$xfail + 1`; \ + echo "XFAIL: $$tst"; \ + ;; \ + *) \ + failed=`expr $$failed + 1`; \ + echo "FAIL: $$tst"; \ + ;; \ + esac; \ + else \ + skip=`expr $$skip + 1`; \ + echo "SKIP: $$tst"; \ + fi; \ + done; \ + if test "$$failed" -eq 0; then \ + if test "$$xfail" -eq 0; then \ + banner="All $$all tests passed"; \ + else \ + banner="All $$all tests behaved as expected ($$xfail expected failures)"; \ + fi; \ + else \ + if test "$$xpass" -eq 0; then \ + banner="$$failed of $$all tests failed"; \ + else \ + banner="$$failed of $$all tests did not behave as expected ($$xpass unexpected passes)"; \ + fi; \ + fi; \ + dashes="$$banner"; \ + skipped=""; \ + if test "$$skip" -ne 0; then \ + skipped="($$skip tests were not run)"; \ + test `echo "$$skipped" | wc -c` -le `echo "$$banner" | wc -c` || \ + dashes="$$skipped"; \ + fi; \ + report=""; \ + if test "$$failed" -ne 0 && test -n "$(PACKAGE_BUGREPORT)"; then \ + report="Please report to $(PACKAGE_BUGREPORT)"; \ + test `echo "$$report" | wc -c` -le `echo "$$banner" | wc -c` || \ + dashes="$$report"; \ + fi; \ + dashes=`echo "$$dashes" | sed s/./=/g`; \ + echo "$$dashes"; \ + echo "$$banner"; \ + test -z "$$skipped" || echo "$$skipped"; \ + test -z "$$report" || echo "$$report"; \ + echo "$$dashes"; \ + test "$$failed" -eq 0; \ + else :; fi + +distdir: $(DISTFILES) + @srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ + topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ + list='$(DISTFILES)'; \ + dist_files=`for file in $$list; do echo $$file; done | \ + sed -e "s|^$$srcdirstrip/||;t" \ + -e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \ + case $$dist_files in \ + */*) $(MKDIR_P) `echo "$$dist_files" | \ + sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \ + sort -u` ;; \ + esac; \ + for file in $$dist_files; do \ + if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \ + if test -d $$d/$$file; then \ + dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \ + if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \ + cp -pR $(srcdir)/$$file $(distdir)$$dir || exit 1; \ + fi; \ + cp -pR $$d/$$file $(distdir)$$dir || exit 1; \ + else \ + test -f $(distdir)/$$file \ + || cp -p $$d/$$file $(distdir)/$$file \ + || exit 1; \ + fi; \ + done +check-am: all-am + $(MAKE) $(AM_MAKEFLAGS) check-TESTS +check: check-am +all-am: Makefile +installdirs: +install: install-am +install-exec: install-exec-am +install-data: install-data-am +uninstall: uninstall-am + +install-am: all-am + @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am + +installcheck: installcheck-am +install-strip: + $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ + install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ + `test -z '$(STRIP)' || \ + echo "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'"` install +mostlyclean-generic: + +clean-generic: + +distclean-generic: + -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES) + +maintainer-clean-generic: + @echo "This command is intended for maintainers to use" + @echo "it deletes files that may require special tools to rebuild." +clean: clean-am + +clean-am: clean-generic clean-libtool mostlyclean-am + +distclean: distclean-am + -rm -f Makefile +distclean-am: clean-am distclean-generic + +dvi: dvi-am + +dvi-am: + +html: html-am + +info: info-am + +info-am: + +install-data-am: + +install-dvi: install-dvi-am + +install-exec-am: + +install-html: install-html-am + +install-info: install-info-am + +install-man: + +install-pdf: install-pdf-am + +install-ps: install-ps-am + +installcheck-am: + +maintainer-clean: maintainer-clean-am + -rm -f Makefile +maintainer-clean-am: distclean-am maintainer-clean-generic + +mostlyclean: mostlyclean-am + +mostlyclean-am: mostlyclean-generic mostlyclean-libtool + +pdf: pdf-am + +pdf-am: + +ps: ps-am + +ps-am: + +uninstall-am: + +.MAKE: install-am install-strip + +.PHONY: all all-am check check-TESTS check-am clean clean-generic \ + clean-libtool distclean distclean-generic distclean-libtool \ + distdir dvi dvi-am html html-am info info-am install \ + install-am install-data install-data-am install-dvi \ + install-dvi-am install-exec install-exec-am install-html \ + install-html-am install-info install-info-am install-man \ + install-pdf install-pdf-am install-ps install-ps-am \ + install-strip installcheck installcheck-am installdirs \ + maintainer-clean maintainer-clean-generic mostlyclean \ + mostlyclean-generic mostlyclean-libtool pdf pdf-am ps ps-am \ + uninstall uninstall-am + +# Tell versions [3.59,3.63) of GNU make to not export all variables. +# Otherwise a system limit (for SysV at least) may be exceeded. +.NOEXPORT: diff --git a/tests/bitmap:ip.t b/tests/bitmap:ip.t index b44f5c4..03cbe0d 100644 --- a/tests/bitmap:ip.t +++ b/tests/bitmap:ip.t @@ -23,21 +23,21 @@ # Range: Delete element not added to the set 1 ipset -D test 2.0.0.2 # Range: Delete element not added to the set, with exist flag -0 ipset -x -D test 2.0.0.2 +0 ipset -! -D test 2.0.0.2 # Range: Add element in the middle 0 ipset -A test 2.0.0.128 # Range: Add element in the middle again 1 ipset -A test 2.0.0.128 # Range: Add element in the middle again, with exist flag -0 ipset -x -A test 2.0.0.128 +0 ipset -! -A test 2.0.0.128 # Range: Delete the same element 0 ipset -D test 2.0.0.128 # Range: Add a range of elements 0 ipset -A test 2.0.0.128-2.0.0.131 timeout 6 # Range: List set -0 ipset list test > .foo +0 ipset list test | sed 's/timeout ./timeout x/' > .foo # Range: Check listing -0 grep '2.0.0.1 timeout' .foo >/dev/null +0 diff .foo bitmap:ip.t.list4 && rm .foo # Sleep 10s so that entries can time out 0 sleep 10s # Range: List set after timeout @@ -77,9 +77,9 @@ # Network: Delete the same element 0 ipset -D test 2.0.0.128 # Network: List set -0 ipset list test > .foo +0 ipset list test | sed 's/timeout ./timeout x/' > .foo # Network: Check listing -0 grep '2.0.255.255 timeout' .foo >/dev/null +0 diff .foo bitmap:ip.t.list5 && rm .foo # Sleep 10s so that entries can time out 0 sleep 10s # Network: List set @@ -118,8 +118,10 @@ 0 ipset -D test 10.2.0.0 # Subnets: Add a subnet of subnets 0 ipset -A test 10.8.0.0/16 timeout 8 +# Subnets: List set +0 ipset list test | sed 's/timeout ./timeout x/' > .foo # Subnets: Check listing -0 ipset list test | grep '10.0.0.0 timeout' >/dev/null +0 diff .foo bitmap:ip.t.list6 && rm .foo # Sleep 10s so that entries can time out 0 sleep 10s # Subnets: List set diff --git a/tests/bitmap:ip.t.list0 b/tests/bitmap:ip.t.list0 index 0be60c0..5f6b35d 100644 --- a/tests/bitmap:ip.t.list0 +++ b/tests/bitmap:ip.t.list0 @@ -1,8 +1,7 @@ Name: test Type: bitmap:ip Header: range 2.0.0.1-2.1.0.0 timeout 5 -Elements: 1 -Size in memory: 524288 +Size in memory: 524408 References: 0 Members: 2.1.0.0 timeout 0 diff --git a/tests/bitmap:ip.t.list1 b/tests/bitmap:ip.t.list1 index 02ccdaa..6064717 100644 --- a/tests/bitmap:ip.t.list1 +++ b/tests/bitmap:ip.t.list1 @@ -1,8 +1,7 @@ Name: test Type: bitmap:ip Header: range 2.0.0.0-2.0.255.255 timeout 5 -Elements: 1 -Size in memory: 524288 +Size in memory: 524408 References: 0 Members: 2.0.0.0 timeout 0 diff --git a/tests/bitmap:ip.t.list2 b/tests/bitmap:ip.t.list2 index 7b17999..e00abc9 100644 --- a/tests/bitmap:ip.t.list2 +++ b/tests/bitmap:ip.t.list2 @@ -1,8 +1,7 @@ Name: test Type: bitmap:ip Header: range 10.0.0.0-10.255.255.255 netmask 24 timeout 5 -Elements: 1 -Size in memory: 524288 +Size in memory: 524408 References: 0 Members: 10.255.255.0 timeout 0 diff --git a/tests/bitmap:ip.t.list3 b/tests/bitmap:ip.t.list3 index 677bb2a..2d5e7a7 100644 --- a/tests/bitmap:ip.t.list3 +++ b/tests/bitmap:ip.t.list3 @@ -1,8 +1,7 @@ Name: test Type: bitmap:ip Header: range 0.0.0.0-255.255.255.255 netmask 16 timeout 5 -Elements: 2 -Size in memory: 524288 +Size in memory: 524408 References: 0 Members: 0.0.0.0 timeout 0 diff --git a/tests/bitmap:ip.t.list4 b/tests/bitmap:ip.t.list4 new file mode 100644 index 0000000..53004cf --- /dev/null +++ b/tests/bitmap:ip.t.list4 @@ -0,0 +1,13 @@ +Name: test +Type: bitmap:ip +Header: range 2.0.0.1-2.1.0.0 timeout x +Size in memory: 524408 +References: 0 +Members: +2.0.0.1 timeout x +2.0.0.128 timeout x +2.0.0.129 timeout x +2.0.0.130 timeout x +2.0.0.131 timeout x +2.1.0.0 timeout x + diff --git a/tests/bitmap:ip.t.list5 b/tests/bitmap:ip.t.list5 new file mode 100644 index 0000000..65d338d --- /dev/null +++ b/tests/bitmap:ip.t.list5 @@ -0,0 +1,9 @@ +Name: test +Type: bitmap:ip +Header: range 2.0.0.0-2.0.255.255 timeout x +Size in memory: 524408 +References: 0 +Members: +2.0.0.0 timeout x +2.0.255.255 timeout x + diff --git a/tests/bitmap:ip.t.list6 b/tests/bitmap:ip.t.list6 new file mode 100644 index 0000000..13b75ad --- /dev/null +++ b/tests/bitmap:ip.t.list6 @@ -0,0 +1,265 @@ +Name: test +Type: bitmap:ip +Header: range 10.0.0.0-10.255.255.255 netmask 24 timeout x +Size in memory: 524408 +References: 0 +Members: +10.0.0.0 timeout x +10.8.0.0 timeout x +10.8.1.0 timeout x +10.8.2.0 timeout x +10.8.3.0 timeout x +10.8.4.0 timeout x +10.8.5.0 timeout x +10.8.6.0 timeout x +10.8.7.0 timeout x +10.8.8.0 timeout x +10.8.9.0 timeout x +10.8.10.0 timeout x +10.8.11.0 timeout x +10.8.12.0 timeout x +10.8.13.0 timeout x +10.8.14.0 timeout x +10.8.15.0 timeout x +10.8.16.0 timeout x +10.8.17.0 timeout x +10.8.18.0 timeout x +10.8.19.0 timeout x +10.8.20.0 timeout x +10.8.21.0 timeout x +10.8.22.0 timeout x +10.8.23.0 timeout x +10.8.24.0 timeout x +10.8.25.0 timeout x +10.8.26.0 timeout x +10.8.27.0 timeout x +10.8.28.0 timeout x +10.8.29.0 timeout x +10.8.30.0 timeout x +10.8.31.0 timeout x +10.8.32.0 timeout x +10.8.33.0 timeout x +10.8.34.0 timeout x +10.8.35.0 timeout x +10.8.36.0 timeout x +10.8.37.0 timeout x +10.8.38.0 timeout x +10.8.39.0 timeout x +10.8.40.0 timeout x +10.8.41.0 timeout x +10.8.42.0 timeout x +10.8.43.0 timeout x +10.8.44.0 timeout x +10.8.45.0 timeout x +10.8.46.0 timeout x +10.8.47.0 timeout x +10.8.48.0 timeout x +10.8.49.0 timeout x +10.8.50.0 timeout x +10.8.51.0 timeout x +10.8.52.0 timeout x +10.8.53.0 timeout x +10.8.54.0 timeout x +10.8.55.0 timeout x +10.8.56.0 timeout x +10.8.57.0 timeout x +10.8.58.0 timeout x +10.8.59.0 timeout x +10.8.60.0 timeout x +10.8.61.0 timeout x +10.8.62.0 timeout x +10.8.63.0 timeout x +10.8.64.0 timeout x +10.8.65.0 timeout x +10.8.66.0 timeout x +10.8.67.0 timeout x +10.8.68.0 timeout x +10.8.69.0 timeout x +10.8.70.0 timeout x +10.8.71.0 timeout x +10.8.72.0 timeout x +10.8.73.0 timeout x +10.8.74.0 timeout x +10.8.75.0 timeout x +10.8.76.0 timeout x +10.8.77.0 timeout x +10.8.78.0 timeout x +10.8.79.0 timeout x +10.8.80.0 timeout x +10.8.81.0 timeout x +10.8.82.0 timeout x +10.8.83.0 timeout x +10.8.84.0 timeout x +10.8.85.0 timeout x +10.8.86.0 timeout x +10.8.87.0 timeout x +10.8.88.0 timeout x +10.8.89.0 timeout x +10.8.90.0 timeout x +10.8.91.0 timeout x +10.8.92.0 timeout x +10.8.93.0 timeout x +10.8.94.0 timeout x +10.8.95.0 timeout x +10.8.96.0 timeout x +10.8.97.0 timeout x +10.8.98.0 timeout x +10.8.99.0 timeout x +10.8.100.0 timeout x +10.8.101.0 timeout x +10.8.102.0 timeout x +10.8.103.0 timeout x +10.8.104.0 timeout x +10.8.105.0 timeout x +10.8.106.0 timeout x +10.8.107.0 timeout x +10.8.108.0 timeout x +10.8.109.0 timeout x +10.8.110.0 timeout x +10.8.111.0 timeout x +10.8.112.0 timeout x +10.8.113.0 timeout x +10.8.114.0 timeout x +10.8.115.0 timeout x +10.8.116.0 timeout x +10.8.117.0 timeout x +10.8.118.0 timeout x +10.8.119.0 timeout x +10.8.120.0 timeout x +10.8.121.0 timeout x +10.8.122.0 timeout x +10.8.123.0 timeout x +10.8.124.0 timeout x +10.8.125.0 timeout x +10.8.126.0 timeout x +10.8.127.0 timeout x +10.8.128.0 timeout x +10.8.129.0 timeout x +10.8.130.0 timeout x +10.8.131.0 timeout x +10.8.132.0 timeout x +10.8.133.0 timeout x +10.8.134.0 timeout x +10.8.135.0 timeout x +10.8.136.0 timeout x +10.8.137.0 timeout x +10.8.138.0 timeout x +10.8.139.0 timeout x +10.8.140.0 timeout x +10.8.141.0 timeout x +10.8.142.0 timeout x +10.8.143.0 timeout x +10.8.144.0 timeout x +10.8.145.0 timeout x +10.8.146.0 timeout x +10.8.147.0 timeout x +10.8.148.0 timeout x +10.8.149.0 timeout x +10.8.150.0 timeout x +10.8.151.0 timeout x +10.8.152.0 timeout x +10.8.153.0 timeout x +10.8.154.0 timeout x +10.8.155.0 timeout x +10.8.156.0 timeout x +10.8.157.0 timeout x +10.8.158.0 timeout x +10.8.159.0 timeout x +10.8.160.0 timeout x +10.8.161.0 timeout x +10.8.162.0 timeout x +10.8.163.0 timeout x +10.8.164.0 timeout x +10.8.165.0 timeout x +10.8.166.0 timeout x +10.8.167.0 timeout x +10.8.168.0 timeout x +10.8.169.0 timeout x +10.8.170.0 timeout x +10.8.171.0 timeout x +10.8.172.0 timeout x +10.8.173.0 timeout x +10.8.174.0 timeout x +10.8.175.0 timeout x +10.8.176.0 timeout x +10.8.177.0 timeout x +10.8.178.0 timeout x +10.8.179.0 timeout x +10.8.180.0 timeout x +10.8.181.0 timeout x +10.8.182.0 timeout x +10.8.183.0 timeout x +10.8.184.0 timeout x +10.8.185.0 timeout x +10.8.186.0 timeout x +10.8.187.0 timeout x +10.8.188.0 timeout x +10.8.189.0 timeout x +10.8.190.0 timeout x +10.8.191.0 timeout x +10.8.192.0 timeout x +10.8.193.0 timeout x +10.8.194.0 timeout x +10.8.195.0 timeout x +10.8.196.0 timeout x +10.8.197.0 timeout x +10.8.198.0 timeout x +10.8.199.0 timeout x +10.8.200.0 timeout x +10.8.201.0 timeout x +10.8.202.0 timeout x +10.8.203.0 timeout x +10.8.204.0 timeout x +10.8.205.0 timeout x +10.8.206.0 timeout x +10.8.207.0 timeout x +10.8.208.0 timeout x +10.8.209.0 timeout x +10.8.210.0 timeout x +10.8.211.0 timeout x +10.8.212.0 timeout x +10.8.213.0 timeout x +10.8.214.0 timeout x +10.8.215.0 timeout x +10.8.216.0 timeout x +10.8.217.0 timeout x +10.8.218.0 timeout x +10.8.219.0 timeout x +10.8.220.0 timeout x +10.8.221.0 timeout x +10.8.222.0 timeout x +10.8.223.0 timeout x +10.8.224.0 timeout x +10.8.225.0 timeout x +10.8.226.0 timeout x +10.8.227.0 timeout x +10.8.228.0 timeout x +10.8.229.0 timeout x +10.8.230.0 timeout x +10.8.231.0 timeout x +10.8.232.0 timeout x +10.8.233.0 timeout x +10.8.234.0 timeout x +10.8.235.0 timeout x +10.8.236.0 timeout x +10.8.237.0 timeout x +10.8.238.0 timeout x +10.8.239.0 timeout x +10.8.240.0 timeout x +10.8.241.0 timeout x +10.8.242.0 timeout x +10.8.243.0 timeout x +10.8.244.0 timeout x +10.8.245.0 timeout x +10.8.246.0 timeout x +10.8.247.0 timeout x +10.8.248.0 timeout x +10.8.249.0 timeout x +10.8.250.0 timeout x +10.8.251.0 timeout x +10.8.252.0 timeout x +10.8.253.0 timeout x +10.8.254.0 timeout x +10.8.255.0 timeout x +10.255.255.0 timeout x + diff --git a/tests/hash:ip,port,ip.t b/tests/hash:ip,port,ip.t new file mode 100644 index 0000000..3c74cd9 --- /dev/null +++ b/tests/hash:ip,port,ip.t @@ -0,0 +1,51 @@ +# Range: Create a set with timeout +0 ipset create test hash:ip,port,ip timeout 5 +# Range: Add partly zero valued element +0 ipset add test 2.0.0.1,0,0.0.0.0 +# Range: Test partly zero valued element +0 ipset test test 2.0.0.1,0,0.0.0.0 +# Range: Delete party zero valued element +0 ipset del test 2.0.0.1,0,0.0.0.0 +# Range: Add almost zero valued element +0 ipset add test 2.0.0.1,0,0.0.0.1 +# Range: Test almost zero valued element +0 ipset test test 2.0.0.1,0,0.0.0.1 +# Range: Delete almost zero valued element +0 ipset del test 2.0.0.1,0,0.0.0.1 +# Range: Add lower boundary +0 ipset add test 2.0.0.1,5,1.1.1.1 +# Range: Add upper boundary +0 ipset add test 2.1.0.0,128,2.2.2.2 +# Range: Test lower boundary +0 ipset test test 2.0.0.1,5,1.1.1.1 +# Range: Test upper boundary +0 ipset test test 2.1.0.0,128,2.2.2.2 +# Range: Test value not added to the set +1 ipset test test 2.0.0.1,5,1.1.1.2 +# Range: Test value not added to the set +1 ipset test test 2.0.0.1,6,1.1.1.1 +# Range: Test value not added to the set +1 ipset test test 2.0.0.2,6,1.1.1.1 +# Range: Test value before lower boundary +1 ipset test test 2.0.0.0,5,1.1.1.1 +# Range: Test value after upper boundary +1 ipset test test 2.1.0.1,128,2.2.2.2 +# Range: Try to add value before lower boundary +0 ipset add test 2.0.0.0,5,1.1.1.1 +# Range: Try to add value after upper boundary +0 ipset add test 2.1.0.1,128,2.2.2.2 +# Range: List set +0 ipset list test | sed 's/timeout ./timeout x/' > .foo0 && ./sort.sh .foo0 +# Range: Check listing +0 diff -I 'Size in memory.*' .foo hash:ip,port,ip.t.list0 && rm .foo +# Range: Sleep 6s so that elements can time out +0 sleep 6 +# Range: List set +0 ipset list test > .foo0 && ./sort.sh .foo0 +# Range: Check listing +0 diff -I 'Size in memory.*' .foo hash:ip,port,ip.t.list1 && rm .foo +# Range: Flush test set +0 ipset flush test +# Range: Delete test set +0 ipset destroy test +# eof diff --git a/tests/hash:ip,port,ip.t.list0 b/tests/hash:ip,port,ip.t.list0 new file mode 100644 index 0000000..b20c8d8 --- /dev/null +++ b/tests/hash:ip,port,ip.t.list0 @@ -0,0 +1,11 @@ +Name: test +Type: hash:ip,port,ip +Header: family inet hashsize 1024 maxelem 65536 timeout x +Size in memory: 8720 +References: 0 +Members: +2.0.0.0,5,1.1.1.1 timeout x +2.0.0.1,5,1.1.1.1 timeout x +2.1.0.0,128,2.2.2.2 timeout x +2.1.0.1,128,2.2.2.2 timeout x + diff --git a/tests/hash:ip,port,ip.t.list1 b/tests/hash:ip,port,ip.t.list1 new file mode 100644 index 0000000..fb0b4a4 --- /dev/null +++ b/tests/hash:ip,port,ip.t.list1 @@ -0,0 +1,7 @@ +Name: test +Type: hash:ip,port,ip +Header: family inet hashsize 1024 maxelem 65536 timeout 5 +Size in memory: 8304 +References: 0 +Members: + diff --git a/tests/hash:ip,port.t b/tests/hash:ip,port.t new file mode 100644 index 0000000..cb88f96 --- /dev/null +++ b/tests/hash:ip,port.t @@ -0,0 +1,43 @@ +# Range: Create a set with timeout +0 ipset create test hash:ip,port timeout 5 +# Range: Add partly zero valued element +0 ipset add test 2.0.0.1,0 +# Range: Test partly zero valued element +0 ipset test test 2.0.0.1,0 +# Range: Delete partly zero valued element +0 ipset del test 2.0.0.1,0 +# Range: Add lower boundary +0 ipset add test 2.0.0.1,5 +# Range: Add upper boundary +0 ipset add test 2.1.0.0,128 +# Range: Test lower boundary +0 ipset test test 2.0.0.1,5 +# Range: Test upper boundary +0 ipset test test 2.1.0.0,128 +# Range: Test value not added to the set +1 ipset test test 2.0.0.1,4 +# Range: Delete value not added to the set +1 ipset del test 2.0.0.1,6 +# Range: Test value before lower boundary +1 ipset test test 2.0.0.0,5 +# Range: Test value after upper boundary +1 ipset test test 2.1.0.1,128 +# Range: Try to add value before lower boundary +0 ipset add test 2.0.0.0,5 +# Range: Try to add value after upper boundary +0 ipset add test 2.1.0.1,128 +# Range: List set +0 ipset list test | sed 's/timeout ./timeout x/' > .foo0 && ./sort.sh .foo0 +# Range: Check listing +0 diff -I 'Size in memory.*' .foo hash:ip,port.t.list0 && rm .foo +# Range: Sleep 6s so that elements can time out +0 sleep 6 +# Range: List set +0 ipset list test > .foo0 && ./sort.sh .foo0 +# Range: Check listing +0 diff -I 'Size in memory.*' .foo hash:ip,port.t.list1 && rm .foo +# Range: Flush test set +0 ipset flush test +# Range: Delete test set +0 ipset destroy test +# eof diff --git a/tests/hash:ip,port.t.list0 b/tests/hash:ip,port.t.list0 new file mode 100644 index 0000000..25d8632 --- /dev/null +++ b/tests/hash:ip,port.t.list0 @@ -0,0 +1,11 @@ +Name: test +Type: hash:ip,port +Header: family inet hashsize 1024 maxelem 65536 timeout x +Size in memory: 8592 +References: 0 +Members: +2.0.0.0,5 timeout x +2.0.0.1,5 timeout x +2.1.0.0,128 timeout x +2.1.0.1,128 timeout x + diff --git a/tests/hash:ip,port.t.list1 b/tests/hash:ip,port.t.list1 new file mode 100644 index 0000000..d455117 --- /dev/null +++ b/tests/hash:ip,port.t.list1 @@ -0,0 +1,7 @@ +Name: test +Type: hash:ip,port +Header: family inet hashsize 1024 maxelem 65536 timeout 5 +Size in memory: 8304 +References: 0 +Members: + diff --git a/tests/hash:ip.t b/tests/hash:ip.t index de6b0df..c7bddb8 100644 --- a/tests/hash:ip.t +++ b/tests/hash:ip.t @@ -18,12 +18,16 @@ 0 ipset -A test 200.100.0.12 # IP: Delete the same value 0 ipset -D test 200.100.0.12 +# IP: List set +0 ipset -L test | sed 's/timeout ./timeout x/' > .foo0 && ./sort.sh .foo0 +# IP: Check listing +0 diff -I 'Size in memory.*' .foo hash:ip.t.list2 && rm .foo # Sleep 6s so that element can time out 0 sleep 6 # IP: List set 0 ipset -L test 2>/dev/null > .foo0 && ./sort.sh .foo0 # IP: Check listing -0 diff .foo hash:ip.t.list0 && rm .foo +0 diff -I 'Size in memory.*' .foo hash:ip.t.list0 && rm .foo # IP: Flush test set 0 ipset -F test # IP: Delete test set @@ -58,20 +62,22 @@ 0 ipset -T test 192.168.68.95 # Network: Test value not added to the set 1 ipset -T test 2.0.1.0 -# Network: List set -0 ipset -L test > .foo && grep '2.0.0.0 timeout' .foo >/dev/null && grep '192.168.68.0 timeout' .foo >/dev/null && rm .foo # Network: Add third element 0 ipset -A test 200.100.10.1 timeout 0 # Network: Add third random network 0 ipset -A test 200.100.0.12 # Network: Delete the same network 0 ipset -D test 200.100.0.12 +# Network: List set +0 ipset -L test | sed 's/timeout ./timeout x/' > .foo0 && ./sort.sh .foo0 +# Network: Check listing +0 diff -I 'Size in memory.*' -I 'Size in memory.*' .foo hash:ip.t.list3 && rm .foo # Sleep 6s so that elements can time out 0 sleep 6 # Network: List set 0 ipset -L test > .foo # Network: Check listing -0 diff .foo hash:ip.t.list1 && rm .foo +0 diff -I 'Size in memory.*' .foo hash:ip.t.list1 && rm .foo # Network: Flush test set 0 ipset -F test # Network: Delete test set diff --git a/tests/hash:ip.t.list0 b/tests/hash:ip.t.list0 index cf2ecac..117faf2 100644 --- a/tests/hash:ip.t.list0 +++ b/tests/hash:ip.t.list0 @@ -1,8 +1,7 @@ Name: test Type: hash:ip -Header: hashsize 128 maxelem 65536 probes 4 resize 50 timeout 5 -Elements: 1 -Size in memory: 2048 +Header: family inet hashsize 128 maxelem 65536 timeout 5 +Size in memory: 1208 References: 0 Members: 192.168.68.69 timeout 0 diff --git a/tests/hash:ip.t.list1 b/tests/hash:ip.t.list1 index c564ba0..3189dae 100644 --- a/tests/hash:ip.t.list1 +++ b/tests/hash:ip.t.list1 @@ -1,8 +1,7 @@ Name: test Type: hash:ip -Header: hashsize 128 maxelem 65536 probes 4 resize 50 netmask 24 timeout 6 -Elements: 1 -Size in memory: 2048 +Header: family inet hashsize 128 maxelem 65536 netmask 24 timeout 6 +Size in memory: 1352 References: 0 Members: 200.100.10.0 timeout 0 diff --git a/tests/hash:ip.t.list2 b/tests/hash:ip.t.list2 new file mode 100644 index 0000000..468ba67 --- /dev/null +++ b/tests/hash:ip.t.list2 @@ -0,0 +1,9 @@ +Name: test +Type: hash:ip +Header: family inet hashsize 128 maxelem 65536 timeout x +Size in memory: 1280 +References: 0 +Members: +192.168.68.69 timeout x +2.0.0.1 timeout x + diff --git a/tests/hash:ip.t.list3 b/tests/hash:ip.t.list3 new file mode 100644 index 0000000..463f0a4 --- /dev/null +++ b/tests/hash:ip.t.list3 @@ -0,0 +1,10 @@ +Name: test +Type: hash:ip +Header: family inet hashsize 128 maxelem 65536 netmask 24 timeout x +Size in memory: 1352 +References: 0 +Members: +192.168.68.0 timeout x +2.0.0.0 timeout x +200.100.10.0 timeout x + diff --git a/tests/hash:ip6,port,ip6.t b/tests/hash:ip6,port,ip6.t new file mode 100644 index 0000000..84afdf9 --- /dev/null +++ b/tests/hash:ip6,port,ip6.t @@ -0,0 +1,51 @@ +# Range: Create a set with timeout +0 ipset create test hash:ip,port,ip family inet6 timeout 5 +# Range: Add partly zero valued element +0 ipset add test 2:0:0::1,0,0:0:0::0 +# Range: Test partly zero valued element +0 ipset test test 2:0:0::1,0,0:0:0::0 +# Range: Delete party zero valued element +0 ipset del test 2:0:0::1,0,0:0:0::0 +# Range: Add almost zero valued element +0 ipset add test 2:0:0::1,0,0:0:0::1 +# Range: Test almost zero valued element +0 ipset test test 2:0:0::1,0,0:0:0::1 +# Range: Delete almost zero valued element +0 ipset del test 2:0:0::1,0,0:0:0::1 +# Range: Add lower boundary +0 ipset add test 2:0:0::1,5,1:1:1::1 +# Range: Add upper boundary +0 ipset add test 2:1:0::0,128,2:2:2::2 +# Range: Test lower boundary +0 ipset test test 2:0:0::1,5,1:1:1::1 +# Range: Test upper boundary +0 ipset test test 2:1:0::0,128,2:2:2::2 +# Range: Test value not added to the set +1 ipset test test 2:0:0::1,5,1:1:1::2 +# Range: Test value not added to the set +1 ipset test test 2:0:0::1,6,1:1:1::1 +# Range: Test value not added to the set +1 ipset test test 2:0:0::2,6,1:1:1::1 +# Range: Test value before lower boundary +1 ipset test test 2:0:0::0,5,1:1:1::1 +# Range: Test value after upper boundary +1 ipset test test 2:1:0::1,128,2:2:2::2 +# Range: Try to add value before lower boundary +0 ipset add test 2:0:0::0,5,1:1:1::1 +# Range: Try to add value after upper boundary +0 ipset add test 2:1:0::1,128,2:2:2::2 +# Range: List set +0 ipset list test | sed 's/timeout ./timeout x/' > .foo0 && ./sort.sh .foo0 +# Range: Check listing +0 diff -I 'Size in memory.*' .foo hash:ip6,port,ip6.t.list0 && rm .foo +# Range: Sleep 6s so that elements can time out +0 sleep 6 +# Range: List set +0 ipset list test > .foo0 && ./sort.sh .foo0 +# Range: Check listing +0 diff -I 'Size in memory.*' .foo hash:ip6,port,ip6.t.list1 && rm .foo +# Range: Flush test set +0 ipset flush test +# Range: Delete test set +0 ipset destroy test +# eof diff --git a/tests/hash:ip6,port,ip6.t.list0 b/tests/hash:ip6,port,ip6.t.list0 new file mode 100644 index 0000000..84cdf5c --- /dev/null +++ b/tests/hash:ip6,port,ip6.t.list0 @@ -0,0 +1,11 @@ +Name: test +Type: hash:ip,port,ip +Header: family inet6 hashsize 1024 maxelem 65536 timeout x +Size in memory: 9104 +References: 0 +Members: +2:1::1,128,2:2:2::2 timeout x +2:1::,128,2:2:2::2 timeout x +2::1,5,1:1:1::1 timeout x +2::,5,1:1:1::1 timeout x + diff --git a/tests/hash:ip6,port,ip6.t.list1 b/tests/hash:ip6,port,ip6.t.list1 new file mode 100644 index 0000000..16fdb92 --- /dev/null +++ b/tests/hash:ip6,port,ip6.t.list1 @@ -0,0 +1,7 @@ +Name: test +Type: hash:ip,port,ip +Header: family inet6 hashsize 1024 maxelem 65536 timeout 5 +Size in memory: 8304 +References: 0 +Members: + diff --git a/tests/hash:ip6,port,net6.t b/tests/hash:ip6,port,net6.t new file mode 100644 index 0000000..0041b5d --- /dev/null +++ b/tests/hash:ip6,port,net6.t @@ -0,0 +1,45 @@ +# Range: Create a set +0 ipset -N test ipportnethash -6 +# Range: Add zero valued element +1 ipset -A test 2:0:0::1,0,0:0:0::0/0 +# Range: Test zero valued element +1 ipset -T test 2:0:0::1,0,0:0:0::0/0 +# Range: Delete zero valued element +1 ipset -D test 2:0:0::1,0,0:0:0::0/0 +# Range: Add almost zero valued element +0 ipset -A test 2:0:0::1,0,0:0:0::0/24 +# Range: Test almost zero valued element +0 ipset -T test 2:0:0::1,0,0:0:0::0/24 +# Range: Delete almost zero valued element +0 ipset -D test 2:0:0::1,0,0:0:0::0/24 +# Range: Add lower boundary +0 ipset -A test 2:0:0::1,5,1:1:1::1/24 +# Range: Add upper boundary +0 ipset -A test 2:1:0::0,128,2:2:2::2/12 +# Range: Test lower boundary +0 ipset -T test 2:0:0::1,5,1:1:1::2 +# Range: Test upper boundary +0 ipset -T test 2:1:0::0,128,2:2:2::0 +# Range: Test value not added to the set +1 ipset -T test 2:0:0::1,5,2:1:1::255 +# Range: Test value not added to the set +1 ipset -T test 2:0:0::1,6,1:1:1::1 +# Range: Test value not added to the set +1 ipset -T test 2:0:0::2,6,1:1:1::1 +# Range: Test value before lower boundary +1 ipset -T test 2:0:0::0,5,1:1:1::1 +# Range: Test value after upper boundary +1 ipset -T test 2:1:0::1,128,2:2:2::2 +# Range: Try to add value before lower boundary +0 ipset -A test 2:0:0::0,5,1:1:1::1/24 +# Range: Try to add value after upper boundary +0 ipset -A test 2:1:0::1,128,2:2:2::2/12 +# Range: List set +0 ipset -L test > .foo0 && ./sort.sh .foo0 +# Range: Check listing +0 diff -I 'Size in memory.*' .foo hash:ip6,port,net6.t.list0 && rm .foo +# Range: Flush test set +0 ipset -F test +# Range: Delete test set +0 ipset -X test +# eof diff --git a/tests/hash:ip6,port,net6.t.list0 b/tests/hash:ip6,port,net6.t.list0 new file mode 100644 index 0000000..cd9bffe --- /dev/null +++ b/tests/hash:ip6,port,net6.t.list0 @@ -0,0 +1,11 @@ +Name: test +Type: hash:ip,port,net +Header: family inet6 family inet6 hashsize 1024 maxelem 65536 +Size in memory: 9928 +References: 0 +Members: +2:1::1,128,::/12 +2:1::,128,::/12 +2::1,5,1::/24 +2::,5,1::/24 + diff --git a/tests/hash:ip6,port.t b/tests/hash:ip6,port.t new file mode 100644 index 0000000..feb525c --- /dev/null +++ b/tests/hash:ip6,port.t @@ -0,0 +1,43 @@ +# Range: Create a set with timeout +0 ipset create test hash:ip,port family inet6 timeout 5 +# Range: Add partly zero valued element +0 ipset add test 2:0:0::1,0 +# Range: Test partly zero valued element +0 ipset test test 2:0:0::1,0 +# Range: Delete partly zero valued element +0 ipset del test 2:0:0::1,0 +# Range: Add lower boundary +0 ipset add test 2:0:0::1,5 +# Range: Add upper boundary +0 ipset add test 2:1:0::0,128 +# Range: Test lower boundary +0 ipset test test 2:0:0::1,5 +# Range: Test upper boundary +0 ipset test test 2:1:0::0,128 +# Range: Test value not added to the set +1 ipset test test 2:0:0::1,4 +# Range: Delete value not added to the set +1 ipset del test 2:0:0::1,6 +# Range: Test value before lower boundary +1 ipset test test 2:0:0::0,5 +# Range: Test value after upper boundary +1 ipset test test 2:1:0::1,128 +# Range: Try to add value before lower boundary +0 ipset add test 2:0:0::0,5 +# Range: Try to add value after upper boundary +0 ipset add test 2:1:0::1,128 +# Range: List set +0 ipset list test | sed 's/timeout ./timeout x/' > .foo0 && ./sort.sh .foo0 +# Range: Check listing +0 diff -I 'Size in memory.*' .foo hash:ip6,port.t.list0 && rm .foo +# Range: Sleep 6s so that elements can time out +0 sleep 6 +# Range: List set +0 ipset list test > .foo0 && ./sort.sh .foo0 +# Range: Check listing +0 diff -I 'Size in memory.*' .foo hash:ip6,port.t.list1 && rm .foo +# Range: Flush test set +0 ipset flush test +# Range: Delete test set +0 ipset destroy test +# eof diff --git a/tests/hash:ip6,port.t.list0 b/tests/hash:ip6,port.t.list0 new file mode 100644 index 0000000..defd377 --- /dev/null +++ b/tests/hash:ip6,port.t.list0 @@ -0,0 +1,11 @@ +Name: test +Type: hash:ip,port +Header: family inet6 hashsize 1024 maxelem 65536 timeout x +Size in memory: 8848 +References: 0 +Members: +2:1::1,128 timeout x +2:1::,128 timeout x +2::1,5 timeout x +2::,5 timeout x + diff --git a/tests/hash:ip6,port.t.list1 b/tests/hash:ip6,port.t.list1 new file mode 100644 index 0000000..8dec3e8 --- /dev/null +++ b/tests/hash:ip6,port.t.list1 @@ -0,0 +1,7 @@ +Name: test +Type: hash:ip,port +Header: family inet6 hashsize 1024 maxelem 65536 timeout 5 +Size in memory: 8304 +References: 0 +Members: + diff --git a/tests/hash:ip6.t b/tests/hash:ip6.t new file mode 100644 index 0000000..dae9e1e --- /dev/null +++ b/tests/hash:ip6.t @@ -0,0 +1,77 @@ +# IP: Create a set with timeout +0 ipset -N test iphash -6 --hashsize 128 timeout 5 +# IP: Add zero valued element +1 ipset -A test :: +# IP: Test zero valued element +1 ipset -T test :: +# IP: Delete zero valued element +1 ipset -D test :: +# IP: Add first random value +0 ipset -A test 2:0:0::1 timeout 5 +# IP: Add second random value +0 ipset -A test 192:168:68::69 timeout 0 +# IP: Test first random value +0 ipset -T test 2:0:0::1 +# IP: Test second random value +0 ipset -T test 192:168:68::69 +# IP: Test value not added to the set +1 ipset -T test 2:0:0::2 +# IP: Add third random value +0 ipset -A test 200:100:0::12 +# IP: Delete the same value +0 ipset -D test 200:100:0::12 +# IP: List set +0 ipset -L test | sed 's/timeout ./timeout x/' > .foo0 && ./sort.sh .foo0 +# IP: Check listing +0 diff -I 'Size in memory.*' .foo hash:ip6.t.list2 && rm .foo +# Sleep 6s so that element can time out +0 sleep 6 +# IP: List set +0 ipset -L test 2>/dev/null > .foo0 && ./sort.sh .foo0 +# IP: Check listing +0 diff -I 'Size in memory.*' .foo hash:ip6.t.list0 && rm .foo +# IP: Flush test set +0 ipset -F test +# IP: Delete test set +0 ipset -X test +# Network: Create a set with timeout +0 ipset -N test iphash -6 --hashsize 128 --netmask 64 timeout 6 +# Network: Add zero valued element +1 ipset -A test :: +# Network: Test zero valued element +1 ipset -T test :: +# Network: Delete zero valued element +1 ipset -D test :: +# 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 4: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:101:0::12 +# Network: Delete the same network +0 ipset -D test 200:101:0::12 +# Network: Test the deleted network +1 ipset -T test 200:101:0::12 +# Network: List set +0 ipset -L test | sed 's/timeout ./timeout x/' > .foo0 && ./sort.sh .foo0 +# Network: Check listing +0 diff -I 'Size in memory.*' .foo hash:ip6.t.list3 && rm .foo +# Sleep 6s so that elements can time out +0 sleep 6 +# Network: List set +0 ipset -L test > .foo +# Network: Check listing +0 diff -I 'Size in memory.*' .foo hash:ip6.t.list1 && rm .foo +# Network: Flush test set +0 ipset -F test +# Network: Delete test set +0 ipset -X test +# eof diff --git a/tests/hash:ip6.t.list0 b/tests/hash:ip6.t.list0 new file mode 100644 index 0000000..ff1c0da --- /dev/null +++ b/tests/hash:ip6.t.list0 @@ -0,0 +1,8 @@ +Name: test +Type: hash:ip +Header: family inet6 hashsize 128 maxelem 65536 timeout 5 +Size in memory: 1240 +References: 0 +Members: +192:168:68::69 timeout 0 + diff --git a/tests/hash:ip6.t.list1 b/tests/hash:ip6.t.list1 new file mode 100644 index 0000000..ce5c5fe --- /dev/null +++ b/tests/hash:ip6.t.list1 @@ -0,0 +1,8 @@ +Name: test +Type: hash:ip +Header: family inet6 hashsize 128 maxelem 65536 netmask 64 timeout 6 +Size in memory: 1448 +References: 0 +Members: +200:100:10:: timeout 0 + diff --git a/tests/hash:ip6.t.list2 b/tests/hash:ip6.t.list2 new file mode 100644 index 0000000..5cd39fb --- /dev/null +++ b/tests/hash:ip6.t.list2 @@ -0,0 +1,9 @@ +Name: test +Type: hash:ip +Header: family inet6 hashsize 128 maxelem 65536 timeout x +Size in memory: 1344 +References: 0 +Members: +192:168:68::69 timeout x +2::1 timeout x + diff --git a/tests/hash:ip6.t.list3 b/tests/hash:ip6.t.list3 new file mode 100644 index 0000000..dce0660 --- /dev/null +++ b/tests/hash:ip6.t.list3 @@ -0,0 +1,10 @@ +Name: test +Type: hash:ip +Header: family inet6 hashsize 128 maxelem 65536 netmask 64 timeout x +Size in memory: 1448 +References: 0 +Members: +192:168:68:: timeout x +200:100:10:: timeout x +2:: timeout x + diff --git a/tests/hash:net.t b/tests/hash:net.t new file mode 100644 index 0000000..b46ac86 --- /dev/null +++ b/tests/hash:net.t @@ -0,0 +1,49 @@ +# Create a set with timeout +0 ipset create test nethash hashsize 128 timeout 6 +# Range: Add zero valued element +1 ipset add test 0.0.0.0/0 +# Range: Test zero valued element +1 ipset test test 0.0.0.0/0 +# Range: Delete zero valued element +1 ipset del test 0.0.0.0/0 +# Range: Try to add /0 +1 ipset add test 1.1.1.1/0 +# Range: Try to add /32 +0 ipset add test 1.1.1.1/32 +# Range: Add almost zero valued element +0 ipset add test 0.0.0.0/1 +# Range: Test almost zero valued element +0 ipset test test 0.0.0.0/1 +# Range: Delete almost zero valued element +0 ipset del test 0.0.0.0/1 +# Range: Test deleted element +1 ipset test test 0.0.0.0/1 +# Range: Delete element not added to the set +1 ipset del test 0.0.0.0/1 +# Range: Add first random network +0 ipset add test 2.0.0.1/24 +# Range: Add second random network +0 ipset add test 192.168.68.69/27 +# Range: Test first random value +0 ipset test test 2.0.0.255 +# Range: Test second random value +0 ipset test test 192.168.68.95 +# Range: Test value not added to the set +1 ipset test test 2.0.1.0 +# Range: Try to add IP address +0 ipset add test 2.0.0.1 +# Range: List set +0 ipset list test | sed 's/timeout ./timeout x/' > .foo0 && ./sort.sh .foo0 +# Range: Check listing +0 diff -I 'Size in memory.*' .foo hash:net.t.list0 && rm .foo +# Sleep 6s so that element can time out +0 sleep 6 +# IP: List set +0 ipset -L test 2>/dev/null > .foo0 && ./sort.sh .foo0 +# IP: Check listing +0 diff -I 'Size in memory.*' .foo hash:net.t.list1 && rm .foo +# Flush test set +0 ipset flush test +# Delete test set +0 ipset destroy test +# eof diff --git a/tests/hash:net.t.list0 b/tests/hash:net.t.list0 new file mode 100644 index 0000000..b5cc00b --- /dev/null +++ b/tests/hash:net.t.list0 @@ -0,0 +1,11 @@ +Name: test +Type: hash:net +Header: family inet hashsize 128 maxelem 65536 timeout x +Size in memory: 1672 +References: 0 +Members: +1.1.1.1 timeout x +192.168.68.64/27 timeout x +2.0.0.0/24 timeout x +2.0.0.1 timeout x + diff --git a/tests/hash:net.t.list1 b/tests/hash:net.t.list1 new file mode 100644 index 0000000..7dec348 --- /dev/null +++ b/tests/hash:net.t.list1 @@ -0,0 +1,7 @@ +Name: test +Type: hash:net +Header: family inet hashsize 128 maxelem 65536 timeout 6 +Size in memory: 1672 +References: 0 +Members: + diff --git a/tests/hash:net6.t b/tests/hash:net6.t new file mode 100644 index 0000000..e4246d9 --- /dev/null +++ b/tests/hash:net6.t @@ -0,0 +1,49 @@ +# Create a set with timeout +0 ipset create test nethash family inet6 hashsize 128 timeout 6 +# Range: Add zero valued element +1 ipset add test ::/0 +# Range: Test zero valued element +1 ipset test test ::/0 +# Range: Delete zero valued element +1 ipset del test ::/0 +# Range: Try to add /0 +1 ipset add test 1:1:1::1/0 +# Range: Try to add /32 +0 ipset add test 1:1:1::1/128 +# Range: Add almost zero valued element +0 ipset add test 0:0:0::0/1 +# Range: Test almost zero valued element +0 ipset test test 0:0:0::0/1 +# Range: Delete almost zero valued element +0 ipset del test 0:0:0::0/1 +# Range: Test deleted element +1 ipset test test 0:0:0::0/1 +# Range: Delete element not added to the set +1 ipset del test 0:0:0::0/1 +# Range: Add first random network +0 ipset add test 2:0:0::1/24 +# Range: Add second random network +0 ipset add test 192:168:68::69/27 +# Range: Test first random value +0 ipset test test 2:0:0::255 +# Range: Test second random value +0 ipset test test 192:168:68::95 +# Range: Test value not added to the set +1 ipset test test 3:0:0::1 +# Range: Try to add IP address +0 ipset add test 3:0:0::1 +# Range: List set +0 ipset list test | sed 's/timeout ./timeout x/' > .foo0 && ./sort.sh .foo0 +# Range: Check listing +0 diff -I 'Size in memory.*' .foo hash:net6.t.list0 && rm .foo +# Sleep 6s so that element can time out +0 sleep 6 +# IP: List set +0 ipset -L test 2>/dev/null > .foo0 && ./sort.sh .foo0 +# IP: Check listing +0 diff -I 'Size in memory.*' .foo hash:net6.t.list1 && rm .foo +# Flush test set +0 ipset flush test +# Delete test set +0 ipset destroy test +# eof diff --git a/tests/hash:net6.t.list0 b/tests/hash:net6.t.list0 new file mode 100644 index 0000000..47a4663 --- /dev/null +++ b/tests/hash:net6.t.list0 @@ -0,0 +1,11 @@ +Name: test +Type: hash:net +Header: family inet6 hashsize 128 maxelem 65536 timeout x +Size in memory: 2696 +References: 0 +Members: +1:1:1::1 timeout x +192:160::/27 timeout x +2::/24 timeout x +3::1 timeout x + diff --git a/tests/hash:net6.t.list1 b/tests/hash:net6.t.list1 new file mode 100644 index 0000000..2a606bc --- /dev/null +++ b/tests/hash:net6.t.list1 @@ -0,0 +1,7 @@ +Name: test +Type: hash:net +Header: family inet6 hashsize 128 maxelem 65536 timeout 6 +Size in memory: 2696 +References: 0 +Members: + diff --git a/tests/iphash.t b/tests/iphash.t index 923931a..c912ad7 100644 --- a/tests/iphash.t +++ b/tests/iphash.t @@ -18,10 +18,14 @@ 0 ipset -A test 200.100.0.12 # IP: Delete the same value 0 ipset -D test 200.100.0.12 +# IP: Delete element not added to the set +1 ipset -D test 200.100.0.12 +# IP: Delete element not added to the set, ignoring error +0 ipset -! -D test 200.100.0.12 # IP: List set 0 ipset -L test 2>/dev/null > .foo0 && ./sort.sh .foo0 # IP: Check listing -0 diff .foo iphash.t.list0 && rm .foo +0 diff -I 'Size in memory.*' .foo iphash.t.list0 && rm .foo # IP: Flush test set 0 ipset -F test # IP: Delete test set @@ -62,10 +66,12 @@ 0 ipset -A test 200.100.0.12 # Network: Delete the same network 0 ipset -D test 200.100.0.12 +# Network: Delete element not added to the set +1 ipset -D test 200.100.0.12 # Network: List set 0 ipset -L test > .foo0 && ./sort.sh .foo0 # Network: Check listing -0 diff .foo iphash.t.list1 && rm .foo +0 diff -I 'Size in memory.*' .foo iphash.t.list1 && rm .foo # Network: Flush test set 0 ipset -F test # Network: Delete test set diff --git a/tests/iphash.t.list0 b/tests/iphash.t.list0 index 3f0c2fa..e2cc4ca 100644 --- a/tests/iphash.t.list0 +++ b/tests/iphash.t.list0 @@ -1,8 +1,7 @@ Name: test Type: hash:ip -Header: hashsize 128 maxelem 65536 probes 4 resize 50 -Elements: 2 -Size in memory: 512 +Header: family inet hashsize 128 maxelem 65536 +Size in memory: 1184 References: 0 Members: 192.168.68.69 diff --git a/tests/iphash.t.list1 b/tests/iphash.t.list1 index f32655a..ec9b493 100644 --- a/tests/iphash.t.list1 +++ b/tests/iphash.t.list1 @@ -1,8 +1,7 @@ Name: test Type: hash:ip -Header: hashsize 128 maxelem 65536 probes 4 resize 50 netmask 24 -Elements: 2 -Size in memory: 512 +Header: family inet hashsize 128 maxelem 65536 netmask 24 +Size in memory: 1184 References: 0 Members: 192.168.68.0 diff --git a/tests/ipmap.t b/tests/ipmap.t index c639e0b..00c35e8 100644 --- a/tests/ipmap.t +++ b/tests/ipmap.t @@ -33,7 +33,7 @@ # Range: Check listing 0 diff .foo ipmap.t.list0 && rm .foo # Range: Delete a range of elements -0 ipset -D test -x 2.0.0.128-2.0.0.132 +0 ipset -! -D test 2.0.0.128-2.0.0.132 # Range: List set 0 ipset -L test > .foo # Range: Check listing diff --git a/tests/ipmap.t.list0 b/tests/ipmap.t.list0 index 560eed1..8bf54c2 100644 --- a/tests/ipmap.t.list0 +++ b/tests/ipmap.t.list0 @@ -1,8 +1,7 @@ Name: test Type: bitmap:ip Header: range 2.0.0.1-2.1.0.0 -Elements: 6 -Size in memory: 8192 +Size in memory: 8232 References: 0 Members: 2.0.0.1 diff --git a/tests/ipmap.t.list1 b/tests/ipmap.t.list1 index 55556a7..4541cf5 100644 --- a/tests/ipmap.t.list1 +++ b/tests/ipmap.t.list1 @@ -1,8 +1,7 @@ Name: test Type: bitmap:ip Header: range 2.0.0.1-2.1.0.0 -Elements: 2 -Size in memory: 8192 +Size in memory: 8232 References: 0 Members: 2.0.0.1 diff --git a/tests/ipmap.t.list2 b/tests/ipmap.t.list2 index 9e1b65d..5f78ba6 100644 --- a/tests/ipmap.t.list2 +++ b/tests/ipmap.t.list2 @@ -1,8 +1,7 @@ Name: test Type: bitmap:ip Header: range 2.0.0.0-2.0.255.255 -Elements: 2 -Size in memory: 8192 +Size in memory: 8232 References: 0 Members: 2.0.0.0 diff --git a/tests/ipmap.t.list3 b/tests/ipmap.t.list3 index 4b2a45a..0acb3ee 100644 --- a/tests/ipmap.t.list3 +++ b/tests/ipmap.t.list3 @@ -1,8 +1,7 @@ Name: test Type: bitmap:ip Header: range 10.0.0.0-10.255.255.255 netmask 24 -Elements: 258 -Size in memory: 8192 +Size in memory: 8232 References: 0 Members: 10.0.0.0 diff --git a/tests/ipmap.t.list4 b/tests/ipmap.t.list4 index f498ba9..67a0c4f 100644 --- a/tests/ipmap.t.list4 +++ b/tests/ipmap.t.list4 @@ -1,8 +1,7 @@ Name: test Type: bitmap:ip Header: range 0.0.0.0-255.255.255.255 netmask 16 -Elements: 2 -Size in memory: 8192 +Size in memory: 8232 References: 0 Members: 0.0.0.0 diff --git a/tests/ipporthash.t b/tests/ipporthash.t index 7c622bb..dc35bcd 100644 --- a/tests/ipporthash.t +++ b/tests/ipporthash.t @@ -1,13 +1,15 @@ -# Range: Try to create from an invalid range -2 ipset -N test ipporthash --from 2.0.0.1 --to 2.1.0.1 -# Range: Create a set from a valid range +# Range: Create a set from a range (range ignored) 0 ipset -N test ipporthash --from 2.0.0.1 --to 2.1.0.0 -# Range: Add zero valued element -1 ipset -A test 2.0.0.1,0 -# Range: Test zero valued element -1 ipset -T test 2.0.0.1,0 -# Range: Delete zero valued element -1 ipset -D test 2.0.0.1,0 +# Range: Destroy set +0 ipset -X test +# Range: Create a set +0 ipset -N test ipporthash +# Range: Add partly zero valued element +0 ipset -A test 2.0.0.1,0 +# Range: Test partly zero valued element +0 ipset -T test 2.0.0.1,0 +# Range: Delete partly zero valued element +0 ipset -D test 2.0.0.1,0 # Range: Add lower boundary 0 ipset -A test 2.0.0.1,5 # Range: Add upper boundary @@ -18,27 +20,25 @@ 0 ipset -T test 2.1.0.0,128 # Range: Test value not added to the set 1 ipset -T test 2.0.0.1,4 -# Range: Test value not added to the set -1 ipset -T test 2.0.0.1,6 +# Range: Delete value not added to the set +1 ipset -D test 2.0.0.1,6 # Range: Test value before lower boundary 1 ipset -T test 2.0.0.0,5 # Range: Test value after upper boundary 1 ipset -T test 2.1.0.1,128 # Range: Try to add value before lower boundary -1 ipset -A test 2.0.0.0,5 +0 ipset -A test 2.0.0.0,5 # Range: Try to add value after upper boundary -1 ipset -A test 2.1.0.1,128 +0 ipset -A test 2.1.0.1,128 # Range: List set 0 ipset -L test > .foo0 && ./sort.sh .foo0 # Range: Check listing -0 diff .foo ipporthash.t.list0 && rm .foo +0 diff -I 'Size in memory.*' .foo ipporthash.t.list0 && rm .foo # Range: Flush test set 0 ipset -F test # Range: Delete test set 0 ipset -X test -# Network: Try to create a set from an invalid network -2 ipset -N test ipporthash --network 2.0.0.0/15 -# Network: Create a set from a valid network +# Network: Create a set from a network (network ignored) 0 ipset -N test ipporthash --network 2.0.0.0/16 # Network: Add lower boundary 0 ipset -A test 2.0.0.0,5 @@ -50,20 +50,20 @@ 0 ipset -T test 2.0.255.255,128 # Network: Test value not added to the set 1 ipset -T test 2.0.0.0,4 -# Network: Test value not added to the set -1 ipset -T test 2.0.0.0,6 +# Network: Delete value not added to the set +1 ipset -D test 2.0.0.0,6 # Network: Test value before lower boundary 1 ipset -T test 1.255.255.255,5 # Network: Test value after upper boundary 1 ipset -T test 2.1.0.0,128 # Network: Try to add value before lower boundary -1 ipset -A test 1.255.255.255,5 +0 ipset -A test 1.255.255.255,5 # Network: Try to add value after upper boundary -1 ipset -A test 2.1.0.0,128 +0 ipset -A test 2.1.0.0,128 # Network: List set 0 ipset -L test > .foo0 && ./sort.sh .foo0 # Network: Check listing -0 diff .foo ipporthash.t.list1 && rm .foo +0 diff -I 'Size in memory.*' .foo ipporthash.t.list1 && rm .foo # Network: Flush test set 0 ipset -F test # Network: Delete test set diff --git a/tests/ipporthash.t.list0 b/tests/ipporthash.t.list0 index b840a94..2e78ac4 100644 --- a/tests/ipporthash.t.list0 +++ b/tests/ipporthash.t.list0 @@ -1,7 +1,11 @@ Name: test -Type: ipporthash +Type: hash:ip,port +Header: family inet hashsize 1024 maxelem 65536 +Size in memory: 8464 References: 0 -Header: from: 2.0.0.1 to: 2.1.0.0 hashsize: 1024 probes: 8 resize: 50 Members: +2.0.0.0,5 2.0.0.1,5 2.1.0.0,128 +2.1.0.1,128 + diff --git a/tests/ipporthash.t.list1 b/tests/ipporthash.t.list1 index be86cda..e0f0da5 100644 --- a/tests/ipporthash.t.list1 +++ b/tests/ipporthash.t.list1 @@ -1,7 +1,11 @@ Name: test -Type: ipporthash +Type: hash:ip,port +Header: family inet hashsize 1024 maxelem 65536 +Size in memory: 8464 References: 0 -Header: from: 2.0.0.0 to: 2.0.255.255 hashsize: 1024 probes: 8 resize: 50 Members: +1.255.255.255,5 2.0.0.0,5 2.0.255.255,128 +2.1.0.0,128 + diff --git a/tests/ipportiphash.t b/tests/ipportiphash.t index 860d5a6..3f61947 100644 --- a/tests/ipportiphash.t +++ b/tests/ipportiphash.t @@ -1,13 +1,15 @@ -# Range: Try to create from an invalid range -2 ipset -N test ipportiphash --from 2.0.0.1 --to 2.1.0.1 -# Range: Create a set from a valid range +# Range: Create a set from a range (range ignored) 0 ipset -N test ipportiphash --from 2.0.0.1 --to 2.1.0.0 -# Range: Add zero valued element -1 ipset -A test 2.0.0.1,0,0.0.0.0 -# Range: Test zero valued element -1 ipset -T test 2.0.0.1,0,0.0.0.0 -# Range: Delete zero valued element -1 ipset -D test 2.0.0.1,0,0.0.0.0 +# Range: Destroy set +0 ipset -X test +# Range: Create a set +0 ipset -N test ipportiphash +# Range: Add partly zero valued element +0 ipset -A test 2.0.0.1,0,0.0.0.0 +# Range: Test partly zero valued element +0 ipset -T test 2.0.0.1,0,0.0.0.0 +# Range: Delete party zero valued element +0 ipset -D test 2.0.0.1,0,0.0.0.0 # Range: Add almost zero valued element 0 ipset -A test 2.0.0.1,0,0.0.0.1 # Range: Test almost zero valued element @@ -33,20 +35,18 @@ # Range: Test value after upper boundary 1 ipset -T test 2.1.0.1,128,2.2.2.2 # Range: Try to add value before lower boundary -1 ipset -A test 2.0.0.0,5,1.1.1.1 +0 ipset -A test 2.0.0.0,5,1.1.1.1 # Range: Try to add value after upper boundary -1 ipset -A test 2.1.0.1,128,2.2.2.2 +0 ipset -A test 2.1.0.1,128,2.2.2.2 # Range: List set 0 ipset -L test > .foo0 && ./sort.sh .foo0 # Range: Check listing -0 diff .foo ipportiphash.t.list0 && rm .foo +0 diff -I 'Size in memory.*' .foo ipportiphash.t.list0 && rm .foo # Range: Flush test set 0 ipset -F test # Range: Delete test set 0 ipset -X test -# Network: Try to create a set from an invalid network -2 ipset -N test ipportiphash --network 2.0.0.0/15 -# Network: Create a set from a valid network +# Network: Create a set from a valid network (network ignored) 0 ipset -N test ipportiphash --network 2.0.0.0/16 # Network: Add lower boundary 0 ipset -A test 2.0.0.0,5,1.1.1.1 @@ -65,13 +65,15 @@ # Network: Test value after upper boundary 1 ipset -T test 2.1.0.0,128,2.2.2.2 # Network: Try to add value before lower boundary -1 ipset -A test 1.255.255.255,5,1.1.1.1 -# Network: Try to add value after upper boundary -1 ipset -A test 2.1.0.0,128,2.2.2.2 +0 ipset -A test 1.255.255.255,5,1.1.1.1 +# Network: Try to test value before lower boundary +0 ipset -T test 1.255.255.255,5,1.1.1.1 +# Network: Try to del value before lower boundary +0 ipset -D test 1.255.255.255,5,1.1.1.1 # Network: List set 0 ipset -L test > .foo0 && ./sort.sh .foo0 # Network: Check listing -0 diff .foo ipportiphash.t.list1 && rm .foo +0 diff -I 'Size in memory.*' .foo ipportiphash.t.list1 && rm .foo # Network: Flush test set 0 ipset -F test # Network: Delete test set diff --git a/tests/ipportiphash.t.list0 b/tests/ipportiphash.t.list0 index acb1041..ba20b14 100644 --- a/tests/ipportiphash.t.list0 +++ b/tests/ipportiphash.t.list0 @@ -1,7 +1,11 @@ Name: test -Type: ipportiphash +Type: hash:ip,port,ip +Header: family inet hashsize 1024 maxelem 65536 +Size in memory: 8528 References: 0 -Header: from: 2.0.0.1 to: 2.1.0.0 hashsize: 1024 probes: 8 resize: 50 Members: +2.0.0.0,5,1.1.1.1 2.0.0.1,5,1.1.1.1 2.1.0.0,128,2.2.2.2 +2.1.0.1,128,2.2.2.2 + diff --git a/tests/ipportiphash.t.list1 b/tests/ipportiphash.t.list1 index 5545078..aca272a 100644 --- a/tests/ipportiphash.t.list1 +++ b/tests/ipportiphash.t.list1 @@ -1,7 +1,9 @@ Name: test -Type: ipportiphash +Type: hash:ip,port,ip +Header: family inet hashsize 1024 maxelem 65536 +Size in memory: 8416 References: 0 -Header: from: 2.0.0.0 to: 2.0.255.255 hashsize: 1024 probes: 8 resize: 50 Members: 2.0.0.0,5,1.1.1.1 2.0.255.255,128,2.2.2.2 + diff --git a/tests/ipportnethash.t b/tests/ipportnethash.t index 93369ef..6aff583 100644 --- a/tests/ipportnethash.t +++ b/tests/ipportnethash.t @@ -1,13 +1,11 @@ -# Range: Try to create from an invalid range -2 ipset -N test ipportnethash --from 2.0.0.1 --to 2.1.0.1 -# Range: Create a set from a valid range +# Range: Create a set from a range (range ignored) 0 ipset -N test ipportnethash --from 2.0.0.1 --to 2.1.0.0 # Range: Add zero valued element -2 ipset -A test 2.0.0.1,0,0.0.0.0/0 +1 ipset -A test 2.0.0.1,0,0.0.0.0/0 # Range: Test zero valued element -2 ipset -T test 2.0.0.1,0,0.0.0.0/0 +1 ipset -T test 2.0.0.1,0,0.0.0.0/0 # Range: Delete zero valued element -2 ipset -D test 2.0.0.1,0,0.0.0.0/0 +1 ipset -D test 2.0.0.1,0,0.0.0.0/0 # Range: Add almost zero valued element 0 ipset -A test 2.0.0.1,0,0.0.0.0/24 # Range: Test almost zero valued element @@ -33,20 +31,18 @@ # Range: Test value after upper boundary 1 ipset -T test 2.1.0.1,128,2.2.2.2 # Range: Try to add value before lower boundary -1 ipset -A test 2.0.0.0,5,1.1.1.1/24 +0 ipset -A test 2.0.0.0,5,1.1.1.1/24 # Range: Try to add value after upper boundary -1 ipset -A test 2.1.0.1,128,2.2.2.2/12 +0 ipset -A test 2.1.0.1,128,2.2.2.2/12 # Range: List set 0 ipset -L test > .foo0 && ./sort.sh .foo0 # Range: Check listing -0 diff .foo ipportnethash.t.list0 && rm .foo +0 diff -I 'Size in memory.*' .foo ipportnethash.t.list0 && rm .foo # Range: Flush test set 0 ipset -F test # Range: Delete test set 0 ipset -X test -# Network: Try to create a set from an invalid network -2 ipset -N test ipportnethash --network 2.0.0.0/15 -# Network: Create a set from a valid network +# Network: Create a set from a valid network (network ignored) 0 ipset -N test ipportnethash --network 2.0.0.0/16 # Network: Add lower boundary 0 ipset -A test 2.0.0.0,5,1.1.1.1/24 @@ -65,13 +61,13 @@ # Network: Test value after upper boundary 1 ipset -T test 2.1.0.0,128,2.2.2.2 # Network: Try to add value before lower boundary -1 ipset -A test 1.255.255.255,5,1.1.1.1/24 +0 ipset -A test 1.255.255.255,5,1.1.1.1/24 # Network: Try to add value after upper boundary -1 ipset -A test 2.1.0.0,128,2.2.2.2/12 +0 ipset -A test 2.1.0.0,128,2.2.2.2/12 # Network: List set 0 ipset -L test > .foo0 && ./sort.sh .foo0 # Network: Check listing -0 diff .foo ipportnethash.t.list1 && rm .foo +0 diff -I 'Size in memory.*' .foo ipportnethash.t.list1 && rm .foo # Network: Flush test set 0 ipset -F test # Network: Delete test set diff --git a/tests/ipportnethash.t.list0 b/tests/ipportnethash.t.list0 index 9727d20..60a0242 100644 --- a/tests/ipportnethash.t.list0 +++ b/tests/ipportnethash.t.list0 @@ -1,7 +1,11 @@ Name: test -Type: ipportnethash +Type: hash:ip,port,net +Header: family inet hashsize 1024 maxelem 65536 +Size in memory: 8776 References: 0 -Header: from: 2.0.0.1 to: 2.1.0.0 hashsize: 1024 probes: 8 resize: 50 Members: +2.0.0.0,5,1.1.1.0/24 2.0.0.1,5,1.1.1.0/24 2.1.0.0,128,2.0.0.0/12 +2.1.0.1,128,2.0.0.0/12 + diff --git a/tests/ipportnethash.t.list1 b/tests/ipportnethash.t.list1 index ad861d4..5d74105 100644 --- a/tests/ipportnethash.t.list1 +++ b/tests/ipportnethash.t.list1 @@ -1,7 +1,11 @@ Name: test -Type: ipportnethash +Type: hash:ip,port,net +Header: family inet hashsize 1024 maxelem 65536 +Size in memory: 8776 References: 0 -Header: from: 2.0.0.0 to: 2.0.255.255 hashsize: 1024 probes: 8 resize: 50 Members: +1.255.255.255,5,1.1.1.0/24 2.0.0.0,5,1.1.1.0/24 2.0.255.255,128,2.0.0.0/12 +2.1.0.0,128,2.0.0.0/12 + diff --git a/tests/iptree.t b/tests/iptree.t index 3cb0e00..b1e3678 100644 --- a/tests/iptree.t +++ b/tests/iptree.t @@ -13,7 +13,7 @@ # Static: Test value not added to the set 1 ipset -T test 192.168.68.70 # Static: List set -0 ipset -L test > .foo +0 ipset -L test > .foo0 && ./sort.sh .foo0 # Static: Check listing 0 diff .foo iptree.t.list0 && rm .foo # Static: Flush test set diff --git a/tests/iptree.t.list0 b/tests/iptree.t.list0 index 07433b9..f93546d 100644 --- a/tests/iptree.t.list0 +++ b/tests/iptree.t.list0 @@ -1,8 +1,9 @@ Name: test -Type: iptree +Type: hash:ip +Header: family inet hashsize 1024 maxelem 65536 +Size in memory: 8352 References: 0 -Header: Members: -2.0.0.1 192.168.68.69 +2.0.0.1 diff --git a/tests/iptreemap.t b/tests/iptreemap.t index 2e9fce7..0c36c3a 100644 --- a/tests/iptreemap.t +++ b/tests/iptreemap.t @@ -28,6 +28,8 @@ 1 ipset -T test 3.0.0.1 # Test the range: third 0 ipset -T test 3.0.0.2 +# Delete second random IP entry +0 ipset -D test 192.168.68.69 # Add a network block 0 ipset -A test 192.168.68.69/27 # Test the lower bound of the network @@ -47,7 +49,7 @@ # Test element after upper bound of deleted network 0 ipset -T test 192.168.68.72 # List set -0 ipset -L test > .foo +0 ipset -L test > .foo0 && ./sort.sh .foo0 # Check listing 0 diff .foo iptreemap.t.list0 && rm .foo # Flush test set diff --git a/tests/iptreemap.t.list0 b/tests/iptreemap.t.list0 index 6e783a0..15db1fe 100644 --- a/tests/iptreemap.t.list0 +++ b/tests/iptreemap.t.list0 @@ -1,11 +1,38 @@ Name: test -Type: iptreemap +Type: hash:ip +Header: family inet hashsize 1024 maxelem 65536 +Size in memory: 9048 References: 0 -Header: gc: 300 Members: +192.168.68.64 +192.168.68.65 +192.168.68.66 +192.168.68.67 +192.168.68.72 +192.168.68.73 +192.168.68.74 +192.168.68.75 +192.168.68.76 +192.168.68.77 +192.168.68.78 +192.168.68.79 +192.168.68.80 +192.168.68.81 +192.168.68.82 +192.168.68.83 +192.168.68.84 +192.168.68.85 +192.168.68.86 +192.168.68.87 +192.168.68.88 +192.168.68.89 +192.168.68.90 +192.168.68.91 +192.168.68.92 +192.168.68.93 +192.168.68.94 +192.168.68.95 2.0.0.1 3.0.0.0 3.0.0.2 -192.168.68.64-192.168.68.67 -192.168.68.72-192.168.68.95 diff --git a/tests/macipmap.t b/tests/macipmap.t index 277672d..90f09ba 100644 --- a/tests/macipmap.t +++ b/tests/macipmap.t @@ -20,6 +20,8 @@ 1 ipset -A test 2.0.0.0 # Range: Try to add value after upper boundary 1 ipset -A test 2.1.0.1 +# Range: Delete element not added to the set +1 ipset -D test 2.0.0.2 # Range: Try to add value with MAC 0 ipset -A test 2.0.0.2,00:11:22:33:44:55 # Range: Test value with invalid MAC @@ -28,6 +30,10 @@ 0 ipset -T test 2.0.0.2,00:11:22:33:44:55 # Range: Add MAC to already added element 0 ipset -A test 2.0.0.1,00:11:22:33:44:56 +# Range: Test value without supplying MAC +0 ipset -T test 2.0.0.1 +# Range: Test value with valid MAC +0 ipset -T test 2.0.0.1,00:11:22:33:44:56 # Range: Add an element in the middle 0 ipset -A test 2.0.200.214,00:11:22:33:44:57 # Range: Delete the same element @@ -62,6 +68,8 @@ 1 ipset -A test 1.255.255.255 # Network: Try to add value after upper boundary 1 ipset -A test 2.1.0.0 +# Network: Delete element not added to the set +1 ipset -D test 2.0.0.2 # Network: Try to add value with MAC 0 ipset -A test 2.0.0.2,00:11:22:33:44:55 # Network: Test value with invalid MAC @@ -110,8 +118,10 @@ 0 ipset -A test 2.0.200.214,00:11:22:33:44:57 # Range: Delete the same element 0 ipset -D test 2.0.200.214 +# Range: List set +0 ipset -L test | sed 's/timeout ./timeout x/' > .foo # Range: Check listing -0 ipset -L test | grep '2.0.0.2,00:11:22:33:44:55 timeout' >/dev/null +0 diff .foo macipmap.t.list3 && rm .foo # Range: wait 10s so that elements can timeout 0 sleep 10 # Range: List set diff --git a/tests/macipmap.t.list0 b/tests/macipmap.t.list0 index f34c882..9403224 100644 --- a/tests/macipmap.t.list0 +++ b/tests/macipmap.t.list0 @@ -1,8 +1,7 @@ Name: test Type: bitmap:ip,mac Header: range 2.0.0.1-2.1.0.0 -Elements: 3 -Size in memory: 458752 +Size in memory: 458864 References: 0 Members: 2.0.0.1,00:11:22:33:44:56 diff --git a/tests/macipmap.t.list1 b/tests/macipmap.t.list1 index cbdf127..3420abe 100644 --- a/tests/macipmap.t.list1 +++ b/tests/macipmap.t.list1 @@ -1,8 +1,7 @@ Name: test Type: bitmap:ip,mac Header: range 2.0.0.0-2.0.255.255 -Elements: 3 -Size in memory: 458752 +Size in memory: 458864 References: 0 Members: 2.0.0.0 diff --git a/tests/macipmap.t.list2 b/tests/macipmap.t.list2 index 9d0413e..6a727d8 100644 --- a/tests/macipmap.t.list2 +++ b/tests/macipmap.t.list2 @@ -1,8 +1,7 @@ Name: test Type: bitmap:ip,mac Header: range 2.0.0.1-2.1.0.0 timeout 10 -Elements: 1 -Size in memory: 1048576 +Size in memory: 1048688 References: 0 Members: 2.1.0.0 timeout 5 diff --git a/tests/macipmap.t.list3 b/tests/macipmap.t.list3 new file mode 100644 index 0000000..9f52f06 --- /dev/null +++ b/tests/macipmap.t.list3 @@ -0,0 +1,10 @@ +Name: test +Type: bitmap:ip,mac +Header: range 2.0.0.1-2.1.0.0 timeout x0 +Size in memory: 1048688 +References: 0 +Members: +2.0.0.1,00:11:22:33:44:56 timeout x +2.0.0.2,00:11:22:33:44:55 timeout x +2.1.0.0 timeout x + diff --git a/tests/nethash.t b/tests/nethash.t index cedcfde..400e110 100644 --- a/tests/nethash.t +++ b/tests/nethash.t @@ -1,37 +1,41 @@ # Create a set 0 ipset -N test nethash --hashsize 128 # Range: Add zero valued element -2 ipset -A test 0.0.0.0/0 +1 ipset -A test 0.0.0.0/0 # Range: Test zero valued element -2 ipset -T test 0.0.0.0/0 +1 ipset -T test 0.0.0.0/0 # Range: Delete zero valued element -2 ipset -D test 0.0.0.0/0 +1 ipset -D test 0.0.0.0/0 # Range: Try to add /0 -2 ipset -A test 1.1.1.1/0 +1 ipset -A test 1.1.1.1/0 # Range: Try to add /32 -2 ipset -A test 1.1.1.1/32 +0 ipset -A test 1.1.1.1/32 # Range: Add almost zero valued element 0 ipset -A test 0.0.0.0/1 # Range: Test almost zero valued element 0 ipset -T test 0.0.0.0/1 # Range: Delete almost zero valued element 0 ipset -D test 0.0.0.0/1 -# Range: Add first random network +# Range: Test deleted element +1 ipset -T test 0.0.0.0/1 +# Range: Delete element not added to the set +1 ipset -D test 0.0.0.0/1 +# Range: Add first random network 0 ipset -A test 2.0.0.1/24 -# Range: Add second random network +# Range: Add second random network 0 ipset -A test 192.168.68.69/27 -# Range: Test first random value +# Range: Test first random value 0 ipset -T test 2.0.0.255 -# Range: Test second random value +# Range: Test second random value 0 ipset -T test 192.168.68.95 -# Range: Test value not added to the set +# Range: Test value not added to the set 1 ipset -T test 2.0.1.0 -# Range: Try to add IP address -2 ipset -A test 2.0.0.1 +# Range: Try to add IP address +0 ipset -A test 2.0.0.1 # Range: List set 0 ipset -L test > .foo0 && ./sort.sh .foo0 # Range: Check listing -0 diff .foo nethash.t.list0 && rm .foo +0 diff -I 'Size in memory.*' .foo nethash.t.list0 && rm .foo # Flush test set 0 ipset -F test # Delete test set diff --git a/tests/nethash.t.list0 b/tests/nethash.t.list0 index 0b008b8..2809d79 100644 --- a/tests/nethash.t.list0 +++ b/tests/nethash.t.list0 @@ -1,7 +1,11 @@ Name: test -Type: nethash +Type: hash:net +Header: family inet hashsize 128 maxelem 65536 +Size in memory: 1544 References: 0 -Header: hashsize: 128 probes: 4 resize: 50 Members: +1.1.1.1 192.168.68.64/27 2.0.0.0/24 +2.0.0.1 + diff --git a/tests/portmap.t b/tests/portmap.t index 5e05849..b2ebf55 100644 --- a/tests/portmap.t +++ b/tests/portmap.t @@ -18,6 +18,8 @@ 1 ipset -A test 0 # Range: Try to add value after upper boundary 1 ipset -A test 1025 +# Range: Delete element not added to the set +1 ipset -D test 567 # Range: Add element in the middle 0 ipset -A test 567 # Range: Delete the same element @@ -66,6 +68,10 @@ 0 ipset -A test 567 # Full: Delete the same element 0 ipset -D test 567 +# Full: List set +0 ipset -L test | sed 's/timeout ./timeout x/' > .foo +# Full: Check listing +0 diff .foo portmap.t.list3 && rm .foo # Full: sleep 10s so that elements can timeout 0 sleep 10 # Full: List set diff --git a/tests/portmap.t.list0 b/tests/portmap.t.list0 index ed0b7d1..9934a75 100644 --- a/tests/portmap.t.list0 +++ b/tests/portmap.t.list0 @@ -1,8 +1,7 @@ Name: test Type: bitmap:port Header: range 1-1024 -Elements: 2 -Size in memory: 128 +Size in memory: 152 References: 0 Members: 1 diff --git a/tests/portmap.t.list1 b/tests/portmap.t.list1 index f67b128..4f13e32 100644 --- a/tests/portmap.t.list1 +++ b/tests/portmap.t.list1 @@ -1,8 +1,7 @@ Name: test Type: bitmap:port Header: range 0-65535 -Elements: 2 -Size in memory: 8192 +Size in memory: 8216 References: 0 Members: 0 diff --git a/tests/portmap.t.list3 b/tests/portmap.t.list3 new file mode 100644 index 0000000..b79cd1e --- /dev/null +++ b/tests/portmap.t.list3 @@ -0,0 +1,9 @@ +Name: test +Type: bitmap:port +Header: range 0-65535 timeout x +Size in memory: 524400 +References: 0 +Members: +0 timeout x +65535 timeout x + diff --git a/tests/runtest.sh b/tests/runtest.sh index a045905..ccd057e 100755 --- a/tests/runtest.sh +++ b/tests/runtest.sh @@ -1,22 +1,19 @@ #!/bin/bash tests="init" -tests="$tests ipmap bitmap:ip macipmap portmap" -tests="$tests iphash hash:ip" -# nethash ipporthash" -# tests="$tests ipportiphash ipportnethash" -# tests="$tests iptree iptreemap" -# tests="$tests setlist" +tests="$tests ipmap bitmap:ip" +tests="$tests macipmap portmap" +tests="$tests iphash hash:ip hash:ip6" +tests="$tests ipporthash hash:ip,port hash:ip6,port" +tests="$tests ipportiphash hash:ip,port,ip hash:ip6,port,ip6" +tests="$tests nethash hash:net hash:net6" +tests="$tests setlist" +tests="$tests iptree iptreemap" if [ "$1" ]; then tests="init $@" fi -if [ ! -x ../src/ipset ]; then - echo "Please rune `make` first and create the ipset binary." - exit 1 -fi - for types in $tests; do ../src/ipset -X test >/dev/null 2>&1 if [ -f $types ]; then diff --git a/tests/setlist.t b/tests/setlist.t index ed21396..f80f5ae 100644 --- a/tests/setlist.t +++ b/tests/setlist.t @@ -1,9 +1,15 @@ +# Setlist: Create dummy set +0 ipset -N dummy list:set # Setlist: Create base set foo 0 ipset -N foo ipmap --from 2.0.0.1 --to 2.1.0.0 # Setlist: Create base set bar 0 ipset -N bar iphash # Setlist: Create setlist kind of set 0 ipset -N test setlist +# Setlist: Swap test and dumy sets +0 ipset -W test dummy +# Setlist: Destroy dummy set +0 ipset -X dummy # Setlist: Add foo set to setlist 0 ipset -A test foo # Setlist: Test foo set in setlist diff --git a/tests/setlist.t.list0 b/tests/setlist.t.list0 index 9b70446..99f88fb 100644 --- a/tests/setlist.t.list0 +++ b/tests/setlist.t.list0 @@ -1,7 +1,8 @@ Name: test -Type: setlist +Type: list:set +Header: size 8 +Size in memory: 112 References: 0 -Header: size: 8 Members: foo bar diff --git a/tests/sort.sh b/tests/sort.sh index 907148a..ccdbc4e 100755 --- a/tests/sort.sh +++ b/tests/sort.sh @@ -1,6 +1,6 @@ #!/bin/sh -head -n 7 $1 > .foo -tail -n +8 $1 | grep '[[:alnum:]]' | sort >> .foo +head -n 6 $1 > .foo +tail -n +7 $1 | grep '[[:alnum:]]' | sort >> .foo echo >> .foo rm $1 -- cgit v1.2.3