From 0d32c5c070f817229110f92d7b31df9a3e4eeec5 Mon Sep 17 00:00:00 2001 From: Jozsef Kadlecsik Date: Sun, 24 Oct 2010 21:42:48 +0200 Subject: Fixes, cleanups, comments - More comments added to the code - ICMP and ICMPv6 support added to the hash:ip,port, hash:ip,port,ip and hash:ip,port,net types - hash:net and hash:ip,port,net types are reworked - hash:net,port type added - Wrong direction parameters fixed in hash:ip,port - Helps and manpage are updated - More tests added - Ugly macros are rewritten to functions in parse.c (Holger Eitzenberger) - resize related bug in hash types fixed (Holger Eitzenberger) - autoreconf patches by Jan Engelhardt applied - netlink patch minimalized: dumping can be initialized by a second parsing of the message (thanks to David and Patrick for the suggestion) - IPv4/IPv6 address attributes are introduced in order to fix the context (suggested by David) --- kernel/Kbuild | 2 +- kernel/include/linux/netfilter/ip_set.h | 96 +++- kernel/include/linux/netfilter/ip_set_bitmap.h | 2 + kernel/include/linux/netfilter/ip_set_chash.h | 186 ++++---- kernel/include/linux/netfilter/ip_set_getport.h | 62 ++- kernel/include/linux/netfilter/ip_set_hash.h | 6 +- kernel/include/linux/netfilter/ip_set_kernel.h | 7 - kernel/include/linux/netfilter/ip_set_list.h | 6 + kernel/include/linux/netfilter/ip_set_slist.h | 9 +- kernel/include/linux/netfilter/ip_set_timeout.h | 27 +- kernel/ip_set.c | 87 ++-- kernel/ip_set_bitmap_ip.c | 85 ++-- kernel/ip_set_bitmap_ipmac.c | 49 +- kernel/ip_set_bitmap_port.c | 13 +- kernel/ip_set_hash_ip.c | 51 +-- kernel/ip_set_hash_ipport.c | 97 ++-- kernel/ip_set_hash_ipportip.c | 121 ++--- kernel/ip_set_hash_ipportnet.c | 128 ++---- kernel/ip_set_hash_net.c | 51 +-- kernel/ip_set_hash_netport.c | 566 ++++++++++++++++++++++++ kernel/ip_set_list_set.c | 6 +- 21 files changed, 1154 insertions(+), 503 deletions(-) create mode 100644 kernel/ip_set_hash_netport.c (limited to 'kernel') diff --git a/kernel/Kbuild b/kernel/Kbuild index 9875d70..c496a93 100644 --- a/kernel/Kbuild +++ b/kernel/Kbuild @@ -6,7 +6,7 @@ EXTRA_CFLAGS := -I$(M)/include \ 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 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_hash_net.o ip_set_hash_ipportnet.o ip_set_hash_netport.o obj-m += ip_set_list_set.o # It's for me... diff --git a/kernel/include/linux/netfilter/ip_set.h b/kernel/include/linux/netfilter/ip_set.h index 1c41396..8abf8f8 100644 --- a/kernel/include/linux/netfilter/ip_set.h +++ b/kernel/include/linux/netfilter/ip_set.h @@ -52,7 +52,7 @@ enum { IPSET_ATTR_PROTOCOL, /* 1: Protocol version */ IPSET_ATTR_SETNAME, /* 2: Name of the set */ IPSET_ATTR_TYPENAME, /* 3: Typename */ - IPSET_ATTR_SETNAME2 = IPSET_ATTR_TYPENAME, /* rename/swap */ + IPSET_ATTR_SETNAME2 = IPSET_ATTR_TYPENAME, /* Setname at rename/swap */ IPSET_ATTR_REVISION, /* 4: Settype revision */ IPSET_ATTR_FAMILY, /* 5: Settype family */ IPSET_ATTR_FLAGS, /* 6: Flags at command level */ @@ -77,7 +77,7 @@ enum { IPSET_ATTR_TIMEOUT, /* 6 */ IPSET_ATTR_PROTO, /* 7 */ IPSET_ATTR_CADT_FLAGS, /* 8 */ - IPSET_ATTR_CADT_LINENO = IPSET_ATTR_LINENO, + IPSET_ATTR_CADT_LINENO = IPSET_ATTR_LINENO, /* 9 */ /* Reserve empty slots */ IPSET_ATTR_CADT_MAX = 16, /* Create-only specific attributes */ @@ -108,6 +108,14 @@ enum { }; #define IPSET_ATTR_ADT_MAX (__IPSET_ATTR_ADT_MAX - 1) +/* IP specific attributes */ +enum { + IPSET_ATTR_IPADDR_IPV4 = IPSET_ATTR_UNSPEC + 1, + IPSET_ATTR_IPADDR_IPV6, + __IPSET_ATTR_IPADDR_MAX, +}; +#define IPSET_ATTR_IPADDR_MAX (__IPSET_ATTR_IPADDR_MAX - 1) + /* Error codes */ enum ipset_errno { IPSET_ERR_PRIVATE = 128, @@ -123,16 +131,20 @@ enum ipset_errno { IPSET_ERR_INVALID_FAMILY, IPSET_ERR_TIMEOUT, IPSET_ERR_REFERENCED, + IPSET_ERR_IPADDR_IPV4, + IPSET_ERR_IPADDR_IPV6, /* Type specific error codes */ IPSET_ERR_TYPE_SPECIFIC = 160, }; +/* Flags at command level */ enum ipset_cmd_flags { IPSET_FLAG_BIT_EXIST = 0, IPSET_FLAG_EXIST = (1 << IPSET_FLAG_BIT_EXIST), }; +/* Flags at CADT attribute level */ enum ipset_cadt_flags { IPSET_FLAG_BIT_BEFORE = 0, IPSET_FLAG_BEFORE = (1 << IPSET_FLAG_BIT_BEFORE), @@ -148,9 +160,6 @@ enum ipset_adt { IPSET_CADT_MAX, }; -#define IPSET_IPPROTO_ANY 255 -#define IPSET_IPPROTO_TCPUDP 254 - #ifdef __KERNEL__ #include #include @@ -196,7 +205,8 @@ 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 */ + /* Strictly speaking not a feature, but a flag for dumping: + * this settype must be dumped last */ IPSET_DUMP_LAST_FLAG = 7, IPSET_DUMP_LAST = (1 << IPSET_DUMP_LAST_FLAG), }; @@ -223,7 +233,7 @@ struct ip_set_type_variant { int (*uadt)(struct ip_set *set, struct nlattr *head, int len, enum ipset_adt adt, u32 *lineno, u32 flags); - /* Low level add/del/test entries */ + /* Low level add/del/test functions */ ipset_adtfn adt[IPSET_ADT_MAX]; /* When adding entries and set is full, try to resize the set */ @@ -241,7 +251,7 @@ struct ip_set_type_variant { struct netlink_callback *cb); /* Return true if "b" set is the same as "a" - * according to the set parameters */ + * according to the create set parameters */ bool (*same_set)(const struct ip_set *a, const struct ip_set *b); }; @@ -285,7 +295,7 @@ struct ip_set { const struct ip_set_type *type; /* The type variant doing the real job */ const struct ip_set_type_variant *variant; - /* The actual INET family */ + /* The actual INET family of the set */ u8 family; /* The type specific data */ void *data; @@ -340,6 +350,7 @@ ip_set_free(void *members) kfree(members); } +/* Ignore IPSET_ERR_EXIST errors if asked to do so? */ static inline bool ip_set_eexist(int ret, u32 flags) { @@ -379,6 +390,52 @@ ip_set_get_n16(const struct nlattr *attr) return attr->nla_type & NLA_F_NET_BYTEORDER ? value : htons(value); } +static const struct nla_policy +ipaddr_policy[IPSET_ATTR_IPADDR_MAX + 1] __read_mostly = { + [IPSET_ATTR_IPADDR_IPV4] = { .type = NLA_U32 }, + [IPSET_ATTR_IPADDR_IPV6] = { .type = NLA_BINARY, + .len = sizeof(struct in6_addr) }, +}; + +static inline int +ip_set_get_ipaddr4(struct nlattr *attr[], int type, u32 *ipaddr) +{ + struct nlattr *tb[IPSET_ATTR_IPADDR_MAX+1] = {}; + + if (!attr[type]) + return -IPSET_ERR_PROTOCOL; + + if (nla_parse(tb, IPSET_ATTR_IPADDR_MAX, + nla_data(attr[type]), nla_len(attr[type]), + ipaddr_policy)) + return -IPSET_ERR_PROTOCOL; + if (!tb[IPSET_ATTR_IPADDR_IPV4]) + return -IPSET_ERR_IPADDR_IPV4; + + *ipaddr = ip_set_get_n32(tb[IPSET_ATTR_IPADDR_IPV4]); + return 0; +} + +static inline int +ip_set_get_ipaddr6(struct nlattr *attr[], int type, union nf_inet_addr *ipaddr) +{ + struct nlattr *tb[IPSET_ATTR_IPADDR_MAX+1] = {}; + + if (!attr[type]) + return -IPSET_ERR_PROTOCOL; + + if (nla_parse(tb, IPSET_ATTR_IPADDR_MAX, + nla_data(attr[type]), nla_len(attr[type]), + ipaddr_policy)) + return -IPSET_ERR_PROTOCOL; + if (!tb[IPSET_ATTR_IPADDR_IPV6]) + return -IPSET_ERR_IPADDR_IPV6; + + memcpy(ipaddr, nla_data(tb[IPSET_ATTR_IPADDR_IPV6]), + sizeof(struct in6_addr)); + return 0; +} + #define ipset_nest_start(skb, attr) nla_nest_start(skb, attr | NLA_F_NESTED) #define ipset_nest_end(skb, start) nla_nest_end(skb, start) @@ -388,6 +445,27 @@ ip_set_get_n16(const struct nlattr *attr) #define NLA_PUT_NET16(skb, type, value) \ NLA_PUT_BE16(skb, type | NLA_F_NET_BYTEORDER, value) +#define NLA_PUT_IPADDR4(skb, type, ipaddr) \ +do { \ + struct nlattr *__nested = ipset_nest_start(skb, type); \ + \ + if (!__nested) \ + goto nla_put_failure; \ + NLA_PUT_NET32(skb, IPSET_ATTR_IPADDR_IPV4, ipaddr); \ + ipset_nest_end(skb, __nested); \ +} while (0) + +#define NLA_PUT_IPADDR6(skb, type, ipaddrptr) \ +do { \ + struct nlattr *__nested = ipset_nest_start(skb, type); \ + \ + if (!__nested) \ + goto nla_put_failure; \ + NLA_PUT(skb, IPSET_ATTR_IPADDR_IPV6, \ + sizeof(struct in6_addr), ipaddrptr); \ + ipset_nest_end(skb, __nested); \ +} while (0) + /* Get address from skbuff */ static inline u32 ip4addr(const struct sk_buff *skb, bool src) diff --git a/kernel/include/linux/netfilter/ip_set_bitmap.h b/kernel/include/linux/netfilter/ip_set_bitmap.h index 0d067d0..f3bff2c 100644 --- a/kernel/include/linux/netfilter/ip_set_bitmap.h +++ b/kernel/include/linux/netfilter/ip_set_bitmap.h @@ -3,7 +3,9 @@ /* Bitmap type specific error codes */ enum { + /* The element is out of the range of the set */ IPSET_ERR_BITMAP_RANGE = IPSET_ERR_TYPE_SPECIFIC, + /* The range exceeds the size limit of the set type */ IPSET_ERR_BITMAP_RANGE_SIZE, }; diff --git a/kernel/include/linux/netfilter/ip_set_chash.h b/kernel/include/linux/netfilter/ip_set_chash.h index 5e615e4..6fd1d32 100644 --- a/kernel/include/linux/netfilter/ip_set_chash.h +++ b/kernel/include/linux/netfilter/ip_set_chash.h @@ -5,13 +5,11 @@ #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. +/* Cacheline 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 - that field + * is ignored when computing the hash key. */ /* Number of elements to store in an array block */ @@ -19,9 +17,10 @@ /* Number of arrays: max ARRAY_SIZE * CHAIN_LIMIT "long" chains */ #define CHASH_DEFAULT_CHAIN_LIMIT 3 +/* Book-keeping of the prefixes added to the set */ struct chash_nets { + u8 cidr; /* the different cidr values in the set */ u32 nets; /* number of elements per cidr */ - u8 cidr; /* the cidr values added to the set */ }; struct chash { @@ -37,14 +36,12 @@ struct chash { #ifdef IP_SET_HASH_WITH_NETMASK u8 netmask; /* netmask value for subnets to store */ #endif -#ifdef IP_SET_HASH_WITH_PROTO - u8 proto; /* default protocol for SET target */ -#endif #ifdef IP_SET_HASH_WITH_NETS - struct chash_nets nets[0]; /* book keeping of networks */ + struct chash_nets nets[0]; /* book-keeping of prefixes */ #endif }; +/* Compute htable_bits from the user input parameter hashsize */ static inline u8 htable_bits(u32 hashsize) { @@ -57,34 +54,56 @@ htable_bits(u32 hashsize) return bits; } +#ifdef IP_SET_HASH_WITH_NETS + +#define SET_HOST_MASK(family) (family == AF_INET ? 32 : 128) + +/* Network cidr size book keeping when the hash stores different + * sized networks */ static inline void -add_cidr(struct chash_nets *nets, u8 host_mask, u8 cidr) +add_cidr(struct chash *h, u8 cidr, u8 host_mask) { u8 i; - pr_debug("add_cidr %u", cidr); - for (i = 0; i < host_mask - 1 && nets[i].cidr; i++) { + ++h->nets[cidr-1].nets; + + pr_debug("add_cidr added %u: %u", cidr, h->nets[cidr-1].nets); + + if (h->nets[cidr-1].nets > 1) + return; + + /* New cidr size */ + for (i = 0; i < host_mask && h->nets[i].cidr; i++) { /* Add in increasing prefix order, so larger cidr first */ - if (nets[i].cidr < cidr) - swap(nets[i].cidr, cidr); + if (h->nets[i].cidr < cidr) + swap(h->nets[i].cidr, cidr); } - if (i < host_mask - 1) - nets[i].cidr = cidr; + if (i < host_mask) + h->nets[i].cidr = cidr; } static inline void -del_cidr(struct chash_nets *nets, u8 host_mask, u8 cidr) +del_cidr(struct chash *h, u8 cidr, u8 host_mask) { 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; + --h->nets[cidr-1].nets; + + pr_debug("del_cidr deleted %u: %u", cidr, h->nets[cidr-1].nets); + + if (h->nets[cidr-1].nets != 0) + return; + + /* All entries with this cidr size deleted, so cleanup h->cidr[] */ + for (i = 0; i < host_mask - 1 && h->nets[i].cidr; i++) { + if (h->nets[i].cidr == cidr) + h->nets[i].cidr = cidr = h->nets[i+1].cidr; } - nets[host_mask - 2].cidr = 0; + h->nets[i - 1].cidr = 0; } +#endif +/* Destroy the hashtable part of the set */ static void chash_destroy(struct slist *t, u8 htable_bits) { @@ -93,12 +112,13 @@ chash_destroy(struct slist *t, u8 htable_bits) for (i = 0; i < jhash_size(htable_bits); i++) slist_for_each_safe(n, tmp, &t[i]) - /* FIXME: slab cache */ + /* FIXME: use slab cache */ kfree(n); ip_set_free(t); } +/* Calculate the actual memory size of the set data */ static size_t chash_memsize(const struct chash *h, size_t dsize, u8 host_mask) { @@ -106,7 +126,7 @@ chash_memsize(const struct chash *h, size_t dsize, u8 host_mask) u32 i; size_t memsize = sizeof(*h) #ifdef IP_SET_HASH_WITH_NETS - + sizeof(struct chash_nets) * (host_mask - 1) + + sizeof(struct chash_nets) * host_mask #endif + jhash_size(h->htable_bits) * sizeof(struct slist); @@ -118,6 +138,7 @@ chash_memsize(const struct chash *h, size_t dsize, u8 host_mask) return memsize; } +/* Flush a hash type of set: destroy all elements */ static void ip_set_hash_flush(struct ip_set *set) { @@ -133,11 +154,12 @@ ip_set_hash_flush(struct ip_set *set) } #ifdef IP_SET_HASH_WITH_NETS memset(h->nets, 0, sizeof(struct chash_nets) - * (set->family == AF_INET ? 31 : 127)); + * SET_HOST_MASK(set->family)); #endif h->elements = 0; } +/* Destroy a hash type of set */ static void ip_set_hash_destroy(struct ip_set *set) { @@ -152,12 +174,15 @@ ip_set_hash_destroy(struct ip_set *set) set->data = NULL; } -#define JHASH2(data, initval, htable_bits) \ -jhash2((u32 *)(data), sizeof(struct type_pf_elem)/sizeof(u32), initval) \ - & jhash_mask(htable_bits) +#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 */ +#define CONCAT(a, b, c) a##b##c +#define TOKEN(a, b, c) CONCAT(a, b, c) + /* Type/family dependent function prototypes */ #define type_pf_data_equal TOKEN(TYPE, PF, _data_equal) @@ -208,10 +233,13 @@ jhash2((u32 *)(data), sizeof(struct type_pf_elem)/sizeof(u32), initval) \ /* Flavour without timeout */ +/* Get the ith element from the array block n */ #define chash_data(n, i) \ (struct type_pf_elem *)((char *)(n) + sizeof(struct slist) \ + (i)*sizeof(struct type_pf_elem)) +/* Add an element to the hash table when resizing the set: + * we spare the maintenance of the internal counters. */ 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) @@ -240,7 +268,7 @@ type_pf_chash_readd(struct chash *h, struct slist *t, u8 htable_bits, prev->next = (struct slist *) tmp; data = chash_data(tmp, 0); } else { - /* Rehashing */ + /* Trigger rehashing */ return -EAGAIN; } found: @@ -248,13 +276,16 @@ found: return 0; } +/* Delete an element from the hash table: swap it with the last + * element in the hash bucket and free up the array if it was + * completely emptied */ 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; + int j; /* Index in array */ if (n->next != NULL) { for (prev = n, tmp = n->next; @@ -276,8 +307,7 @@ type_pf_chash_del_elem(struct chash *h, struct slist *prev, 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); + del_cidr(h, data->cidr, HOST_MASK); #endif if (j == 0) { prev->next = NULL; @@ -288,6 +318,9 @@ type_pf_chash_del_elem(struct chash *h, struct slist *prev, h->elements--; } +/* Resize a hash: create a new hash table with doubling the hashsize + * and inserting the elements to it. Repeat until we succeed or + * fail due to memory pressures. */ static int type_pf_resize(struct ip_set *set, gfp_t gfp_flags, bool retried) { @@ -299,7 +332,7 @@ type_pf_resize(struct ip_set *set, gfp_t gfp_flags, bool retried) int ret; retry: - ret = 0; + ret = i = 0; htable_bits++; if (!htable_bits) /* In case we have plenty of memory :-) */ @@ -310,8 +343,8 @@ retry: return -ENOMEM; write_lock_bh(&set->lock); - for (i = 0; i < jhash_size(h->htable_bits); i++) { next_slot: + for (; i < jhash_size(h->htable_bits); i++) { slist_for_each(n, &h->htable[i]) { for (j = 0; j < h->array_size; j++) { data = chash_data(n, j); @@ -344,6 +377,8 @@ next_slot: return 0; } +/* Add an element to a hash and update the internal counters when succeeded, + * otherwise report the proper error code. */ static int type_pf_chash_add(struct ip_set *set, void *value, gfp_t gfp_flags, u32 timeout) @@ -356,11 +391,7 @@ type_pf_chash_add(struct ip_set *set, void *value, 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); @@ -390,13 +421,13 @@ type_pf_chash_add(struct ip_set *set, void *value, 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); + add_cidr(h, d->cidr, HOST_MASK); #endif h->elements++; return 0; } +/* Delete an element from the hash */ static int type_pf_chash_del(struct ip_set *set, void *value, gfp_t gfp_flags, u32 timeout) @@ -423,6 +454,9 @@ type_pf_chash_del(struct ip_set *set, void *value, } #ifdef IP_SET_HASH_WITH_NETS + +/* Special test function which takes into account the different network + * sizes added to the set */ static inline int type_pf_chash_test_cidrs(struct ip_set *set, struct type_pf_elem *d, @@ -433,11 +467,11 @@ type_pf_chash_test_cidrs(struct ip_set *set, const struct type_pf_elem *data; int i, j = 0; u32 hash; - u8 host_mask = set->family == AF_INET ? 32 : 128; + u8 host_mask = SET_HOST_MASK(set->family); retry: pr_debug("test by nets"); - for (; j < host_mask - 1 && h->nets[j].cidr; j++) { + for (; j < host_mask && 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]) @@ -455,6 +489,7 @@ retry: } #endif +/* Test whether the element is added to the set */ static inline int type_pf_chash_test(struct ip_set *set, void *value, gfp_t gfp_flags, u32 timeout) @@ -465,10 +500,11 @@ type_pf_chash_test(struct ip_set *set, void *value, 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) +#ifdef IP_SET_HASH_WITH_NETS + /* If we test an IP address and not a network address, + * try all possible network sizes */ + if (d->cidr == SET_HOST_MASK(set->family)) return type_pf_chash_test_cidrs(set, d, gfp_flags, timeout); #endif @@ -484,6 +520,7 @@ type_pf_chash_test(struct ip_set *set, void *value, return 0; } +/* Reply a HEADER request: fill out the header part of the set */ static int type_pf_head(struct ip_set *set, struct sk_buff *skb) { @@ -507,10 +544,6 @@ type_pf_head(struct ip_set *set, struct sk_buff *skb) #ifdef IP_SET_HASH_WITH_NETMASK if (h->netmask != HOST_MASK) NLA_PUT_U8(skb, IPSET_ATTR_NETMASK, h->netmask); -#endif -#ifdef IP_SET_HASH_WITH_PROTO - if (h->proto != IPSET_IPPROTO_TCPUDP) - NLA_PUT_U8(skb, IPSET_ATTR_PROTO, h->proto); #endif NLA_PUT_NET32(skb, IPSET_ATTR_REFERENCES, htonl(atomic_read(&set->ref) - 1)); @@ -524,6 +557,7 @@ nla_put_failure: return -EFAULT; } +/* Reply a LIST/SAVE request: dump the elements of the specified set */ static int type_pf_list(struct ip_set *set, struct sk_buff *skb, struct netlink_callback *cb) @@ -599,7 +633,8 @@ static const struct ip_set_type_variant type_pf_variant __read_mostly = { /* Flavour with timeout support */ #define chash_tdata(n, i) \ -(struct type_pf_elem *)((char *)(n) + sizeof(struct slist) + (i)*sizeof(struct type_pf_telem)) +(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) @@ -666,7 +701,7 @@ type_pf_chash_treadd(struct chash *h, struct slist *t, u8 htable_bits, prev->next = (struct slist *) tmp; data = chash_tdata(tmp, 0); } else { - /* Rehashing */ + /* Trigger rehashing */ return -EAGAIN; } found: @@ -681,7 +716,7 @@ type_pf_chash_del_telem(struct chash *h, struct slist *prev, { struct type_pf_elem *d, *data = chash_tdata(n, i); struct slist *tmp; - int j; + int j; /* Index in array */ pr_debug("del %u", i); if (n->next != NULL) { @@ -706,8 +741,7 @@ type_pf_chash_del_telem(struct chash *h, struct slist *prev, 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); + del_cidr(h, data->cidr, HOST_MASK); #endif if (j == 0) { prev->next = NULL; @@ -718,6 +752,7 @@ type_pf_chash_del_telem(struct chash *h, struct slist *prev, h->elements--; } +/* Delete expired elements from the hashtable */ static void type_pf_chash_expire(struct chash *h) { @@ -760,7 +795,7 @@ type_pf_tresize(struct ip_set *set, gfp_t gfp_flags, bool retried) } retry: - ret = 0; + ret = i = 0; htable_bits++; if (!htable_bits) /* In case we have plenty of memory :-) */ @@ -771,8 +806,8 @@ retry: return -ENOMEM; write_lock_bh(&set->lock); - for (i = 0; i < jhash_size(h->htable_bits); i++) { next_slot: + for (; i < jhash_size(h->htable_bits); i++) { slist_for_each(n, &h->htable[i]) { for (j = 0; j < h->array_size; j++) { data = chash_tdata(n, j); @@ -781,8 +816,8 @@ next_slot: goto next_slot; } ret = type_pf_chash_treadd(h, t, htable_bits, - data, gfp_flags, - type_pf_data_timeout(data)); + data, gfp_flags, + type_pf_data_timeout(data)); if (ret < 0) { write_unlock_bh(&set->lock); chash_destroy(t, htable_bits); @@ -821,11 +856,7 @@ type_pf_chash_tadd(struct ip_set *set, void *value, 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); @@ -854,17 +885,14 @@ type_pf_chash_tadd(struct ip_set *set, void *value, return -EAGAIN; } found: - if (type_pf_data_isnull(data)) { + 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); + else + del_cidr(h, data->cidr, HOST_MASK); + + add_cidr(h, d->cidr, HOST_MASK); #endif - } type_pf_data_copy(data, d); type_pf_data_timeout_set(data, timeout); return 0; @@ -908,10 +936,10 @@ type_pf_chash_ttest_cidrs(struct ip_set *set, struct slist *n; int i, j = 0; u32 hash; - u8 host_mask = set->family == AF_INET ? 32 : 128; + u8 host_mask = SET_HOST_MASK(set->family); retry: - for (; j < host_mask - 1 && h->nets[j].cidr; j++) { + for (; j < host_mask && 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]) @@ -938,10 +966,9 @@ type_pf_chash_ttest(struct ip_set *set, void *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) +#ifdef IP_SET_HASH_WITH_NETS + if (d->cidr == SET_HOST_MASK(set->family)) return type_pf_chash_ttest_cidrs(set, d, gfp_flags, timeout); #endif @@ -1048,7 +1075,8 @@ type_pf_gc_init(struct ip_set *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)); + pr_debug("gc initialized, run in every %u", + IPSET_GC_PERIOD(h->timeout)); } #undef type_pf_data_equal diff --git a/kernel/include/linux/netfilter/ip_set_getport.h b/kernel/include/linux/netfilter/ip_set_getport.h index cf150d3..e4d469d 100644 --- a/kernel/include/linux/netfilter/ip_set_getport.h +++ b/kernel/include/linux/netfilter/ip_set_getport.h @@ -2,13 +2,14 @@ #define _IP_SET_GETPORT_H #ifdef __KERNEL__ +#include +#include #include #include #define IPSET_INVALID_PORT 65536 /* We must handle non-linear skbs */ - static inline bool get_port(const struct sk_buff *skb, int protocol, unsigned int protooff, bool src, u16 *port, u8 *proto) @@ -38,13 +39,32 @@ get_port(const struct sk_buff *skb, int protocol, unsigned int protooff, *port = src ? uh->source : uh->dest; break; } - default: - if (*proto == IPSET_IPPROTO_TCPUDP) + case IPPROTO_ICMP: { + struct icmphdr _icmph; + const struct icmphdr *ic; + + ic = skb_header_pointer(skb, protooff, sizeof(_icmph), &_icmph); + if (ic == NULL) + return false; + + *port = (ic->type << 8) & ic->code; + break; + } + case IPPROTO_ICMPV6: { + struct icmp6hdr _icmph; + const struct icmp6hdr *ic; + + ic = skb_header_pointer(skb, protooff, sizeof(_icmph), &_icmph); + if (ic == NULL) return false; + + *port = (ic->icmp6_type << 8) & ic->icmp6_code; + break; + } + default: break; } - if (*proto != IPSET_IPPROTO_TCPUDP) - *proto = protocol; + *proto = protocol; return true; } @@ -56,9 +76,6 @@ get_ip4_port(const struct sk_buff *skb, bool src, u16 *port, u8 *proto) unsigned int protooff = ip_hdrlen(skb); int protocol = iph->protocol; - if (!(*proto >= IPSET_IPPROTO_TCPUDP || *proto == protocol)) - return false; - /* See comments at tcp_match in ip_tables.c */ if (ntohs(iph->frag_off) & IP_OFFSET) return false; @@ -77,21 +94,32 @@ get_ip6_port(const struct sk_buff *skb, bool src, u16 *port, u8 *proto) if (protocol < 0 || fragoff) return false; - if (!(*proto >= IPSET_IPPROTO_TCPUDP || *proto == protocol)) - return false; - return get_port(skb, protocol, protooff, src, port, proto); } static inline bool get_ip_port(const struct sk_buff *skb, u8 pf, bool src, u16 *port) { - u8 proto = IPSET_IPPROTO_TCPUDP; - - if (pf == AF_INET) - return get_ip4_port(skb, src, port, &proto); - else - return get_ip6_port(skb, src, port, &proto); + bool ret; + u8 proto; + + switch (pf) { + case AF_INET: + ret = get_ip4_port(skb, src, port, &proto); + case AF_INET6: + ret = get_ip6_port(skb, src, port, &proto); + default: + return false; + } + if (!ret) + return ret; + switch (proto) { + case IPPROTO_TCP: + case IPPROTO_UDP: + return true; + default: + return false; + } } #endif /* __KERNEL__ */ diff --git a/kernel/include/linux/netfilter/ip_set_hash.h b/kernel/include/linux/netfilter/ip_set_hash.h index 4003af0..e149a2b 100644 --- a/kernel/include/linux/netfilter/ip_set_hash.h +++ b/kernel/include/linux/netfilter/ip_set_hash.h @@ -1,11 +1,15 @@ #ifndef __IP_SET_HASH_H #define __IP_SET_HASH_H -/* Bitmap type specific error codes */ +/* Hash type specific error codes */ enum { + /* Hash is full */ IPSET_ERR_HASH_FULL = IPSET_ERR_TYPE_SPECIFIC, + /* Null-valued element */ IPSET_ERR_HASH_ELEM, + /* Invalid protocol */ IPSET_ERR_INVALID_PROTO, + /* Protocol missing but must be specified */ IPSET_ERR_MISSING_PROTO, }; diff --git a/kernel/include/linux/netfilter/ip_set_kernel.h b/kernel/include/linux/netfilter/ip_set_kernel.h index 0f04217..d770589 100644 --- a/kernel/include/linux/netfilter/ip_set_kernel.h +++ b/kernel/include/linux/netfilter/ip_set_kernel.h @@ -1,13 +1,6 @@ #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__ #ifdef CONFIG_DEBUG_KERNEL diff --git a/kernel/include/linux/netfilter/ip_set_list.h b/kernel/include/linux/netfilter/ip_set_list.h index c40643e..9988570 100644 --- a/kernel/include/linux/netfilter/ip_set_list.h +++ b/kernel/include/linux/netfilter/ip_set_list.h @@ -3,11 +3,17 @@ /* List type specific error codes */ enum { + /* Set name to be added/deleted/tested does not exist. */ IPSET_ERR_NAME = IPSET_ERR_TYPE_SPECIFIC, + /* list:set type is not permitted to add */ IPSET_ERR_LOOP, + /* Missing reference set */ IPSET_ERR_BEFORE, + /* Reference set does not exist */ IPSET_ERR_NAMEREF, + /* Set is full */ IPSET_ERR_LIST_FULL, + /* Reference set is not added to the set */ IPSET_ERR_REF_EXIST, }; diff --git a/kernel/include/linux/netfilter/ip_set_slist.h b/kernel/include/linux/netfilter/ip_set_slist.h index abc5afe..3e8d8b0 100644 --- a/kernel/include/linux/netfilter/ip_set_slist.h +++ b/kernel/include/linux/netfilter/ip_set_slist.h @@ -25,7 +25,8 @@ struct slist { pos = pos->next) #define slist_for_each_prev(prev, pos, head) \ - for (prev = head, pos = (head)->next; pos && ({ prefetch(pos->next); 1; }); \ + for (prev = head, pos = (head)->next; \ + pos && ({ prefetch(pos->next); 1; }); \ prev = pos, pos = pos->next) #define slist_for_each_safe(pos, n, head) \ @@ -46,7 +47,8 @@ struct slist { pos = pos->next) /** - * slist_for_each_entry_continue - iterate over a hlist continuing after current point + * 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. @@ -58,7 +60,8 @@ struct slist { pos = pos->next) /** - * slist_for_each_entry_from - iterate over a hlist continuing from current point + * 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. diff --git a/kernel/include/linux/netfilter/ip_set_timeout.h b/kernel/include/linux/netfilter/ip_set_timeout.h index bf1cbf6..b917480 100644 --- a/kernel/include/linux/netfilter/ip_set_timeout.h +++ b/kernel/include/linux/netfilter/ip_set_timeout.h @@ -17,7 +17,7 @@ #define IPSET_GC_PERIOD(timeout) \ ((timeout/3) ? min_t(u32, (timeout)/3, IPSET_GC_TIME) : 1) -/* Set is defined without timeout support */ +/* Set is defined without timeout support: timeout value may be 0 */ #define IPSET_NO_TIMEOUT UINT_MAX #define with_timeout(timeout) ((timeout) != IPSET_NO_TIMEOUT) @@ -27,11 +27,14 @@ ip_set_timeout_uget(struct nlattr *tb) { unsigned int timeout = ip_set_get_h32(tb); + /* Userspace supplied TIMEOUT parameter: adjust crazy size */ return timeout == IPSET_NO_TIMEOUT ? IPSET_NO_TIMEOUT - 1 : timeout; } #ifdef IP_SET_BITMAP_TIMEOUT +/* Bitmap specific timeout constants and macros for the entries */ + /* Bitmap entry is unset */ #define IPSET_ELEM_UNSET 0 /* Bitmap entry is set with no timeout value */ @@ -63,6 +66,7 @@ ip_set_timeout_set(u32 timeout) t = timeout * HZ + jiffies; if (t == IPSET_ELEM_UNSET || t == IPSET_ELEM_PERMANENT) + /* Bingo! */ t++; return t; @@ -76,19 +80,23 @@ ip_set_timeout_get(unsigned long timeout) #else +/* Hash specific timeout constants and macros for the entries */ + /* Hash entry is set with no timeout value */ -#define IPSET_ELEM_UNSET 0 +#define IPSET_ELEM_PERMANENT 0 static inline bool ip_set_timeout_test(unsigned long timeout) { - return timeout == IPSET_ELEM_UNSET || time_after(timeout, jiffies); + return timeout == IPSET_ELEM_PERMANENT + || time_after(timeout, jiffies); } static inline bool ip_set_timeout_expired(unsigned long timeout) { - return timeout != IPSET_ELEM_UNSET && time_before(timeout, jiffies); + return timeout != IPSET_ELEM_PERMANENT + && time_before(timeout, jiffies); } static inline unsigned long @@ -97,10 +105,11 @@ ip_set_timeout_set(u32 timeout) unsigned long t; if (!timeout) - return IPSET_ELEM_UNSET; + return IPSET_ELEM_PERMANENT; t = timeout * HZ + jiffies; - if (t == IPSET_ELEM_UNSET) + if (t == IPSET_ELEM_PERMANENT) + /* Bingo! :-) */ t++; return t; @@ -109,10 +118,10 @@ ip_set_timeout_set(u32 timeout) static inline u32 ip_set_timeout_get(unsigned long timeout) { - return timeout == IPSET_ELEM_UNSET ? 0 : (timeout - jiffies)/HZ; + return timeout == IPSET_ELEM_PERMANENT ? 0 : (timeout - jiffies)/HZ; } -#endif /* IP_SET_BITMAP_TIMEOUT */ +#endif /* ! IP_SET_BITMAP_TIMEOUT */ #endif /* __KERNEL__ */ -#endif /*_IP_SET_TIMEOUT_H */ +#endif /* _IP_SET_TIMEOUT_H */ diff --git a/kernel/ip_set.c b/kernel/ip_set.c index 74b2e91..a1813e2 100644 --- a/kernel/ip_set.c +++ b/kernel/ip_set.c @@ -91,8 +91,9 @@ find_set_type_rcu(const char *name, u8 family, u8 revision) return type; } -/* Find a given set type by name and family together - * with the supported minimal and maximum revisions. +/* Find a given set type by name and family. + * If we succeeded, the supported minimal and maximum revisions are + * filled out. */ static bool find_set_type_minmax(const char *name, u8 family, @@ -224,7 +225,7 @@ ip_set_test(ip_set_id_t index, const struct sk_buff *skb, read_unlock_bh(&set->lock); if (ret == -EAGAIN) { - /* Type requests element to be re-added */ + /* Type requests element to be completed */ pr_debug("element must be competed, ADD is triggered"); write_lock_bh(&set->lock); set->variant->kadt(set, skb, IPSET_ADD, family, dim, flags); @@ -842,9 +843,10 @@ ip_set_swap(struct sock *ctnl, struct sk_buff *skb, /* List/save set data */ -#define DUMP_ALL 0L -#define DUMP_ONE 1L -#define DUMP_LAST 2L +#define DUMP_INIT 0L +#define DUMP_ALL 1L +#define DUMP_ONE 2L +#define DUMP_LAST 3L static int ip_set_dump_done(struct netlink_callback *cb) @@ -868,6 +870,38 @@ dump_attrs(struct nlmsghdr *nlh) } } +static inline int +dump_init(struct netlink_callback *cb) +{ + struct nlmsghdr *nlh = nlmsg_hdr(cb->skb); + int min_len = NLMSG_SPACE(sizeof(struct nfgenmsg)); + struct nlattr *cda[IPSET_ATTR_CMD_MAX+1]; + struct nlattr *attr = (void *)nlh + min_len; + ip_set_id_t index; + + /* Second pass, so parser can't fail */ + nla_parse(cda, IPSET_ATTR_CMD_MAX, + attr, nlh->nlmsg_len - min_len, ip_set_setname_policy); + + /* cb->args[0] : dump single set/all sets + * [1] : set index + * [..]: type specific + */ + + if (!cda[IPSET_ATTR_SETNAME]) { + cb->args[0] = DUMP_ALL; + return 0; + } + + index = find_set_id(nla_data(cda[IPSET_ATTR_SETNAME])); + if (index == IPSET_INVALID_ID) + return -EEXIST; + + cb->args[0] = DUMP_ONE; + cb->args[1] = index; + return 0; +} + static int ip_set_dump_start(struct sk_buff *skb, struct netlink_callback *cb) { @@ -877,6 +911,16 @@ 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; + if (cb->args[0] == DUMP_INIT) { + ret = dump_init(cb); + if (ret < 0) { + /* We have to create and send the error message + * manually :-( */ + netlink_ack(cb->skb, nlmsg_hdr(cb->skb), ret); + return ret; + } + } + if (cb->args[1] >= ip_set_max) goto out; @@ -971,28 +1015,12 @@ ip_set_dump(struct sock *ctnl, struct sk_buff *skb, NFNL_CB_CONST struct nlmsghdr *nlh, NFNL_CB_CONST struct nlattr * NFNL_CB_CONST attr[]) { - ip_set_id_t index; - if (unlikely(protocol_failed(attr))) return -IPSET_ERR_PROTOCOL; - if (!attr[IPSET_ATTR_SETNAME]) - return netlink_dump_start(ctnl, skb, nlh, - ip_set_dump_start, - ip_set_dump_done); - - index = find_set_id(nla_data(attr[IPSET_ATTR_SETNAME])); - if (index == IPSET_INVALID_ID) - return -EEXIST; - - /* 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, DUMP_ONE, index); + return netlink_dump_start(ctnl, skb, nlh, + ip_set_dump_start, + ip_set_dump_done); } /* Add, del and test */ @@ -1025,7 +1053,8 @@ 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, GFP_KERNEL, retried++)) == 0); + && (ret = set->variant->resize(set, GFP_ATOMIC, + retried++)) == 0); if (!ret || (ret == -IPSET_ERR_EXIST && eexist)) return 0; @@ -1240,7 +1269,8 @@ 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)) { - pr_debug("can't find: %s, family: %u", typename, family); + pr_debug("can't find: %s, family: %u", + typename, family); return -EEXIST; } } @@ -1503,7 +1533,8 @@ ip_set_init(void) if (ip_set_max >= IPSET_INVALID_ID) ip_set_max = IPSET_INVALID_ID - 1; - ip_set_list = kzalloc(sizeof(struct ip_set *) * ip_set_max, GFP_KERNEL); + ip_set_list = kzalloc(sizeof(struct ip_set *) * ip_set_max, + GFP_KERNEL); if (!ip_set_list) { pr_err("ip_set: Unable to create ip_set_list"); return -ENOMEM; diff --git a/kernel/ip_set_bitmap_ip.c b/kernel/ip_set_bitmap_ip.c index 76baa13..5bb6a3c 100644 --- a/kernel/ip_set_bitmap_ip.c +++ b/kernel/ip_set_bitmap_ip.c @@ -103,8 +103,8 @@ bitmap_ip_kadt(struct ip_set *set, const struct sk_buff *skb, static const struct nla_policy bitmap_ip_adt_policy[IPSET_ATTR_ADT_MAX+1] __read_mostly = { - [IPSET_ATTR_IP] = { .type = NLA_U32 }, - [IPSET_ATTR_IP_TO] = { .type = NLA_U32 }, + [IPSET_ATTR_IP] = { .type = NLA_NESTED }, + [IPSET_ATTR_IP_TO] = { .type = NLA_NESTED }, [IPSET_ATTR_CIDR] = { .type = NLA_U8 }, [IPSET_ATTR_TIMEOUT] = { .type = NLA_U32 }, [IPSET_ATTR_LINENO] = { .type = NLA_U32 }, @@ -126,16 +126,16 @@ bitmap_ip_uadt(struct ip_set *set, struct nlattr *head, int len, if (tb[IPSET_ATTR_LINENO]) *lineno = nla_get_u32(tb[IPSET_ATTR_LINENO]); - if (tb[IPSET_ATTR_IP]) - ip = ip_set_get_h32(tb[IPSET_ATTR_IP]); - else - return -IPSET_ERR_PROTOCOL; + ret = ip_set_get_ipaddr4(tb, IPSET_ATTR_IP, &ip); + if (ret) + return ret; + ip = ntohl(ip); 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 */ + /* Set was defined without timeout support: + * don't ignore the attribute silently */ if (tb[IPSET_ATTR_TIMEOUT]) return -IPSET_ERR_TIMEOUT; @@ -143,7 +143,10 @@ bitmap_ip_uadt(struct ip_set *set, struct nlattr *head, int len, return bitmap_ip_test(map, ip_to_id(map, ip)); if (tb[IPSET_ATTR_IP_TO]) { - ip_to = ip_set_get_h32(tb[IPSET_ATTR_IP_TO]); + ret = ip_set_get_ipaddr4(tb, IPSET_ATTR_IP_TO, &ip_to); + if (ret) + return ret; + ip_to = ntohl(ip_to); if (ip > ip_to) { swap(ip, ip_to); if (ip < map->first_ip) @@ -203,8 +206,8 @@ bitmap_ip_head(struct ip_set *set, struct sk_buff *skb) 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_IPADDR4(skb, IPSET_ATTR_IP, htonl(map->first_ip)); + NLA_PUT_IPADDR4(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_REFERENCES, @@ -241,8 +244,8 @@ bitmap_ip_list(struct ip_set *set, } else goto nla_put_failure; } - NLA_PUT_NET32(skb, IPSET_ATTR_IP, - htonl(map->first_ip + id * map->hosts)); + NLA_PUT_IPADDR4(skb, IPSET_ATTR_IP, + htonl(map->first_ip + id * map->hosts)); ipset_nest_end(skb, nested); } ipset_nest_end(skb, atd); @@ -280,7 +283,7 @@ static const struct ip_set_type_variant bitmap_ip __read_mostly = { /* Timeout variant */ struct bitmap_ip_timeout { - void *members; /* the set members */ + unsigned long *members; /* the set members */ 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 */ @@ -295,21 +298,17 @@ struct bitmap_ip_timeout { static inline bool bitmap_ip_timeout_test(const struct bitmap_ip_timeout *map, u32 id) { - unsigned long *table = map->members; - - return ip_set_timeout_test(table[id]); + return ip_set_timeout_test(map->members[id]); } static inline int bitmap_ip_timeout_add(struct bitmap_ip_timeout *map, u32 id, u32 timeout) { - unsigned long *table = map->members; - if (bitmap_ip_timeout_test(map, id)) return -IPSET_ERR_EXIST; - table[id] = ip_set_timeout_set(timeout); + map->members[id] = ip_set_timeout_set(timeout); return 0; } @@ -317,13 +316,12 @@ bitmap_ip_timeout_add(struct bitmap_ip_timeout *map, static inline int bitmap_ip_timeout_del(struct bitmap_ip_timeout *map, u32 id) { - unsigned long *table = map->members; int ret = -IPSET_ERR_EXIST; if (bitmap_ip_timeout_test(map, id)) ret = 0; - table[id] = IPSET_ELEM_UNSET; + map->members[id] = IPSET_ELEM_UNSET; return ret; } @@ -368,10 +366,10 @@ bitmap_ip_timeout_uadt(struct ip_set *set, struct nlattr *head, int len, if (tb[IPSET_ATTR_LINENO]) *lineno = nla_get_u32(tb[IPSET_ATTR_LINENO]); - if (tb[IPSET_ATTR_IP]) - ip = ip_set_get_h32(tb[IPSET_ATTR_IP]); - else - return -IPSET_ERR_PROTOCOL; + ret = ip_set_get_ipaddr4(tb, IPSET_ATTR_IP, &ip); + if (ret) + return ret; + ip = ntohl(ip); if (ip < map->first_ip || ip > map->last_ip) return -IPSET_ERR_BITMAP_RANGE; @@ -381,7 +379,10 @@ bitmap_ip_timeout_uadt(struct ip_set *set, struct nlattr *head, int len, ip_to_id((const struct bitmap_ip *)map, ip)); if (tb[IPSET_ATTR_IP_TO]) { - ip_to = ip_set_get_h32(tb[IPSET_ATTR_IP_TO]); + ret = ip_set_get_ipaddr4(tb, IPSET_ATTR_IP_TO, &ip_to); + if (ret) + return ret; + ip_to = ntohl(ip_to); if (ip > ip_to) { swap(ip, ip_to); if (ip < map->first_ip) @@ -434,7 +435,7 @@ bitmap_ip_timeout_flush(struct ip_set *set) { struct bitmap_ip_timeout *map = set->data; - memset(map->members, 0, map->memsize); + memset(map->members, IPSET_ELEM_UNSET, map->memsize); } static int @@ -446,11 +447,11 @@ bitmap_ip_timeout_head(struct ip_set *set, struct sk_buff *skb) 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_IPADDR4(skb, IPSET_ATTR_IP, htonl(map->first_ip)); + NLA_PUT_IPADDR4(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_TIMEOUT , htonl(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, @@ -486,8 +487,8 @@ bitmap_ip_timeout_list(struct ip_set *set, } else goto nla_put_failure; } - NLA_PUT_NET32(skb, IPSET_ATTR_IP, - htonl(map->first_ip + id * map->hosts)); + NLA_PUT_IPADDR4(skb, IPSET_ATTR_IP, + htonl(map->first_ip + id * map->hosts)); NLA_PUT_NET32(skb, IPSET_ATTR_TIMEOUT, htonl(ip_set_timeout_get(table[id]))); ipset_nest_end(skb, nested); @@ -563,8 +564,8 @@ bitmap_ip_gc_init(struct ip_set *set) static const struct nla_policy bitmap_ip_create_policy[IPSET_ATTR_CREATE_MAX+1] __read_mostly = { - [IPSET_ATTR_IP] = { .type = NLA_U32 }, - [IPSET_ATTR_IP_TO] = { .type = NLA_U32 }, + [IPSET_ATTR_IP] = { .type = NLA_NESTED }, + [IPSET_ATTR_IP_TO] = { .type = NLA_NESTED }, [IPSET_ATTR_CIDR] = { .type = NLA_U8 }, [IPSET_ATTR_NETMASK] = { .type = NLA_U8 }, [IPSET_ATTR_TIMEOUT] = { .type = NLA_U32 }, @@ -597,18 +598,22 @@ bitmap_ip_create(struct ip_set *set, struct nlattr *head, int len, struct nlattr *tb[IPSET_ATTR_CREATE_MAX+1]; u32 first_ip, last_ip, hosts, elements; u8 netmask = 32; + int ret; if (nla_parse(tb, IPSET_ATTR_CREATE_MAX, head, len, bitmap_ip_create_policy)) return -IPSET_ERR_PROTOCOL; - if (tb[IPSET_ATTR_IP]) - first_ip = ip_set_get_h32(tb[IPSET_ATTR_IP]); - else - return -IPSET_ERR_PROTOCOL; + ret = ip_set_get_ipaddr4(tb, IPSET_ATTR_IP, &first_ip); + if (ret) + return ret; + first_ip = ntohl(first_ip); if (tb[IPSET_ATTR_IP_TO]) { - last_ip = ip_set_get_h32(tb[IPSET_ATTR_IP_TO]); + ret = ip_set_get_ipaddr4(tb, IPSET_ATTR_IP_TO, &last_ip); + if (ret) + return ret; + last_ip = htonl(last_ip); if (first_ip > last_ip) { u32 tmp = first_ip; diff --git a/kernel/ip_set_bitmap_ipmac.c b/kernel/ip_set_bitmap_ipmac.c index c595e18..6778819 100644 --- a/kernel/ip_set_bitmap_ipmac.c +++ b/kernel/ip_set_bitmap_ipmac.c @@ -51,6 +51,7 @@ struct bitmap_ipmac { size_t dsize; /* size of element */ }; +/* ADT structure for generic function args */ struct ipmac { u32 id; /* id in array */ unsigned char *ether; /* ethernet address */ @@ -133,7 +134,7 @@ bitmap_ipmac_add(struct ip_set *set, void *value, if (!data->ether) /* Already added without ethernet address */ return -IPSET_ERR_EXIST; - /* Fill the MAC address and activate the timer */ + /* Fill the MAC address */ memcpy(elem->ether, data->ether, ETH_ALEN); elem->match = MAC_FILLED; break; @@ -192,8 +193,8 @@ bitmap_ipmac_list(struct ip_set *set, } else goto nla_put_failure; } - NLA_PUT_NET32(skb, IPSET_ATTR_IP, - htonl(map->first_ip + id)); + NLA_PUT_IPADDR4(skb, IPSET_ATTR_IP, + htonl(map->first_ip + id)); if (elem->match == MAC_FILLED) NLA_PUT(skb, IPSET_ATTR_ETHER, ETH_ALEN, elem->ether); @@ -255,7 +256,7 @@ bitmap_ipmac_tadd(struct ip_set *set, void *value, elem->timeout = ip_set_timeout_set(timeout); break; case MAC_FILLED: - if (bitmap_expired(map, data->id)) + if (!bitmap_expired(map, data->id)) return -IPSET_ERR_EXIST; /* Fall through */ case MAC_EMPTY: @@ -264,7 +265,7 @@ bitmap_ipmac_tadd(struct ip_set *set, void *value, elem->match = MAC_FILLED; } else elem->match = MAC_UNSET; - /* If MAC is unset yet, we store plain timeout + /* If MAC is unset yet, we store plain timeout value * because the timer is not activated yet * and we can reuse it later when MAC is filled out, * possibly by the kernel */ @@ -318,8 +319,8 @@ bitmap_ipmac_tlist(struct ip_set *set, } else goto nla_put_failure; } - NLA_PUT_NET32(skb, IPSET_ATTR_IP, - htonl(map->first_ip + id)); + NLA_PUT_IPADDR4(skb, IPSET_ATTR_IP, + htonl(map->first_ip + id)); if (elem->match == MAC_FILLED) NLA_PUT(skb, IPSET_ATTR_ETHER, ETH_ALEN, elem->ether); @@ -365,7 +366,7 @@ bitmap_ipmac_kadt(struct ip_set *set, const struct sk_buff *skb, static const struct nla_policy bitmap_ipmac_adt_policy[IPSET_ATTR_ADT_MAX + 1] __read_mostly = { - [IPSET_ATTR_IP] = { .type = NLA_U32 }, + [IPSET_ATTR_IP] = { .type = NLA_NESTED }, [IPSET_ATTR_ETHER] = { .type = NLA_BINARY, .len = ETH_ALEN }, [IPSET_ATTR_TIMEOUT] = { .type = NLA_U32 }, [IPSET_ATTR_LINENO] = { .type = NLA_U32 }, @@ -389,10 +390,10 @@ bitmap_ipmac_uadt(struct ip_set *set, struct nlattr *head, int len, if (tb[IPSET_ATTR_LINENO]) *lineno = nla_get_u32(tb[IPSET_ATTR_LINENO]); - if (tb[IPSET_ATTR_IP]) - data.id = ip_set_get_h32(tb[IPSET_ATTR_IP]); - else - return -IPSET_ERR_PROTOCOL; + ret = ip_set_get_ipaddr4(tb, IPSET_ATTR_IP, &data.id); + if (ret) + return ret; + data.id = ntohl(data.id); if (data.id < map->first_ip || data.id > map->last_ip) return -IPSET_ERR_BITMAP_RANGE; @@ -410,7 +411,7 @@ bitmap_ipmac_uadt(struct ip_set *set, struct nlattr *head, int len, data.id -= map->first_ip; - ret = adtfn(set, &data, GFP_KERNEL, timeout); + ret = adtfn(set, &data, GFP_ATOMIC, timeout); return ip_set_eexist(ret, flags) ? 0 : ret; } @@ -447,8 +448,8 @@ bitmap_ipmac_head(struct ip_set *set, struct sk_buff *skb) 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_IPADDR4(skb, IPSET_ATTR_IP, htonl(map->first_ip)); + NLA_PUT_IPADDR4(skb, IPSET_ATTR_IP_TO, htonl(map->last_ip)); NLA_PUT_NET32(skb, IPSET_ATTR_REFERENCES, htonl(atomic_read(&set->ref) - 1)); NLA_PUT_NET32(skb, IPSET_ATTR_MEMSIZE, @@ -543,8 +544,8 @@ bitmap_ipmac_gc_init(struct ip_set *set) static const struct nla_policy bitmap_ipmac_create_policy[IPSET_ATTR_CREATE_MAX+1] __read_mostly = { - [IPSET_ATTR_IP] = { .type = NLA_U32 }, - [IPSET_ATTR_IP_TO] = { .type = NLA_U32 }, + [IPSET_ATTR_IP] = { .type = NLA_NESTED }, + [IPSET_ATTR_IP_TO] = { .type = NLA_NESTED }, [IPSET_ATTR_TIMEOUT] = { .type = NLA_U32 }, }; @@ -573,18 +574,22 @@ bitmap_ipmac_create(struct ip_set *set, struct nlattr *head, int len, struct nlattr *tb[IPSET_ATTR_CREATE_MAX+1]; u32 first_ip, last_ip, elements; struct bitmap_ipmac *map; + int ret; if (nla_parse(tb, IPSET_ATTR_CREATE_MAX, head, len, bitmap_ipmac_create_policy)) return -IPSET_ERR_PROTOCOL; - if (tb[IPSET_ATTR_IP]) - first_ip = ip_set_get_h32(tb[IPSET_ATTR_IP]); - else - return -IPSET_ERR_PROTOCOL; + ret = ip_set_get_ipaddr4(tb, IPSET_ATTR_IP, &first_ip); + if (ret) + return ret; + first_ip = ntohl(first_ip); if (tb[IPSET_ATTR_IP_TO]) { - last_ip = ip_set_get_h32(tb[IPSET_ATTR_IP_TO]); + ret = ip_set_get_ipaddr4(tb, IPSET_ATTR_IP_TO, &last_ip); + if (ret) + return ret; + last_ip = ntohl(last_ip); if (first_ip > last_ip) { u32 tmp = first_ip; diff --git a/kernel/ip_set_bitmap_port.c b/kernel/ip_set_bitmap_port.c index c385f99..27363f6 100644 --- a/kernel/ip_set_bitmap_port.c +++ b/kernel/ip_set_bitmap_port.c @@ -265,7 +265,7 @@ const struct ip_set_type_variant bitmap_port __read_mostly = { /* Timeout variant */ struct bitmap_port_timeout { - void *members; /* the set members */ + unsigned long *members; /* the set members */ u16 first_port; /* host byte order, included in range */ u16 last_port; /* host byte order, included in range */ size_t memsize; /* members size */ @@ -277,21 +277,17 @@ struct bitmap_port_timeout { static inline bool bitmap_port_timeout_test(const struct bitmap_port_timeout *map, u16 id) { - unsigned long *timeout = map->members; - - return ip_set_timeout_test(timeout[id]); + return ip_set_timeout_test(map->members[id]); } static int bitmap_port_timeout_add(const struct bitmap_port_timeout *map, u16 id, u32 timeout) { - unsigned long *table = map->members; - if (bitmap_port_timeout_test(map, id)) return -IPSET_ERR_EXIST; - table[id] = ip_set_timeout_set(timeout); + map->members[id] = ip_set_timeout_set(timeout); return 0; } @@ -300,13 +296,12 @@ static int bitmap_port_timeout_del(const struct bitmap_port_timeout *map, u16 id) { - unsigned long *table = map->members; int ret = -IPSET_ERR_EXIST; if (bitmap_port_timeout_test(map, id)) ret = 0; - table[id] = IPSET_ELEM_UNSET; + map->members[id] = IPSET_ELEM_UNSET; return ret; } diff --git a/kernel/ip_set_hash_ip.c b/kernel/ip_set_hash_ip.c index 3295b26..6fad300 100644 --- a/kernel/ip_set_hash_ip.c +++ b/kernel/ip_set_hash_ip.c @@ -90,7 +90,7 @@ hash_ip4_data_zero_out(struct hash_ip4_elem *elem) static inline bool hash_ip4_data_list(struct sk_buff *skb, const struct hash_ip4_elem *data) { - NLA_PUT_NET32(skb, IPSET_ATTR_IP, data->ip); + NLA_PUT_IPADDR4(skb, IPSET_ATTR_IP, data->ip); return 0; nla_put_failure: @@ -103,7 +103,7 @@ hash_ip4_data_tlist(struct sk_buff *skb, const struct hash_ip4_elem *data) const struct hash_ip4_telem *tdata = (const struct hash_ip4_telem *)data; - NLA_PUT_NET32(skb, IPSET_ATTR_IP, tdata->ip); + NLA_PUT_IPADDR4(skb, IPSET_ATTR_IP, tdata->ip); NLA_PUT_NET32(skb, IPSET_ATTR_TIMEOUT, htonl(ip_set_timeout_get(tdata->timeout))); @@ -136,8 +136,8 @@ hash_ip4_kadt(struct ip_set *set, const struct sk_buff *skb, 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_IP] = { .type = NLA_NESTED }, + [IPSET_ATTR_IP_TO] = { .type = NLA_NESTED }, [IPSET_ATTR_CIDR] = { .type = NLA_U8 }, [IPSET_ATTR_TIMEOUT] = { .type = NLA_U32 }, [IPSET_ATTR_LINENO] = { .type = NLA_U32 }, @@ -160,10 +160,9 @@ hash_ip4_uadt(struct ip_set *set, struct nlattr *head, int len, if (tb[IPSET_ATTR_LINENO]) *lineno = nla_get_u32(tb[IPSET_ATTR_LINENO]); - if (tb[IPSET_ATTR_IP]) - ip = ip_set_get_n32(tb[IPSET_ATTR_IP]); - else - return -IPSET_ERR_PROTOCOL; + ret = ip_set_get_ipaddr4(tb, IPSET_ATTR_IP, &ip); + if (ret) + return ret; ip &= NETMASK(h->netmask); if (ip == 0) @@ -176,11 +175,14 @@ hash_ip4_uadt(struct ip_set *set, struct nlattr *head, int len, } if (adt == IPSET_TEST) - return adtfn(set, &ip, GFP_KERNEL, timeout); + return adtfn(set, &ip, GFP_ATOMIC, timeout); ip = ntohl(ip); if (tb[IPSET_ATTR_IP_TO]) { - ip_to = ip_set_get_h32(tb[IPSET_ATTR_IP_TO]); + ret = ip_set_get_ipaddr4(tb, IPSET_ATTR_IP_TO, &ip_to); + if (ret) + return ret; + ip_to = ntohl(ip_to); if (ip > ip_to) swap(ip, ip_to); } else if (tb[IPSET_ATTR_CIDR]) { @@ -197,7 +199,7 @@ hash_ip4_uadt(struct ip_set *set, struct nlattr *head, int len, for (; !before(ip_to, ip); ip += hosts) { nip = htonl(ip); - ret = adtfn(set, &nip, GFP_KERNEL, timeout); + ret = adtfn(set, &nip, GFP_ATOMIC, timeout); if (ret && !ip_set_eexist(ret, flags)) return ret; @@ -279,7 +281,7 @@ ip6_netmask(union nf_inet_addr *ip, u8 prefix) 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); + NLA_PUT_IPADDR6(skb, IPSET_ATTR_IP, &data->ip); return 0; nla_put_failure: @@ -292,7 +294,7 @@ 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_IPADDR6(skb, IPSET_ATTR_IP, &e->ip); NLA_PUT_NET32(skb, IPSET_ATTR_TIMEOUT, htonl(ip_set_timeout_get(e->timeout))); return 0; @@ -326,8 +328,7 @@ hash_ip6_kadt(struct ip_set *set, const struct sk_buff *skb, static const struct nla_policy hash_ip6_adt_policy[IPSET_ATTR_ADT_MAX + 1] __read_mostly = { - [IPSET_ATTR_IP] = { .type = NLA_BINARY, - .len = sizeof(struct in6_addr) }, + [IPSET_ATTR_IP] = { .type = NLA_NESTED }, [IPSET_ATTR_TIMEOUT] = { .type = NLA_U32 }, [IPSET_ATTR_LINENO] = { .type = NLA_U32 }, }; @@ -339,7 +340,7 @@ hash_ip6_uadt(struct ip_set *set, struct nlattr *head, int len, struct chash *h = set->data; struct nlattr *tb[IPSET_ATTR_ADT_MAX+1]; ipset_adtfn adtfn = set->variant->adt[adt]; - union nf_inet_addr *ip; + union nf_inet_addr ip; u32 timeout = h->timeout; int ret; @@ -350,13 +351,12 @@ hash_ip6_uadt(struct ip_set *set, struct nlattr *head, int len, if (tb[IPSET_ATTR_LINENO]) *lineno = nla_get_u32(tb[IPSET_ATTR_LINENO]); - if (tb[IPSET_ATTR_IP]) - ip = nla_data(tb[IPSET_ATTR_IP]); - else - return -IPSET_ERR_PROTOCOL; + ret = ip_set_get_ipaddr6(tb, IPSET_ATTR_IP, &ip); + if (ret) + return ret; - ip6_netmask(ip, h->netmask); - if (ipv6_addr_any(&ip->in6)) + ip6_netmask(&ip, h->netmask); + if (ipv6_addr_any(&ip.in6)) return -IPSET_ERR_HASH_ELEM; if (tb[IPSET_ATTR_TIMEOUT]) { @@ -365,7 +365,7 @@ hash_ip6_uadt(struct ip_set *set, struct nlattr *head, int len, timeout = ip_set_timeout_uget(tb[IPSET_ATTR_TIMEOUT]); } - ret = adtfn(set, ip, GFP_KERNEL, timeout); + ret = adtfn(set, &ip, GFP_ATOMIC, timeout); return ip_set_eexist(ret, flags) ? 0 : ret; } @@ -430,8 +430,9 @@ hash_ip_create(struct ip_set *set, struct nlattr *head, int len, u32 flags) 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); + h->htable = ip_set_alloc( + jhash_size(h->htable_bits) * sizeof(struct slist), + GFP_KERNEL); if (!h->htable) { kfree(h); return -ENOMEM; diff --git a/kernel/ip_set_hash_ipport.c b/kernel/ip_set_hash_ipport.c index 8554c80..1dd8187 100644 --- a/kernel/ip_set_hash_ipport.c +++ b/kernel/ip_set_hash_ipport.c @@ -104,10 +104,9 @@ 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_IPADDR4(skb, IPSET_ATTR_IP, data->ip); NLA_PUT_NET16(skb, IPSET_ATTR_PORT, data->port); - if (data->proto != IPSET_IPPROTO_TCPUDP) - NLA_PUT_U8(skb, IPSET_ATTR_PROTO, data->proto); + NLA_PUT_U8(skb, IPSET_ATTR_PROTO, data->proto); return 0; nla_put_failure: @@ -121,10 +120,9 @@ hash_ipport4_data_tlist(struct sk_buff *skb, const struct hash_ipport4_telem *tdata = (const struct hash_ipport4_telem *)data; - NLA_PUT_NET32(skb, IPSET_ATTR_IP, tdata->ip); + NLA_PUT_IPADDR4(skb, IPSET_ATTR_IP, tdata->ip); NLA_PUT_NET16(skb, IPSET_ATTR_PORT, tdata->port); - if (data->proto != IPSET_IPPROTO_TCPUDP) - NLA_PUT_U8(skb, IPSET_ATTR_PROTO, data->proto); + NLA_PUT_U8(skb, IPSET_ATTR_PROTO, data->proto); NLA_PUT_NET32(skb, IPSET_ATTR_TIMEOUT, htonl(ip_set_timeout_get(tdata->timeout))); @@ -134,8 +132,6 @@ nla_put_failure: return 1; } -#define IP_SET_HASH_WITH_PROTO - #define PF 4 #define HOST_MASK 32 #include @@ -146,9 +142,9 @@ hash_ipport4_kadt(struct ip_set *set, const struct sk_buff *skb, { struct chash *h = set->data; ipset_adtfn adtfn = set->variant->adt[adt]; - struct hash_ipport4_elem data = { .proto = h->proto }; + struct hash_ipport4_elem data = { }; - if (!get_ip4_port(skb, flags & IPSET_DIM_ONE_SRC, + if (!get_ip4_port(skb, flags & IPSET_DIM_TWO_SRC, &data.port, &data.proto)) return -EINVAL; @@ -158,8 +154,8 @@ hash_ipport4_kadt(struct ip_set *set, const struct sk_buff *skb, } static const struct nla_policy -hash_ipport4_adt_policy[IPSET_ATTR_ADT_MAX + 1] __read_mostly = { - [IPSET_ATTR_IP] = { .type = NLA_U32 }, +hash_ipport_adt_policy[IPSET_ATTR_ADT_MAX + 1] __read_mostly = { + [IPSET_ATTR_IP] = { .type = NLA_NESTED }, [IPSET_ATTR_PORT] = { .type = NLA_U16 }, [IPSET_ATTR_PROTO] = { .type = NLA_U8 }, [IPSET_ATTR_TIMEOUT] = { .type = NLA_U32 }, @@ -173,21 +169,20 @@ hash_ipport4_uadt(struct ip_set *set, struct nlattr *head, int len, struct chash *h = set->data; struct nlattr *tb[IPSET_ATTR_ADT_MAX+1]; ipset_adtfn adtfn = set->variant->adt[adt]; - struct hash_ipport4_elem data = { .proto = h->proto }; + 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)) + hash_ipport_adt_policy)) return -IPSET_ERR_PROTOCOL; if (tb[IPSET_ATTR_LINENO]) *lineno = nla_get_u32(tb[IPSET_ATTR_LINENO]); - if (tb[IPSET_ATTR_IP]) - data.ip = ip_set_get_n32(tb[IPSET_ATTR_IP]); - else - return -IPSET_ERR_PROTOCOL; + ret = ip_set_get_ipaddr4(tb, IPSET_ATTR_IP, &data.ip); + if (ret) + return ret; if (tb[IPSET_ATTR_PORT]) data.port = ip_set_get_n16(tb[IPSET_ATTR_PORT]); @@ -197,15 +192,15 @@ hash_ipport4_uadt(struct ip_set *set, struct nlattr *head, int len, if (tb[IPSET_ATTR_PROTO]) { data.proto = nla_get_u8(tb[IPSET_ATTR_PROTO]); - if (data.proto == 0 || data.proto >= IPSET_IPPROTO_TCPUDP) + if (data.proto == 0) return -IPSET_ERR_INVALID_PROTO; - } else if (data.proto == IPSET_IPPROTO_ANY) + } else return -IPSET_ERR_MISSING_PROTO; switch (data.proto) { case IPPROTO_UDP: case IPPROTO_TCP: - case IPSET_IPPROTO_TCPUDP: + case IPPROTO_ICMP: break; default: data.port = 0; @@ -218,7 +213,7 @@ hash_ipport4_uadt(struct ip_set *set, struct nlattr *head, int len, timeout = ip_set_timeout_uget(tb[IPSET_ATTR_TIMEOUT]); } - ret = adtfn(set, &data, GFP_KERNEL, timeout); + ret = adtfn(set, &data, GFP_ATOMIC, timeout); return ip_set_eexist(ret, flags) ? 0 : ret; } @@ -232,7 +227,6 @@ hash_ipport_same_set(const struct ip_set *a, const struct ip_set *b) /* Resizing changes htable_bits, so we ignore it */ return x->maxelem == y->maxelem && x->timeout == y->timeout - && x->proto == y->proto && x->array_size == y->array_size && x->chain_limit == y->chain_limit; } @@ -297,10 +291,9 @@ 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_IPADDR6(skb, IPSET_ATTR_IP, &data->ip); NLA_PUT_NET16(skb, IPSET_ATTR_PORT, data->port); - if (data->proto != IPSET_IPPROTO_TCPUDP) - NLA_PUT_U8(skb, IPSET_ATTR_PROTO, data->proto); + NLA_PUT_U8(skb, IPSET_ATTR_PROTO, data->proto); return 0; nla_put_failure: @@ -314,10 +307,9 @@ hash_ipport6_data_tlist(struct sk_buff *skb, 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_IPADDR6(skb, IPSET_ATTR_IP, &e->ip); NLA_PUT_NET16(skb, IPSET_ATTR_PORT, data->port); - if (data->proto != IPSET_IPPROTO_TCPUDP) - NLA_PUT_U8(skb, IPSET_ATTR_PROTO, data->proto); + NLA_PUT_U8(skb, IPSET_ATTR_PROTO, data->proto); NLA_PUT_NET32(skb, IPSET_ATTR_TIMEOUT, htonl(ip_set_timeout_get(e->timeout))); return 0; @@ -339,9 +331,9 @@ hash_ipport6_kadt(struct ip_set *set, const struct sk_buff *skb, { struct chash *h = set->data; ipset_adtfn adtfn = set->variant->adt[adt]; - struct hash_ipport6_elem data = { .proto = h->proto }; + struct hash_ipport6_elem data = { }; - if (!get_ip6_port(skb, flags & IPSET_DIM_ONE_SRC, + if (!get_ip6_port(skb, flags & IPSET_DIM_TWO_SRC, &data.port, &data.proto)) return -EINVAL; @@ -350,16 +342,6 @@ hash_ipport6_kadt(struct ip_set *set, const struct sk_buff *skb, return adtfn(set, &data, GFP_ATOMIC, h->timeout); } -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_PROTO] = { .type = NLA_U8 }, - [IPSET_ATTR_TIMEOUT] = { .type = NLA_U32 }, - [IPSET_ATTR_LINENO] = { .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) @@ -367,22 +349,20 @@ hash_ipport6_uadt(struct ip_set *set, struct nlattr *head, int len, struct chash *h = set->data; struct nlattr *tb[IPSET_ATTR_ADT_MAX+1]; ipset_adtfn adtfn = set->variant->adt[adt]; - struct hash_ipport6_elem data = { .proto = h->proto }; + struct hash_ipport6_elem data = { }; u32 timeout = h->timeout; int ret; if (nla_parse(tb, IPSET_ATTR_ADT_MAX, head, len, - hash_ipport6_adt_policy)) + hash_ipport_adt_policy)) return -IPSET_ERR_PROTOCOL; if (tb[IPSET_ATTR_LINENO]) *lineno = nla_get_u32(tb[IPSET_ATTR_LINENO]); - if (tb[IPSET_ATTR_IP]) - memcpy(&data.ip, nla_data(tb[IPSET_ATTR_IP]), - sizeof(struct in6_addr)); - else - return -IPSET_ERR_PROTOCOL; + ret = ip_set_get_ipaddr6(tb, IPSET_ATTR_IP, &data.ip); + if (ret) + return ret; if (tb[IPSET_ATTR_PORT]) data.port = ip_set_get_n16(tb[IPSET_ATTR_PORT]); @@ -392,15 +372,15 @@ hash_ipport6_uadt(struct ip_set *set, struct nlattr *head, int len, if (tb[IPSET_ATTR_PROTO]) { data.proto = nla_get_u8(tb[IPSET_ATTR_PROTO]); - if (data.proto == 0 || data.proto >= IPSET_IPPROTO_TCPUDP) + if (data.proto == 0) return -IPSET_ERR_INVALID_PROTO; - } else if (data.proto == IPSET_IPPROTO_ANY) + } else return -IPSET_ERR_MISSING_PROTO; switch (data.proto) { case IPPROTO_UDP: case IPPROTO_TCP: - case IPSET_IPPROTO_TCPUDP: + case IPPROTO_ICMPV6: break; default: data.port = 0; @@ -413,7 +393,7 @@ hash_ipport6_uadt(struct ip_set *set, struct nlattr *head, int len, timeout = ip_set_timeout_uget(tb[IPSET_ATTR_TIMEOUT]); } - ret = adtfn(set, &data, GFP_KERNEL, timeout); + ret = adtfn(set, &data, GFP_ATOMIC, timeout); return ip_set_eexist(ret, flags) ? 0 : ret; } @@ -436,7 +416,6 @@ hash_ipport_create(struct ip_set *set, struct nlattr *head, int len, u32 flags) struct nlattr *tb[IPSET_ATTR_CREATE_MAX+1]; struct chash *h; u32 hashsize = IPSET_DEFAULT_HASHSIZE, maxelem = IPSET_DEFAULT_MAXELEM; - u8 proto = IPSET_IPPROTO_TCPUDP; /* Backward compatibility */ if (!(set->family == AF_INET || set->family == AF_INET6)) return -IPSET_ERR_INVALID_FAMILY; @@ -454,12 +433,6 @@ hash_ipport_create(struct ip_set *set, struct nlattr *head, int len, u32 flags) if (tb[IPSET_ATTR_MAXELEM]) maxelem = ip_set_get_h32(tb[IPSET_ATTR_MAXELEM]); - if (tb[IPSET_ATTR_PROTO]) { - proto = nla_get_u8(tb[IPSET_ATTR_PROTO]); - if (!proto) - return -IPSET_ERR_INVALID_PROTO; - } - h = kzalloc(sizeof(*h), GFP_KERNEL); if (!h) return -ENOMEM; @@ -469,11 +442,11 @@ hash_ipport_create(struct ip_set *set, struct nlattr *head, int len, u32 flags) h->array_size = CHASH_DEFAULT_ARRAY_SIZE; h->chain_limit = CHASH_DEFAULT_CHAIN_LIMIT; get_random_bytes(&h->initval, sizeof(h->initval)); - h->proto = proto; h->timeout = IPSET_NO_TIMEOUT; - h->htable = ip_set_alloc(jhash_size(h->htable_bits) * sizeof(struct slist), - GFP_KERNEL); + h->htable = ip_set_alloc( + jhash_size(h->htable_bits) * sizeof(struct slist), + GFP_KERNEL); if (!h->htable) { kfree(h); return -ENOMEM; diff --git a/kernel/ip_set_hash_ipportip.c b/kernel/ip_set_hash_ipportip.c index d2db3a9..a20f1ef 100644 --- a/kernel/ip_set_hash_ipportip.c +++ b/kernel/ip_set_hash_ipportip.c @@ -107,11 +107,10 @@ 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_IPADDR4(skb, IPSET_ATTR_IP, data->ip); + NLA_PUT_IPADDR4(skb, IPSET_ATTR_IP2, data->ip2); NLA_PUT_NET16(skb, IPSET_ATTR_PORT, data->port); - if (data->proto != IPSET_IPPROTO_TCPUDP) - NLA_PUT_U8(skb, IPSET_ATTR_PROTO, data->proto); + NLA_PUT_U8(skb, IPSET_ATTR_PROTO, data->proto); return 0; nla_put_failure: @@ -125,11 +124,10 @@ hash_ipportip4_data_tlist(struct sk_buff *skb, 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_IPADDR4(skb, IPSET_ATTR_IP, tdata->ip); + NLA_PUT_IPADDR4(skb, IPSET_ATTR_IP2, tdata->ip2); NLA_PUT_NET16(skb, IPSET_ATTR_PORT, tdata->port); - if (data->proto != IPSET_IPPROTO_TCPUDP) - NLA_PUT_U8(skb, IPSET_ATTR_PROTO, data->proto); + NLA_PUT_U8(skb, IPSET_ATTR_PROTO, data->proto); NLA_PUT_NET32(skb, IPSET_ATTR_TIMEOUT, htonl(ip_set_timeout_get(tdata->timeout))); @@ -139,8 +137,6 @@ nla_put_failure: return 1; } -#define IP_SET_HASH_WITH_PROTO - #define PF 4 #define HOST_MASK 32 #include @@ -151,7 +147,7 @@ hash_ipportip4_kadt(struct ip_set *set, const struct sk_buff *skb, { struct chash *h = set->data; ipset_adtfn adtfn = set->variant->adt[adt]; - struct hash_ipportip4_elem data = { .proto = h->proto }; + struct hash_ipportip4_elem data = { }; if (!get_ip4_port(skb, flags & IPSET_DIM_TWO_SRC, &data.port, &data.proto)) @@ -164,9 +160,9 @@ hash_ipportip4_kadt(struct ip_set *set, const struct sk_buff *skb, } 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 }, +hash_ipportip_adt_policy[IPSET_ATTR_ADT_MAX + 1] __read_mostly = { + [IPSET_ATTR_IP] = { .type = NLA_NESTED }, + [IPSET_ATTR_IP2] = { .type = NLA_NESTED }, [IPSET_ATTR_PORT] = { .type = NLA_U16 }, [IPSET_ATTR_PROTO] = { .type = NLA_U8 }, [IPSET_ATTR_TIMEOUT] = { .type = NLA_U32 }, @@ -180,26 +176,24 @@ hash_ipportip4_uadt(struct ip_set *set, struct nlattr *head, int len, struct chash *h = set->data; struct nlattr *tb[IPSET_ATTR_ADT_MAX+1]; ipset_adtfn adtfn = set->variant->adt[adt]; - struct hash_ipportip4_elem data = { .proto = h->proto }; + 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)) + hash_ipportip_adt_policy)) return -IPSET_ERR_PROTOCOL; if (tb[IPSET_ATTR_LINENO]) *lineno = nla_get_u32(tb[IPSET_ATTR_LINENO]); - if (tb[IPSET_ATTR_IP]) - data.ip = ip_set_get_n32(tb[IPSET_ATTR_IP]); - else - return -IPSET_ERR_PROTOCOL; + ret = ip_set_get_ipaddr4(tb, IPSET_ATTR_IP, &data.ip); + if (ret) + return ret; - if (tb[IPSET_ATTR_IP2]) - data.ip2 = ip_set_get_n32(tb[IPSET_ATTR_IP2]); - else - return -IPSET_ERR_PROTOCOL; + ret = ip_set_get_ipaddr4(tb, IPSET_ATTR_IP2, &data.ip2); + if (ret) + return ret; if (tb[IPSET_ATTR_PORT]) data.port = ip_set_get_n16(tb[IPSET_ATTR_PORT]); @@ -209,15 +203,15 @@ hash_ipportip4_uadt(struct ip_set *set, struct nlattr *head, int len, if (tb[IPSET_ATTR_PROTO]) { data.proto = nla_get_u8(tb[IPSET_ATTR_PROTO]); - if (data.proto == 0 || data.proto >= IPSET_IPPROTO_TCPUDP) + if (data.proto == 0) return -IPSET_ERR_INVALID_PROTO; - } else if (data.proto == IPSET_IPPROTO_ANY) + } else return -IPSET_ERR_MISSING_PROTO; switch (data.proto) { case IPPROTO_UDP: case IPPROTO_TCP: - case IPSET_IPPROTO_TCPUDP: + case IPPROTO_ICMP: break; default: data.port = 0; @@ -230,7 +224,7 @@ hash_ipportip4_uadt(struct ip_set *set, struct nlattr *head, int len, timeout = ip_set_timeout_uget(tb[IPSET_ATTR_TIMEOUT]); } - ret = adtfn(set, &data, GFP_KERNEL, timeout); + ret = adtfn(set, &data, GFP_ATOMIC, timeout); return ip_set_eexist(ret, flags) ? 0 : ret; } @@ -244,7 +238,6 @@ hash_ipportip_same_set(const struct ip_set *a, const struct ip_set *b) /* Resizing changes htable_bits, so we ignore it */ return x->maxelem == y->maxelem && x->timeout == y->timeout - && x->proto == y->proto && x->array_size == y->array_size && x->chain_limit == y->chain_limit; } @@ -312,11 +305,10 @@ 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_IPADDR6(skb, IPSET_ATTR_IP, &data->ip); + NLA_PUT_IPADDR6(skb, IPSET_ATTR_IP2, &data->ip2); NLA_PUT_NET16(skb, IPSET_ATTR_PORT, data->port); - if (data->proto != IPSET_IPPROTO_TCPUDP) - NLA_PUT_U8(skb, IPSET_ATTR_PROTO, data->proto); + NLA_PUT_U8(skb, IPSET_ATTR_PROTO, data->proto); return 0; nla_put_failure: @@ -330,11 +322,10 @@ hash_ipportip6_data_tlist(struct sk_buff *skb, 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_IPADDR6(skb, IPSET_ATTR_IP, &e->ip); + NLA_PUT_IPADDR6(skb, IPSET_ATTR_IP2, &data->ip2); NLA_PUT_NET16(skb, IPSET_ATTR_PORT, data->port); - if (data->proto != IPSET_IPPROTO_TCPUDP) - NLA_PUT_U8(skb, IPSET_ATTR_PROTO, data->proto); + NLA_PUT_U8(skb, IPSET_ATTR_PROTO, data->proto); NLA_PUT_NET32(skb, IPSET_ATTR_TIMEOUT, htonl(ip_set_timeout_get(e->timeout))); return 0; @@ -356,7 +347,7 @@ hash_ipportip6_kadt(struct ip_set *set, const struct sk_buff *skb, { struct chash *h = set->data; ipset_adtfn adtfn = set->variant->adt[adt]; - struct hash_ipportip6_elem data = { .proto = h->proto }; + struct hash_ipportip6_elem data = { }; if (!get_ip6_port(skb, flags & IPSET_DIM_TWO_SRC, &data.port, &data.proto)) @@ -368,18 +359,6 @@ hash_ipportip6_kadt(struct ip_set *set, const struct sk_buff *skb, return adtfn(set, &data, GFP_ATOMIC, h->timeout); } -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_PROTO] = { .type = NLA_U8 }, - [IPSET_ATTR_TIMEOUT] = { .type = NLA_U32 }, - [IPSET_ATTR_LINENO] = { .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) @@ -387,28 +366,24 @@ hash_ipportip6_uadt(struct ip_set *set, struct nlattr *head, int len, struct chash *h = set->data; struct nlattr *tb[IPSET_ATTR_ADT_MAX+1]; ipset_adtfn adtfn = set->variant->adt[adt]; - struct hash_ipportip6_elem data = { .proto = h->proto }; + struct hash_ipportip6_elem data = { }; u32 timeout = h->timeout; int ret; if (nla_parse(tb, IPSET_ATTR_ADT_MAX, head, len, - hash_ipportip6_adt_policy)) + hash_ipportip_adt_policy)) return -IPSET_ERR_PROTOCOL; if (tb[IPSET_ATTR_LINENO]) *lineno = nla_get_u32(tb[IPSET_ATTR_LINENO]); - if (tb[IPSET_ATTR_IP]) - memcpy(&data.ip, nla_data(tb[IPSET_ATTR_IP]), - sizeof(struct in6_addr)); - else - return -IPSET_ERR_PROTOCOL; + ret = ip_set_get_ipaddr6(tb, IPSET_ATTR_IP, &data.ip); + if (ret) + return ret; - if (tb[IPSET_ATTR_IP2]) - memcpy(&data.ip2, nla_data(tb[IPSET_ATTR_IP2]), - sizeof(struct in6_addr)); - else - return -IPSET_ERR_PROTOCOL; + ret = ip_set_get_ipaddr6(tb, IPSET_ATTR_IP2, &data.ip2); + if (ret) + return ret; if (tb[IPSET_ATTR_PORT]) data.port = ip_set_get_n16(tb[IPSET_ATTR_PORT]); @@ -418,15 +393,15 @@ hash_ipportip6_uadt(struct ip_set *set, struct nlattr *head, int len, if (tb[IPSET_ATTR_PROTO]) { data.proto = nla_get_u8(tb[IPSET_ATTR_PROTO]); - if (data.proto == 0 || data.proto >= IPSET_IPPROTO_TCPUDP) + if (data.proto == 0) return -IPSET_ERR_INVALID_PROTO; - } else if (data.proto == IPSET_IPPROTO_ANY) + } else return -IPSET_ERR_MISSING_PROTO; switch (data.proto) { case IPPROTO_UDP: case IPPROTO_TCP: - case IPSET_IPPROTO_TCPUDP: + case IPPROTO_ICMPV6: break; default: data.port = 0; @@ -439,7 +414,7 @@ hash_ipportip6_uadt(struct ip_set *set, struct nlattr *head, int len, timeout = ip_set_timeout_uget(tb[IPSET_ATTR_TIMEOUT]); } - ret = adtfn(set, &data, GFP_KERNEL, timeout); + ret = adtfn(set, &data, GFP_ATOMIC, timeout); return ip_set_eexist(ret, flags) ? 0 : ret; } @@ -452,7 +427,6 @@ hash_ipportip_create_policy[IPSET_ATTR_CREATE_MAX+1] __read_mostly = { [IPSET_ATTR_MAXELEM] = { .type = NLA_U32 }, [IPSET_ATTR_PROBES] = { .type = NLA_U8 }, [IPSET_ATTR_RESIZE] = { .type = NLA_U8 }, - [IPSET_ATTR_PROTO] = { .type = NLA_U8 }, [IPSET_ATTR_TIMEOUT] = { .type = NLA_U32 }, }; @@ -463,7 +437,6 @@ hash_ipportip_create(struct ip_set *set, struct nlattr *head, struct nlattr *tb[IPSET_ATTR_CREATE_MAX+1]; struct chash *h; u32 hashsize = IPSET_DEFAULT_HASHSIZE, maxelem = IPSET_DEFAULT_MAXELEM; - u8 proto = IPSET_IPPROTO_TCPUDP; /* Backward compatibility */ if (!(set->family == AF_INET || set->family == AF_INET6)) return -IPSET_ERR_INVALID_FAMILY; @@ -481,12 +454,6 @@ hash_ipportip_create(struct ip_set *set, struct nlattr *head, if (tb[IPSET_ATTR_MAXELEM]) maxelem = ip_set_get_h32(tb[IPSET_ATTR_MAXELEM]); - if (tb[IPSET_ATTR_PROTO]) { - proto = nla_get_u8(tb[IPSET_ATTR_PROTO]); - if (!proto) - return -IPSET_ERR_INVALID_PROTO; - } - h = kzalloc(sizeof(*h), GFP_KERNEL); if (!h) return -ENOMEM; @@ -496,11 +463,11 @@ hash_ipportip_create(struct ip_set *set, struct nlattr *head, h->array_size = CHASH_DEFAULT_ARRAY_SIZE; h->chain_limit = CHASH_DEFAULT_CHAIN_LIMIT; get_random_bytes(&h->initval, sizeof(h->initval)); - h->proto = proto; h->timeout = IPSET_NO_TIMEOUT; - h->htable = ip_set_alloc(jhash_size(h->htable_bits) * sizeof(struct slist), - GFP_KERNEL); + h->htable = ip_set_alloc( + jhash_size(h->htable_bits) * sizeof(struct slist), + GFP_KERNEL); if (!h->htable) { kfree(h); return -ENOMEM; diff --git a/kernel/ip_set_hash_ipportnet.c b/kernel/ip_set_hash_ipportnet.c index f2c0d07..3904168 100644 --- a/kernel/ip_set_hash_ipportnet.c +++ b/kernel/ip_set_hash_ipportnet.c @@ -115,12 +115,11 @@ 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_IPADDR4(skb, IPSET_ATTR_IP, data->ip); + NLA_PUT_IPADDR4(skb, IPSET_ATTR_IP2, data->ip2); NLA_PUT_NET16(skb, IPSET_ATTR_PORT, data->port); NLA_PUT_U8(skb, IPSET_ATTR_CIDR2, data->cidr); - if (data->proto != IPSET_IPPROTO_TCPUDP) - NLA_PUT_U8(skb, IPSET_ATTR_PROTO, data->proto); + NLA_PUT_U8(skb, IPSET_ATTR_PROTO, data->proto); return 0; nla_put_failure: @@ -134,12 +133,11 @@ hash_ipportnet4_data_tlist(struct sk_buff *skb, 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_IPADDR4(skb, IPSET_ATTR_IP, tdata->ip); + NLA_PUT_IPADDR4(skb, IPSET_ATTR_IP2, tdata->ip2); NLA_PUT_NET16(skb, IPSET_ATTR_PORT, tdata->port); NLA_PUT_U8(skb, IPSET_ATTR_CIDR2, data->cidr); - if (data->proto != IPSET_IPPROTO_TCPUDP) - NLA_PUT_U8(skb, IPSET_ATTR_PROTO, data->proto); + NLA_PUT_U8(skb, IPSET_ATTR_PROTO, data->proto); NLA_PUT_NET32(skb, IPSET_ATTR_TIMEOUT, htonl(ip_set_timeout_get(tdata->timeout))); @@ -163,8 +161,7 @@ hash_ipportnet4_kadt(struct ip_set *set, const struct sk_buff *skb, struct chash *h = set->data; ipset_adtfn adtfn = set->variant->adt[adt]; struct hash_ipportnet4_elem data = - { .cidr = h->nets[0].cidr || HOST_MASK, - .proto = h->proto }; + { .cidr = h->nets[0].cidr || HOST_MASK }; if (data.cidr == 0) return -EINVAL; @@ -183,9 +180,9 @@ hash_ipportnet4_kadt(struct ip_set *set, const struct sk_buff *skb, } 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 }, +hash_ipportnet_adt_policy[IPSET_ATTR_ADT_MAX + 1] __read_mostly = { + [IPSET_ATTR_IP] = { .type = NLA_NESTED }, + [IPSET_ATTR_IP2] = { .type = NLA_NESTED }, [IPSET_ATTR_PORT] = { .type = NLA_U16 }, [IPSET_ATTR_CIDR2] = { .type = NLA_U8 }, [IPSET_ATTR_PROTO] = { .type = NLA_U8 }, @@ -200,27 +197,24 @@ hash_ipportnet4_uadt(struct ip_set *set, struct nlattr *head, int len, struct chash *h = set->data; struct nlattr *tb[IPSET_ATTR_ADT_MAX+1]; ipset_adtfn adtfn = set->variant->adt[adt]; - struct hash_ipportnet4_elem data = { .cidr = HOST_MASK, - .proto = h->proto }; + 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)) + hash_ipportnet_adt_policy)) return -IPSET_ERR_PROTOCOL; if (tb[IPSET_ATTR_LINENO]) *lineno = nla_get_u32(tb[IPSET_ATTR_LINENO]); - if (tb[IPSET_ATTR_IP]) - data.ip = ip_set_get_n32(tb[IPSET_ATTR_IP]); - else - return -IPSET_ERR_PROTOCOL; + ret = ip_set_get_ipaddr4(tb, IPSET_ATTR_IP, &data.ip); + if (ret) + return ret; - if (tb[IPSET_ATTR_IP2]) - data.ip2 = ip_set_get_n32(tb[IPSET_ATTR_IP2]); - else - return -IPSET_ERR_PROTOCOL; + ret = ip_set_get_ipaddr4(tb, IPSET_ATTR_IP2, &data.ip2); + if (ret) + return ret; if (tb[IPSET_ATTR_CIDR2]) data.cidr = nla_get_u8(tb[IPSET_ATTR_CIDR2]); @@ -238,15 +232,15 @@ hash_ipportnet4_uadt(struct ip_set *set, struct nlattr *head, int len, if (tb[IPSET_ATTR_PROTO]) { data.proto = nla_get_u8(tb[IPSET_ATTR_PROTO]); - if (data.proto == 0 || data.proto >= IPSET_IPPROTO_TCPUDP) + if (data.proto == 0) return -IPSET_ERR_INVALID_PROTO; - } else if (data.proto == IPSET_IPPROTO_ANY) + } else return -IPSET_ERR_MISSING_PROTO; switch (data.proto) { case IPPROTO_UDP: case IPPROTO_TCP: - case IPSET_IPPROTO_TCPUDP: + case IPPROTO_ICMP: break; default: data.port = 0; @@ -259,7 +253,7 @@ hash_ipportnet4_uadt(struct ip_set *set, struct nlattr *head, int len, timeout = ip_set_timeout_uget(tb[IPSET_ATTR_TIMEOUT]); } - ret = adtfn(set, &data, GFP_KERNEL, timeout); + ret = adtfn(set, &data, GFP_ATOMIC, timeout); return ip_set_eexist(ret, flags) ? 0 : ret; } @@ -273,7 +267,6 @@ hash_ipportnet_same_set(const struct ip_set *a, const struct ip_set *b) /* Resizing changes htable_bits, so we ignore it */ return x->maxelem == y->maxelem && x->timeout == y->timeout - && x->proto == y->proto && x->array_size == y->array_size && x->chain_limit == y->chain_limit; } @@ -358,12 +351,11 @@ 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_IPADDR6(skb, IPSET_ATTR_IP, &data->ip); + NLA_PUT_IPADDR6(skb, IPSET_ATTR_IP2, &data->ip2); NLA_PUT_NET16(skb, IPSET_ATTR_PORT, data->port); NLA_PUT_U8(skb, IPSET_ATTR_CIDR2, data->cidr); - if (data->proto != IPSET_IPPROTO_TCPUDP) - NLA_PUT_U8(skb, IPSET_ATTR_PROTO, data->proto); + NLA_PUT_U8(skb, IPSET_ATTR_PROTO, data->proto); return 0; nla_put_failure: @@ -377,12 +369,11 @@ hash_ipportnet6_data_tlist(struct sk_buff *skb, 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_IPADDR6(skb, IPSET_ATTR_IP, &e->ip); + NLA_PUT_IPADDR6(skb, IPSET_ATTR_IP2, &data->ip2); NLA_PUT_NET16(skb, IPSET_ATTR_PORT, data->port); NLA_PUT_U8(skb, IPSET_ATTR_CIDR2, data->cidr); - if (data->proto != IPSET_IPPROTO_TCPUDP) - NLA_PUT_U8(skb, IPSET_ATTR_PROTO, data->proto); + NLA_PUT_U8(skb, IPSET_ATTR_PROTO, data->proto); NLA_PUT_NET32(skb, IPSET_ATTR_TIMEOUT, htonl(ip_set_timeout_get(e->timeout))); return 0; @@ -405,8 +396,7 @@ hash_ipportnet6_kadt(struct ip_set *set, const struct sk_buff *skb, struct chash *h = set->data; ipset_adtfn adtfn = set->variant->adt[adt]; struct hash_ipportnet6_elem data = - { .cidr = h->nets[0].cidr || HOST_MASK, - .proto = h->proto }; + { .cidr = h->nets[0].cidr || HOST_MASK }; if (data.cidr == 0) return -EINVAL; @@ -424,19 +414,6 @@ hash_ipportnet6_kadt(struct ip_set *set, const struct sk_buff *skb, return adtfn(set, &data, GFP_ATOMIC, h->timeout); } -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_PROTO] = { .type = NLA_U8 }, - [IPSET_ATTR_TIMEOUT] = { .type = NLA_U32 }, - [IPSET_ATTR_LINENO] = { .type = NLA_U32 }, -}; - static int hash_ipportnet6_uadt(struct ip_set *set, struct nlattr *head, int len, enum ipset_adt adt, u32 *lineno, u32 flags) @@ -444,29 +421,24 @@ hash_ipportnet6_uadt(struct ip_set *set, struct nlattr *head, int len, struct chash *h = set->data; struct nlattr *tb[IPSET_ATTR_ADT_MAX+1]; ipset_adtfn adtfn = set->variant->adt[adt]; - struct hash_ipportnet6_elem data = { .cidr = HOST_MASK, - .proto = h->proto }; + struct hash_ipportnet6_elem data = { .cidr = HOST_MASK }; u32 timeout = h->timeout; int ret; if (nla_parse(tb, IPSET_ATTR_ADT_MAX, head, len, - hash_ipportnet6_adt_policy)) + hash_ipportnet_adt_policy)) return -IPSET_ERR_PROTOCOL; if (tb[IPSET_ATTR_LINENO]) *lineno = nla_get_u32(tb[IPSET_ATTR_LINENO]); - if (tb[IPSET_ATTR_IP]) - memcpy(&data.ip, nla_data(tb[IPSET_ATTR_IP]), - sizeof(struct in6_addr)); - else - return -IPSET_ERR_PROTOCOL; + ret = ip_set_get_ipaddr6(tb, IPSET_ATTR_IP, &data.ip); + if (ret) + return ret; - if (tb[IPSET_ATTR_IP2]) - memcpy(&data.ip2, nla_data(tb[IPSET_ATTR_IP2]), - sizeof(struct in6_addr)); - else - return -IPSET_ERR_PROTOCOL; + ret = ip_set_get_ipaddr6(tb, IPSET_ATTR_IP2, &data.ip2); + if (ret) + return ret; if (tb[IPSET_ATTR_CIDR2]) data.cidr = nla_get_u8(tb[IPSET_ATTR_CIDR2]); @@ -484,15 +456,15 @@ hash_ipportnet6_uadt(struct ip_set *set, struct nlattr *head, int len, if (tb[IPSET_ATTR_PROTO]) { data.proto = nla_get_u8(tb[IPSET_ATTR_PROTO]); - if (data.proto == 0 || data.proto >= IPSET_IPPROTO_TCPUDP) + if (data.proto == 0) return -IPSET_ERR_INVALID_PROTO; - } else if (data.proto == IPSET_IPPROTO_ANY) + } else return -IPSET_ERR_MISSING_PROTO; switch (data.proto) { case IPPROTO_UDP: case IPPROTO_TCP: - case IPSET_IPPROTO_TCPUDP: + case IPPROTO_ICMPV6: break; default: data.port = 0; @@ -505,7 +477,7 @@ hash_ipportnet6_uadt(struct ip_set *set, struct nlattr *head, int len, timeout = ip_set_timeout_uget(tb[IPSET_ATTR_TIMEOUT]); } - ret = adtfn(set, &data, GFP_KERNEL, timeout); + ret = adtfn(set, &data, GFP_ATOMIC, timeout); return ip_set_eexist(ret, flags) ? 0 : ret; } @@ -518,7 +490,6 @@ hash_ipportnet_create_policy[IPSET_ATTR_CREATE_MAX+1] __read_mostly = { [IPSET_ATTR_MAXELEM] = { .type = NLA_U32 }, [IPSET_ATTR_PROBES] = { .type = NLA_U8 }, [IPSET_ATTR_RESIZE] = { .type = NLA_U8 }, - [IPSET_ATTR_PROTO] = { .type = NLA_U8 }, [IPSET_ATTR_TIMEOUT] = { .type = NLA_U32 }, }; @@ -529,7 +500,6 @@ hash_ipportnet_create(struct ip_set *set, struct nlattr *head, struct nlattr *tb[IPSET_ATTR_CREATE_MAX+1]; struct chash *h; u32 hashsize = IPSET_DEFAULT_HASHSIZE, maxelem = IPSET_DEFAULT_MAXELEM; - u8 proto = IPSET_IPPROTO_TCPUDP; /* Backward compatibility */ if (!(set->family == AF_INET || set->family == AF_INET6)) return -IPSET_ERR_INVALID_FAMILY; @@ -547,14 +517,9 @@ hash_ipportnet_create(struct ip_set *set, struct nlattr *head, if (tb[IPSET_ATTR_MAXELEM]) maxelem = ip_set_get_h32(tb[IPSET_ATTR_MAXELEM]); - if (tb[IPSET_ATTR_PROTO]) { - proto = nla_get_u8(tb[IPSET_ATTR_PROTO]); - if (!proto) - return -IPSET_ERR_INVALID_PROTO; - } h = kzalloc(sizeof(*h) + sizeof(struct chash_nets) - * (set->family == AF_INET ? 31 : 127), GFP_KERNEL); + * (set->family == AF_INET ? 32 : 128), GFP_KERNEL); h = kzalloc(sizeof(*h), GFP_KERNEL); if (!h) return -ENOMEM; @@ -563,11 +528,11 @@ hash_ipportnet_create(struct ip_set *set, struct nlattr *head, h->array_size = CHASH_DEFAULT_ARRAY_SIZE; h->chain_limit = CHASH_DEFAULT_CHAIN_LIMIT; get_random_bytes(&h->initval, sizeof(h->initval)); - h->proto = proto; h->timeout = IPSET_NO_TIMEOUT; - h->htable = ip_set_alloc(jhash_size(h->htable_bits) * sizeof(struct slist), - GFP_KERNEL); + h->htable = ip_set_alloc( + jhash_size(h->htable_bits) * sizeof(struct slist), + GFP_KERNEL); if (!h->htable) { kfree(h); return -ENOMEM; @@ -579,7 +544,8 @@ hash_ipportnet_create(struct ip_set *set, struct nlattr *head, h->timeout = ip_set_timeout_uget(tb[IPSET_ATTR_TIMEOUT]); set->variant = set->family == AF_INET - ? &hash_ipportnet4_tvariant : &hash_ipportnet6_tvariant; + ? &hash_ipportnet4_tvariant + : &hash_ipportnet6_tvariant; if (set->family == AF_INET) hash_ipportnet4_gc_init(set); diff --git a/kernel/ip_set_hash_net.c b/kernel/ip_set_hash_net.c index 6b755ce..27191f2 100644 --- a/kernel/ip_set_hash_net.c +++ b/kernel/ip_set_hash_net.c @@ -106,7 +106,7 @@ hash_net4_data_zero_out(struct hash_net4_elem *elem) static inline bool hash_net4_data_list(struct sk_buff *skb, const struct hash_net4_elem *data) { - NLA_PUT_NET32(skb, IPSET_ATTR_IP, data->ip); + NLA_PUT_IPADDR4(skb, IPSET_ATTR_IP, data->ip); NLA_PUT_U8(skb, IPSET_ATTR_CIDR, data->cidr); return 0; @@ -120,7 +120,7 @@ hash_net4_data_tlist(struct sk_buff *skb, const struct hash_net4_elem *data) const struct hash_net4_telem *tdata = (const struct hash_net4_telem *)data; - NLA_PUT_NET32(skb, IPSET_ATTR_IP, tdata->ip); + NLA_PUT_IPADDR4(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))); @@ -157,8 +157,8 @@ hash_net4_kadt(struct ip_set *set, const struct sk_buff *skb, } static const struct nla_policy -hash_net4_adt_policy[IPSET_ATTR_ADT_MAX + 1] __read_mostly = { - [IPSET_ATTR_IP] = { .type = NLA_U32 }, +hash_net_adt_policy[IPSET_ATTR_ADT_MAX + 1] __read_mostly = { + [IPSET_ATTR_IP] = { .type = NLA_NESTED }, [IPSET_ATTR_CIDR] = { .type = NLA_U8 }, [IPSET_ATTR_TIMEOUT] = { .type = NLA_U32 }, }; @@ -175,16 +175,15 @@ hash_net4_uadt(struct ip_set *set, struct nlattr *head, int len, int ret; if (nla_parse(tb, IPSET_ATTR_ADT_MAX, head, len, - hash_net4_adt_policy)) + hash_net_adt_policy)) return -IPSET_ERR_PROTOCOL; if (tb[IPSET_ATTR_LINENO]) *lineno = nla_get_u32(tb[IPSET_ATTR_LINENO]); - if (tb[IPSET_ATTR_IP]) - data.ip = ip_set_get_n32(tb[IPSET_ATTR_IP]); - else - return -IPSET_ERR_PROTOCOL; + ret = ip_set_get_ipaddr4(tb, IPSET_ATTR_IP, &data.ip); + if (ret) + return ret; if (tb[IPSET_ATTR_CIDR]) data.cidr = nla_get_u8(tb[IPSET_ATTR_CIDR]); @@ -200,7 +199,7 @@ hash_net4_uadt(struct ip_set *set, struct nlattr *head, int len, timeout = ip_set_timeout_uget(tb[IPSET_ATTR_TIMEOUT]); } - ret = adtfn(set, &data, GFP_KERNEL, timeout); + ret = adtfn(set, &data, GFP_ATOMIC, timeout); return ip_set_eexist(ret, flags) ? 0 : ret; } @@ -292,7 +291,7 @@ hash_net6_data_netmask(struct hash_net6_elem *elem, u8 cidr) static inline bool hash_net6_data_list(struct sk_buff *skb, const struct hash_net6_elem *data) { - NLA_PUT(skb, IPSET_ATTR_IP, sizeof(struct in6_addr), &data->ip); + NLA_PUT_IPADDR6(skb, IPSET_ATTR_IP, &data->ip); NLA_PUT_U8(skb, IPSET_ATTR_CIDR, data->cidr); return 0; @@ -306,7 +305,7 @@ 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_IPADDR6(skb, IPSET_ATTR_IP, &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))); @@ -342,15 +341,6 @@ hash_net6_kadt(struct ip_set *set, const struct sk_buff *skb, 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 }, - [IPSET_ATTR_LINENO] = { .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) @@ -363,17 +353,15 @@ hash_net6_uadt(struct ip_set *set, struct nlattr *head, int len, int ret; if (nla_parse(tb, IPSET_ATTR_ADT_MAX, head, len, - hash_net6_adt_policy)) + hash_net_adt_policy)) return -IPSET_ERR_PROTOCOL; if (tb[IPSET_ATTR_LINENO]) *lineno = nla_get_u32(tb[IPSET_ATTR_LINENO]); - if (tb[IPSET_ATTR_IP]) - memcpy(&data.ip, nla_data(tb[IPSET_ATTR_IP]), - sizeof(struct in6_addr)); - else - return -IPSET_ERR_PROTOCOL; + ret = ip_set_get_ipaddr6(tb, IPSET_ATTR_IP, &data.ip); + if (ret) + return ret; if (tb[IPSET_ATTR_CIDR]) data.cidr = nla_get_u8(tb[IPSET_ATTR_CIDR]); @@ -389,7 +377,7 @@ hash_net6_uadt(struct ip_set *set, struct nlattr *head, int len, timeout = ip_set_timeout_uget(tb[IPSET_ATTR_TIMEOUT]); } - ret = adtfn(set, &data, GFP_KERNEL, timeout); + ret = adtfn(set, &data, GFP_ATOMIC, timeout); return ip_set_eexist(ret, flags) ? 0 : ret; } @@ -430,7 +418,7 @@ hash_net_create(struct ip_set *set, struct nlattr *head, int len, u32 flags) h = kzalloc(sizeof(*h) + sizeof(struct chash_nets) - * (set->family == AF_INET ? 31 : 127), GFP_KERNEL); + * (set->family == AF_INET ? 32 : 128), GFP_KERNEL); if (!h) return -ENOMEM; @@ -441,8 +429,9 @@ hash_net_create(struct ip_set *set, struct nlattr *head, int len, u32 flags) 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); + h->htable = ip_set_alloc( + jhash_size(h->htable_bits) * sizeof(struct slist), + GFP_KERNEL); if (!h->htable) { kfree(h); return -ENOMEM; diff --git a/kernel/ip_set_hash_netport.c b/kernel/ip_set_hash_netport.c new file mode 100644 index 0000000..f7f43b8 --- /dev/null +++ b/kernel/ip_set_hash_netport.c @@ -0,0 +1,566 @@ +/* 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 set type: the hash:net,port type */ + +#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,port type of IP sets"); +MODULE_ALIAS("ip_set_hash:net,port"); + +/* Type specific function prefix */ +#define TYPE hash_netport + +static bool +hash_netport_same_set(const struct ip_set *a, const struct ip_set *b); + +#define hash_netport4_same_set hash_netport_same_set +#define hash_netport6_same_set hash_netport_same_set + +/* The type variant functions: IPv4 */ + +/* Member elements without timeout */ +struct hash_netport4_elem { + u32 ip; + u16 port; + u8 proto; + u8 cidr; +}; + +/* Member elements with timeout support */ +struct hash_netport4_telem { + u32 ip; + u16 port; + u8 proto; + u8 cidr; + unsigned long timeout; +}; + +static inline bool +hash_netport4_data_equal(const struct hash_netport4_elem *ip1, + const struct hash_netport4_elem *ip2) +{ + return ip1->ip == ip2->ip + && ip1->port == ip2->port + && ip1->proto == ip2->proto + && ip1->cidr == ip2->cidr; +} + +static inline bool +hash_netport4_data_isnull(const struct hash_netport4_elem *elem) +{ + return elem->proto == 0; +} + +static inline void +hash_netport4_data_copy(struct hash_netport4_elem *dst, + const struct hash_netport4_elem *src) +{ + dst->ip = src->ip; + dst->port = src->port; + dst->proto = src->proto; + dst->cidr = src->cidr; +} + +static inline void +hash_netport4_data_swap(struct hash_netport4_elem *dst, + struct hash_netport4_elem *src) +{ + swap(dst->ip, src->ip); + swap(dst->port, src->port); + swap(dst->proto, src->proto); + swap(dst->cidr, src->cidr); +} + +static inline void +hash_netport4_data_netmask(struct hash_netport4_elem *elem, u8 cidr) +{ + elem->ip &= NETMASK(cidr); + elem->cidr = cidr; +} + +static inline void +hash_netport4_data_zero_out(struct hash_netport4_elem *elem) +{ + elem->proto = 0; +} + +static inline bool +hash_netport4_data_list(struct sk_buff *skb, + const struct hash_netport4_elem *data) +{ + NLA_PUT_IPADDR4(skb, IPSET_ATTR_IP, data->ip); + NLA_PUT_NET16(skb, IPSET_ATTR_PORT, data->port); + NLA_PUT_U8(skb, IPSET_ATTR_CIDR, data->cidr); + NLA_PUT_U8(skb, IPSET_ATTR_PROTO, data->proto); + return 0; + +nla_put_failure: + return 1; +} + +static inline bool +hash_netport4_data_tlist(struct sk_buff *skb, + const struct hash_netport4_elem *data) +{ + const struct hash_netport4_telem *tdata = + (const struct hash_netport4_telem *)data; + + NLA_PUT_IPADDR4(skb, IPSET_ATTR_IP, tdata->ip); + NLA_PUT_NET16(skb, IPSET_ATTR_PORT, tdata->port); + NLA_PUT_U8(skb, IPSET_ATTR_CIDR, data->cidr); + NLA_PUT_U8(skb, IPSET_ATTR_PROTO, data->proto); + NLA_PUT_NET32(skb, IPSET_ATTR_TIMEOUT, + htonl(ip_set_timeout_get(tdata->timeout))); + + return 0; + +nla_put_failure: + return 1; +} + +#define IP_SET_HASH_WITH_PROTO +#define IP_SET_HASH_WITH_NETS + +#define PF 4 +#define HOST_MASK 32 +#include + +static int +hash_netport4_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_netport4_elem data = + { .cidr = h->nets[0].cidr || HOST_MASK }; + + if (data.cidr == 0) + return -EINVAL; + if (adt == IPSET_TEST) + data.cidr = HOST_MASK; + + if (!get_ip4_port(skb, flags & IPSET_DIM_TWO_SRC, + &data.port, &data.proto)) + return -EINVAL; + + ip4addrptr(skb, flags & IPSET_DIM_ONE_SRC, &data.ip); + data.ip &= NETMASK(data.cidr); + + return adtfn(set, &data, GFP_ATOMIC, h->timeout); +} + +static const struct nla_policy +hash_netport_adt_policy[IPSET_ATTR_ADT_MAX + 1] __read_mostly = { + [IPSET_ATTR_IP] = { .type = NLA_NESTED }, + [IPSET_ATTR_PORT] = { .type = NLA_U16 }, + [IPSET_ATTR_PROTO] = { .type = NLA_U8 }, + [IPSET_ATTR_CIDR] = { .type = NLA_U8 }, + [IPSET_ATTR_TIMEOUT] = { .type = NLA_U32 }, + [IPSET_ATTR_LINENO] = { .type = NLA_U32 }, +}; + +static int +hash_netport4_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+1]; + ipset_adtfn adtfn = set->variant->adt[adt]; + struct hash_netport4_elem data = { .cidr = HOST_MASK }; + u32 timeout = h->timeout; + int ret; + + if (nla_parse(tb, IPSET_ATTR_ADT_MAX, head, len, + hash_netport_adt_policy)) + return -IPSET_ERR_PROTOCOL; + + if (tb[IPSET_ATTR_LINENO]) + *lineno = nla_get_u32(tb[IPSET_ATTR_LINENO]); + + ret = ip_set_get_ipaddr4(tb, IPSET_ATTR_IP, &data.ip); + if (ret) + return ret; + + 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_PORT]) + data.port = ip_set_get_n16(tb[IPSET_ATTR_PORT]); + else + return -IPSET_ERR_PROTOCOL; + + if (tb[IPSET_ATTR_PROTO]) { + data.proto = nla_get_u8(tb[IPSET_ATTR_PROTO]); + + if (data.proto == 0) + return -IPSET_ERR_INVALID_PROTO; + } else + return -IPSET_ERR_MISSING_PROTO; + + switch (data.proto) { + case IPPROTO_UDP: + case IPPROTO_TCP: + case IPPROTO_ICMP: + break; + default: + data.port = 0; + break; + } + + 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_ATOMIC, timeout); + + return ip_set_eexist(ret, flags) ? 0 : ret; +} + +static bool +hash_netport_same_set(const struct ip_set *a, const struct ip_set *b) +{ + struct chash *x = a->data; + struct chash *y = b->data; + + /* Resizing changes htable_bits, so we ignore it */ + return x->maxelem == y->maxelem + && x->timeout == y->timeout + && x->array_size == y->array_size + && x->chain_limit == y->chain_limit; +} + +/* The type variant functions: IPv6 */ + +struct hash_netport6_elem { + union nf_inet_addr ip; + u16 port; + u8 proto; + u8 cidr; +}; + +struct hash_netport6_telem { + union nf_inet_addr ip; + u16 port; + u8 proto; + u8 cidr; + unsigned long timeout; +}; + +static inline bool +hash_netport6_data_equal(const struct hash_netport6_elem *ip1, + const struct hash_netport6_elem *ip2) +{ + return ipv6_addr_cmp(&ip1->ip.in6, &ip2->ip.in6) == 0 + && ip1->port == ip2->port + && ip1->proto == ip2->proto + && ip1->cidr == ip2->cidr; +} + +static inline bool +hash_netport6_data_isnull(const struct hash_netport6_elem *elem) +{ + return elem->proto == 0; +} + +static inline void +hash_netport6_data_copy(struct hash_netport6_elem *dst, + const struct hash_netport6_elem *src) +{ + memcpy(dst, src, sizeof(*dst)); +} + +static inline void +hash_netport6_data_swap(struct hash_netport6_elem *dst, + struct hash_netport6_elem *src) +{ + struct hash_netport6_elem tmp; + + memcpy(&tmp, dst, sizeof(tmp)); + memcpy(dst, src, sizeof(tmp)); + memcpy(src, &tmp, sizeof(tmp)); +} + +static inline void +hash_netport6_data_zero_out(struct hash_netport6_elem *elem) +{ + elem->proto = 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 void +hash_netport6_data_netmask(struct hash_netport6_elem *elem, u8 cidr) +{ + ip6_netmask(&elem->ip, cidr); + elem->cidr = cidr; +} + +static inline bool +hash_netport6_data_list(struct sk_buff *skb, + const struct hash_netport6_elem *data) +{ + NLA_PUT_IPADDR6(skb, IPSET_ATTR_IP, &data->ip); + NLA_PUT_NET16(skb, IPSET_ATTR_PORT, data->port); + NLA_PUT_U8(skb, IPSET_ATTR_CIDR, data->cidr); + NLA_PUT_U8(skb, IPSET_ATTR_PROTO, data->proto); + return 0; + +nla_put_failure: + return 1; +} + +static inline bool +hash_netport6_data_tlist(struct sk_buff *skb, + const struct hash_netport6_elem *data) +{ + const struct hash_netport6_telem *e = + (const struct hash_netport6_telem *)data; + + NLA_PUT_IPADDR6(skb, IPSET_ATTR_IP, &e->ip); + NLA_PUT_NET16(skb, IPSET_ATTR_PORT, data->port); + NLA_PUT_U8(skb, IPSET_ATTR_CIDR, data->cidr); + NLA_PUT_U8(skb, IPSET_ATTR_PROTO, data->proto); + NLA_PUT_NET32(skb, IPSET_ATTR_TIMEOUT, + htonl(ip_set_timeout_get(e->timeout))); + return 0; + +nla_put_failure: + return 1; +} + +#undef PF +#undef HOST_MASK + +#define PF 6 +#define HOST_MASK 128 +#include + +static int +hash_netport6_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_netport6_elem data = + { .cidr = h->nets[0].cidr || HOST_MASK }; + + if (data.cidr == 0) + return -EINVAL; + if (adt == IPSET_TEST) + data.cidr = HOST_MASK; + + if (!get_ip6_port(skb, flags & IPSET_DIM_TWO_SRC, + &data.port, &data.proto)) + return -EINVAL; + + 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 int +hash_netport6_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+1]; + ipset_adtfn adtfn = set->variant->adt[adt]; + struct hash_netport6_elem data = { .cidr = HOST_MASK }; + u32 timeout = h->timeout; + int ret; + + if (nla_parse(tb, IPSET_ATTR_ADT_MAX, head, len, + hash_netport_adt_policy)) + return -IPSET_ERR_PROTOCOL; + + if (tb[IPSET_ATTR_LINENO]) + *lineno = nla_get_u32(tb[IPSET_ATTR_LINENO]); + + ret = ip_set_get_ipaddr6(tb, IPSET_ATTR_IP, &data.ip); + if (ret) + return ret; + + 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_PORT]) + data.port = ip_set_get_n16(tb[IPSET_ATTR_PORT]); + else + return -IPSET_ERR_PROTOCOL; + + if (tb[IPSET_ATTR_PROTO]) { + data.proto = nla_get_u8(tb[IPSET_ATTR_PROTO]); + + if (data.proto == 0) + return -IPSET_ERR_INVALID_PROTO; + } else + return -IPSET_ERR_MISSING_PROTO; + + switch (data.proto) { + case IPPROTO_UDP: + case IPPROTO_TCP: + case IPPROTO_ICMPV6: + break; + default: + data.port = 0; + break; + } + + 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_ATOMIC, timeout); + + return ip_set_eexist(ret, flags) ? 0 : ret; +} + +/* Create hash:ip type of sets */ + +static const struct nla_policy +hash_netport_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_PROTO] = { .type = NLA_U8 }, + [IPSET_ATTR_TIMEOUT] = { .type = NLA_U32 }, +}; + +static int +hash_netport_create(struct ip_set *set, struct nlattr *head, int len, u32 flags) +{ + struct nlattr *tb[IPSET_ATTR_CREATE_MAX+1]; + struct chash *h; + u32 hashsize = IPSET_DEFAULT_HASHSIZE, maxelem = IPSET_DEFAULT_MAXELEM; + + 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_netport_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 ? 32 : 128), 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); + 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_netport4_tvariant : &hash_netport6_tvariant; + + if (set->family == AF_INET) + hash_netport4_gc_init(set); + else + hash_netport6_gc_init(set); + } else { + set->variant = set->family == AF_INET + ? &hash_netport4_variant : &hash_netport6_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_netport_type = { + .name = "hash:net,port", + .protocol = IPSET_PROTOCOL, + .features = IPSET_TYPE_IP | IPSET_TYPE_PORT, + .dimension = IPSET_DIM_TWO, + .family = AF_UNSPEC, + .revision = 0, + .create = hash_netport_create, + .me = THIS_MODULE, +}; + +static int __init +hash_netport_init(void) +{ + return ip_set_type_register(&hash_netport_type); +} + +static void __exit +hash_netport_fini(void) +{ + ip_set_type_unregister(&hash_netport_type); +} + +module_init(hash_netport_init); +module_exit(hash_netport_fini); diff --git a/kernel/ip_set_list_set.c b/kernel/ip_set_list_set.c index c1e4699..ea3f0a9 100644 --- a/kernel/ip_set_list_set.c +++ b/kernel/ip_set_list_set.c @@ -250,7 +250,8 @@ list_set_uadt(struct ip_set *set, struct nlattr *head, int len, } if (tb[IPSET_ATTR_NAMEREF]) { - refid = ip_set_get_byname(nla_data(tb[IPSET_ATTR_NAMEREF]), &s); + refid = ip_set_get_byname(nla_data(tb[IPSET_ATTR_NAMEREF]), + &s); if (refid == IPSET_INVALID_ID) { ret = -IPSET_ERR_NAMEREF; goto finish; @@ -320,7 +321,8 @@ list_set_uadt(struct ip_set *set, struct nlattr *head, int len, continue; else if (elem->id == id && (before == 0 - || (before > 0 && next_id_eq(map, i, refid)))) + || (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)) -- cgit v1.2.3