Commit 41d22f7b2e48c77175b62cec3797d7d7173a626e

Authored by Jozsef Kadlecsik
Committed by Patrick McHardy
1 parent 5663bc30e6

netfilter: ipset: hash:ip,port,net set type support

The module implements the hash:ip,port,net type support in four flavours:
for IPv4 and IPv6, both without and with timeout support. The elements
are three dimensional: IPv4/IPv6 address, protocol/port and IPv4/IPv6
network address/prefix triples. The different prefixes are searched/matched
from the longest prefix to the shortes one (most specific to least).
In other words the processing time linearly grows with the number of
different prefixes in the set.

Signed-off-by: Jozsef Kadlecsik <kadlec@blackhole.kfki.hu>
Signed-off-by: Patrick McHardy <kaber@trash.net>

Showing 3 changed files with 642 additions and 0 deletions Side-by-side Diff

net/netfilter/ipset/Kconfig
... ... @@ -79,5 +79,15 @@
79 79  
80 80 To compile it as a module, choose M here. If unsure, say N.
81 81  
  82 +config IP_SET_HASH_IPPORTNET
  83 + tristate "hash:ip,port,net set support"
  84 + depends on IP_SET
  85 + help
  86 + This option adds the hash:ip,port,net set type support, by which
  87 + one can store IPv4/IPv6 address, protocol/port, and IPv4/IPv6
  88 + network address/prefix triples in a set.
  89 +
  90 + To compile it as a module, choose M here. If unsure, say N.
  91 +
82 92 endif # IP_SET
net/netfilter/ipset/Makefile
... ... @@ -16,4 +16,5 @@
16 16 obj-$(CONFIG_IP_SET_HASH_IP) += ip_set_hash_ip.o
17 17 obj-$(CONFIG_IP_SET_HASH_IPPORT) += ip_set_hash_ipport.o
18 18 obj-$(CONFIG_IP_SET_HASH_IPPORTIP) += ip_set_hash_ipportip.o
  19 +obj-$(CONFIG_IP_SET_HASH_IPPORTNET) += ip_set_hash_ipportnet.o
net/netfilter/ipset/ip_set_hash_ipportnet.c
  1 +/* Copyright (C) 2003-2011 Jozsef Kadlecsik <kadlec@blackhole.kfki.hu>
  2 + *
  3 + * This program is free software; you can redistribute it and/or modify
  4 + * it under the terms of the GNU General Public License version 2 as
  5 + * published by the Free Software Foundation.
  6 + */
  7 +
  8 +/* Kernel module implementing an IP set type: the hash:ip,port,net type */
  9 +
  10 +#include <linux/jhash.h>
  11 +#include <linux/module.h>
  12 +#include <linux/ip.h>
  13 +#include <linux/skbuff.h>
  14 +#include <linux/errno.h>
  15 +#include <asm/uaccess.h>
  16 +#include <asm/bitops.h>
  17 +#include <linux/spinlock.h>
  18 +#include <linux/random.h>
  19 +#include <net/ip.h>
  20 +#include <net/ipv6.h>
  21 +#include <net/netlink.h>
  22 +#include <net/tcp.h>
  23 +
  24 +#include <linux/netfilter.h>
  25 +#include <linux/netfilter/ipset/pfxlen.h>
  26 +#include <linux/netfilter/ipset/ip_set.h>
  27 +#include <linux/netfilter/ipset/ip_set_timeout.h>
  28 +#include <linux/netfilter/ipset/ip_set_getport.h>
  29 +#include <linux/netfilter/ipset/ip_set_hash.h>
  30 +
  31 +MODULE_LICENSE("GPL");
  32 +MODULE_AUTHOR("Jozsef Kadlecsik <kadlec@blackhole.kfki.hu>");
  33 +MODULE_DESCRIPTION("hash:ip,port,net type of IP sets");
  34 +MODULE_ALIAS("ip_set_hash:ip,port,net");
  35 +
  36 +/* Type specific function prefix */
  37 +#define TYPE hash_ipportnet
  38 +
  39 +static bool
  40 +hash_ipportnet_same_set(const struct ip_set *a, const struct ip_set *b);
  41 +
  42 +#define hash_ipportnet4_same_set hash_ipportnet_same_set
  43 +#define hash_ipportnet6_same_set hash_ipportnet_same_set
  44 +
  45 +/* The type variant functions: IPv4 */
  46 +
  47 +/* Member elements without timeout */
  48 +struct hash_ipportnet4_elem {
  49 + __be32 ip;
  50 + __be32 ip2;
  51 + __be16 port;
  52 + u8 cidr;
  53 + u8 proto;
  54 +};
  55 +
  56 +/* Member elements with timeout support */
  57 +struct hash_ipportnet4_telem {
  58 + __be32 ip;
  59 + __be32 ip2;
  60 + __be16 port;
  61 + u8 cidr;
  62 + u8 proto;
  63 + unsigned long timeout;
  64 +};
  65 +
  66 +static inline bool
  67 +hash_ipportnet4_data_equal(const struct hash_ipportnet4_elem *ip1,
  68 + const struct hash_ipportnet4_elem *ip2)
  69 +{
  70 + return ip1->ip == ip2->ip &&
  71 + ip1->ip2 == ip2->ip2 &&
  72 + ip1->cidr == ip2->cidr &&
  73 + ip1->port == ip2->port &&
  74 + ip1->proto == ip2->proto;
  75 +}
  76 +
  77 +static inline bool
  78 +hash_ipportnet4_data_isnull(const struct hash_ipportnet4_elem *elem)
  79 +{
  80 + return elem->proto == 0;
  81 +}
  82 +
  83 +static inline void
  84 +hash_ipportnet4_data_copy(struct hash_ipportnet4_elem *dst,
  85 + const struct hash_ipportnet4_elem *src)
  86 +{
  87 + memcpy(dst, src, sizeof(*dst));
  88 +}
  89 +
  90 +static inline void
  91 +hash_ipportnet4_data_netmask(struct hash_ipportnet4_elem *elem, u8 cidr)
  92 +{
  93 + elem->ip2 &= ip_set_netmask(cidr);
  94 + elem->cidr = cidr;
  95 +}
  96 +
  97 +static inline void
  98 +hash_ipportnet4_data_zero_out(struct hash_ipportnet4_elem *elem)
  99 +{
  100 + elem->proto = 0;
  101 +}
  102 +
  103 +static bool
  104 +hash_ipportnet4_data_list(struct sk_buff *skb,
  105 + const struct hash_ipportnet4_elem *data)
  106 +{
  107 + NLA_PUT_IPADDR4(skb, IPSET_ATTR_IP, data->ip);
  108 + NLA_PUT_IPADDR4(skb, IPSET_ATTR_IP2, data->ip2);
  109 + NLA_PUT_NET16(skb, IPSET_ATTR_PORT, data->port);
  110 + NLA_PUT_U8(skb, IPSET_ATTR_CIDR2, data->cidr);
  111 + NLA_PUT_U8(skb, IPSET_ATTR_PROTO, data->proto);
  112 + return 0;
  113 +
  114 +nla_put_failure:
  115 + return 1;
  116 +}
  117 +
  118 +static bool
  119 +hash_ipportnet4_data_tlist(struct sk_buff *skb,
  120 + const struct hash_ipportnet4_elem *data)
  121 +{
  122 + const struct hash_ipportnet4_telem *tdata =
  123 + (const struct hash_ipportnet4_telem *)data;
  124 +
  125 + NLA_PUT_IPADDR4(skb, IPSET_ATTR_IP, tdata->ip);
  126 + NLA_PUT_IPADDR4(skb, IPSET_ATTR_IP2, tdata->ip2);
  127 + NLA_PUT_NET16(skb, IPSET_ATTR_PORT, tdata->port);
  128 + NLA_PUT_U8(skb, IPSET_ATTR_CIDR2, data->cidr);
  129 + NLA_PUT_U8(skb, IPSET_ATTR_PROTO, data->proto);
  130 + NLA_PUT_NET32(skb, IPSET_ATTR_TIMEOUT,
  131 + htonl(ip_set_timeout_get(tdata->timeout)));
  132 +
  133 + return 0;
  134 +
  135 +nla_put_failure:
  136 + return 1;
  137 +}
  138 +
  139 +#define IP_SET_HASH_WITH_PROTO
  140 +#define IP_SET_HASH_WITH_NETS
  141 +
  142 +#define PF 4
  143 +#define HOST_MASK 32
  144 +#include <linux/netfilter/ipset/ip_set_ahash.h>
  145 +
  146 +static int
  147 +hash_ipportnet4_kadt(struct ip_set *set, const struct sk_buff *skb,
  148 + enum ipset_adt adt, u8 pf, u8 dim, u8 flags)
  149 +{
  150 + const struct ip_set_hash *h = set->data;
  151 + ipset_adtfn adtfn = set->variant->adt[adt];
  152 + struct hash_ipportnet4_elem data =
  153 + { .cidr = h->nets[0].cidr || HOST_MASK };
  154 +
  155 + if (data.cidr == 0)
  156 + return -EINVAL;
  157 + if (adt == IPSET_TEST)
  158 + data.cidr = HOST_MASK;
  159 +
  160 + if (!ip_set_get_ip4_port(skb, flags & IPSET_DIM_TWO_SRC,
  161 + &data.port, &data.proto))
  162 + return -EINVAL;
  163 +
  164 + ip4addrptr(skb, flags & IPSET_DIM_ONE_SRC, &data.ip);
  165 + ip4addrptr(skb, flags & IPSET_DIM_THREE_SRC, &data.ip2);
  166 + data.ip2 &= ip_set_netmask(data.cidr);
  167 +
  168 + return adtfn(set, &data, h->timeout);
  169 +}
  170 +
  171 +static int
  172 +hash_ipportnet4_uadt(struct ip_set *set, struct nlattr *tb[],
  173 + enum ipset_adt adt, u32 *lineno, u32 flags)
  174 +{
  175 + const struct ip_set_hash *h = set->data;
  176 + ipset_adtfn adtfn = set->variant->adt[adt];
  177 + struct hash_ipportnet4_elem data = { .cidr = HOST_MASK };
  178 + u32 ip, ip_to, p, port, port_to;
  179 + u32 timeout = h->timeout;
  180 + int ret;
  181 +
  182 + if (unlikely(!tb[IPSET_ATTR_IP] || !tb[IPSET_ATTR_IP2] ||
  183 + !ip_set_attr_netorder(tb, IPSET_ATTR_PORT) ||
  184 + !ip_set_optattr_netorder(tb, IPSET_ATTR_PORT_TO) ||
  185 + !ip_set_optattr_netorder(tb, IPSET_ATTR_TIMEOUT)))
  186 + return -IPSET_ERR_PROTOCOL;
  187 +
  188 + if (tb[IPSET_ATTR_LINENO])
  189 + *lineno = nla_get_u32(tb[IPSET_ATTR_LINENO]);
  190 +
  191 + ret = ip_set_get_ipaddr4(tb[IPSET_ATTR_IP], &data.ip);
  192 + if (ret)
  193 + return ret;
  194 +
  195 + ret = ip_set_get_ipaddr4(tb[IPSET_ATTR_IP2], &data.ip2);
  196 + if (ret)
  197 + return ret;
  198 +
  199 + if (tb[IPSET_ATTR_CIDR2])
  200 + data.cidr = nla_get_u8(tb[IPSET_ATTR_CIDR2]);
  201 +
  202 + if (!data.cidr)
  203 + return -IPSET_ERR_INVALID_CIDR;
  204 +
  205 + data.ip2 &= ip_set_netmask(data.cidr);
  206 +
  207 + if (tb[IPSET_ATTR_PORT])
  208 + data.port = nla_get_be16(tb[IPSET_ATTR_PORT]);
  209 + else
  210 + return -IPSET_ERR_PROTOCOL;
  211 +
  212 + if (tb[IPSET_ATTR_PROTO]) {
  213 + data.proto = nla_get_u8(tb[IPSET_ATTR_PROTO]);
  214 +
  215 + if (data.proto == 0)
  216 + return -IPSET_ERR_INVALID_PROTO;
  217 + } else
  218 + return -IPSET_ERR_MISSING_PROTO;
  219 +
  220 + switch (data.proto) {
  221 + case IPPROTO_UDP:
  222 + case IPPROTO_TCP:
  223 + case IPPROTO_ICMP:
  224 + break;
  225 + default:
  226 + data.port = 0;
  227 + break;
  228 + }
  229 +
  230 + if (tb[IPSET_ATTR_TIMEOUT]) {
  231 + if (!with_timeout(h->timeout))
  232 + return -IPSET_ERR_TIMEOUT;
  233 + timeout = ip_set_timeout_uget(tb[IPSET_ATTR_TIMEOUT]);
  234 + }
  235 +
  236 + if (adt == IPSET_TEST ||
  237 + !(data.proto == IPPROTO_TCP || data.proto == IPPROTO_UDP) ||
  238 + !(tb[IPSET_ATTR_IP_TO] || tb[IPSET_ATTR_CIDR] ||
  239 + tb[IPSET_ATTR_PORT_TO])) {
  240 + ret = adtfn(set, &data, timeout);
  241 + return ip_set_eexist(ret, flags) ? 0 : ret;
  242 + }
  243 +
  244 + ip = ntohl(data.ip);
  245 + if (tb[IPSET_ATTR_IP_TO]) {
  246 + ret = ip_set_get_hostipaddr4(tb[IPSET_ATTR_IP_TO], &ip_to);
  247 + if (ret)
  248 + return ret;
  249 + if (ip > ip_to)
  250 + swap(ip, ip_to);
  251 + } else if (tb[IPSET_ATTR_CIDR]) {
  252 + u8 cidr = nla_get_u8(tb[IPSET_ATTR_CIDR]);
  253 +
  254 + if (cidr > 32)
  255 + return -IPSET_ERR_INVALID_CIDR;
  256 + ip &= ip_set_hostmask(cidr);
  257 + ip_to = ip | ~ip_set_hostmask(cidr);
  258 + } else
  259 + ip_to = ip;
  260 +
  261 + port = ntohs(data.port);
  262 + if (tb[IPSET_ATTR_PORT_TO]) {
  263 + port_to = ip_set_get_h16(tb[IPSET_ATTR_PORT_TO]);
  264 + if (port > port_to)
  265 + swap(port, port_to);
  266 + } else
  267 + port_to = port;
  268 +
  269 + for (; !before(ip_to, ip); ip++)
  270 + for (p = port; p <= port_to; p++) {
  271 + data.ip = htonl(ip);
  272 + data.port = htons(p);
  273 + ret = adtfn(set, &data, timeout);
  274 +
  275 + if (ret && !ip_set_eexist(ret, flags))
  276 + return ret;
  277 + else
  278 + ret = 0;
  279 + }
  280 + return ret;
  281 +}
  282 +
  283 +static bool
  284 +hash_ipportnet_same_set(const struct ip_set *a, const struct ip_set *b)
  285 +{
  286 + const struct ip_set_hash *x = a->data;
  287 + const struct ip_set_hash *y = b->data;
  288 +
  289 + /* Resizing changes htable_bits, so we ignore it */
  290 + return x->maxelem == y->maxelem &&
  291 + x->timeout == y->timeout;
  292 +}
  293 +
  294 +/* The type variant functions: IPv6 */
  295 +
  296 +struct hash_ipportnet6_elem {
  297 + union nf_inet_addr ip;
  298 + union nf_inet_addr ip2;
  299 + __be16 port;
  300 + u8 cidr;
  301 + u8 proto;
  302 +};
  303 +
  304 +struct hash_ipportnet6_telem {
  305 + union nf_inet_addr ip;
  306 + union nf_inet_addr ip2;
  307 + __be16 port;
  308 + u8 cidr;
  309 + u8 proto;
  310 + unsigned long timeout;
  311 +};
  312 +
  313 +static inline bool
  314 +hash_ipportnet6_data_equal(const struct hash_ipportnet6_elem *ip1,
  315 + const struct hash_ipportnet6_elem *ip2)
  316 +{
  317 + return ipv6_addr_cmp(&ip1->ip.in6, &ip2->ip.in6) == 0 &&
  318 + ipv6_addr_cmp(&ip1->ip2.in6, &ip2->ip2.in6) == 0 &&
  319 + ip1->cidr == ip2->cidr &&
  320 + ip1->port == ip2->port &&
  321 + ip1->proto == ip2->proto;
  322 +}
  323 +
  324 +static inline bool
  325 +hash_ipportnet6_data_isnull(const struct hash_ipportnet6_elem *elem)
  326 +{
  327 + return elem->proto == 0;
  328 +}
  329 +
  330 +static inline void
  331 +hash_ipportnet6_data_copy(struct hash_ipportnet6_elem *dst,
  332 + const struct hash_ipportnet6_elem *src)
  333 +{
  334 + memcpy(dst, src, sizeof(*dst));
  335 +}
  336 +
  337 +static inline void
  338 +hash_ipportnet6_data_zero_out(struct hash_ipportnet6_elem *elem)
  339 +{
  340 + elem->proto = 0;
  341 +}
  342 +
  343 +static inline void
  344 +ip6_netmask(union nf_inet_addr *ip, u8 prefix)
  345 +{
  346 + ip->ip6[0] &= ip_set_netmask6(prefix)[0];
  347 + ip->ip6[1] &= ip_set_netmask6(prefix)[1];
  348 + ip->ip6[2] &= ip_set_netmask6(prefix)[2];
  349 + ip->ip6[3] &= ip_set_netmask6(prefix)[3];
  350 +}
  351 +
  352 +static inline void
  353 +hash_ipportnet6_data_netmask(struct hash_ipportnet6_elem *elem, u8 cidr)
  354 +{
  355 + ip6_netmask(&elem->ip2, cidr);
  356 + elem->cidr = cidr;
  357 +}
  358 +
  359 +static bool
  360 +hash_ipportnet6_data_list(struct sk_buff *skb,
  361 + const struct hash_ipportnet6_elem *data)
  362 +{
  363 + NLA_PUT_IPADDR6(skb, IPSET_ATTR_IP, &data->ip);
  364 + NLA_PUT_IPADDR6(skb, IPSET_ATTR_IP2, &data->ip2);
  365 + NLA_PUT_NET16(skb, IPSET_ATTR_PORT, data->port);
  366 + NLA_PUT_U8(skb, IPSET_ATTR_CIDR2, data->cidr);
  367 + NLA_PUT_U8(skb, IPSET_ATTR_PROTO, data->proto);
  368 + return 0;
  369 +
  370 +nla_put_failure:
  371 + return 1;
  372 +}
  373 +
  374 +static bool
  375 +hash_ipportnet6_data_tlist(struct sk_buff *skb,
  376 + const struct hash_ipportnet6_elem *data)
  377 +{
  378 + const struct hash_ipportnet6_telem *e =
  379 + (const struct hash_ipportnet6_telem *)data;
  380 +
  381 + NLA_PUT_IPADDR6(skb, IPSET_ATTR_IP, &e->ip);
  382 + NLA_PUT_IPADDR6(skb, IPSET_ATTR_IP2, &data->ip2);
  383 + NLA_PUT_NET16(skb, IPSET_ATTR_PORT, data->port);
  384 + NLA_PUT_U8(skb, IPSET_ATTR_CIDR2, data->cidr);
  385 + NLA_PUT_U8(skb, IPSET_ATTR_PROTO, data->proto);
  386 + NLA_PUT_NET32(skb, IPSET_ATTR_TIMEOUT,
  387 + htonl(ip_set_timeout_get(e->timeout)));
  388 + return 0;
  389 +
  390 +nla_put_failure:
  391 + return 1;
  392 +}
  393 +
  394 +#undef PF
  395 +#undef HOST_MASK
  396 +
  397 +#define PF 6
  398 +#define HOST_MASK 128
  399 +#include <linux/netfilter/ipset/ip_set_ahash.h>
  400 +
  401 +static int
  402 +hash_ipportnet6_kadt(struct ip_set *set, const struct sk_buff *skb,
  403 + enum ipset_adt adt, u8 pf, u8 dim, u8 flags)
  404 +{
  405 + const struct ip_set_hash *h = set->data;
  406 + ipset_adtfn adtfn = set->variant->adt[adt];
  407 + struct hash_ipportnet6_elem data =
  408 + { .cidr = h->nets[0].cidr || HOST_MASK };
  409 +
  410 + if (data.cidr == 0)
  411 + return -EINVAL;
  412 + if (adt == IPSET_TEST)
  413 + data.cidr = HOST_MASK;
  414 +
  415 + if (!ip_set_get_ip6_port(skb, flags & IPSET_DIM_TWO_SRC,
  416 + &data.port, &data.proto))
  417 + return -EINVAL;
  418 +
  419 + ip6addrptr(skb, flags & IPSET_DIM_ONE_SRC, &data.ip.in6);
  420 + ip6addrptr(skb, flags & IPSET_DIM_THREE_SRC, &data.ip2.in6);
  421 + ip6_netmask(&data.ip2, data.cidr);
  422 +
  423 + return adtfn(set, &data, h->timeout);
  424 +}
  425 +
  426 +static int
  427 +hash_ipportnet6_uadt(struct ip_set *set, struct nlattr *tb[],
  428 + enum ipset_adt adt, u32 *lineno, u32 flags)
  429 +{
  430 + const struct ip_set_hash *h = set->data;
  431 + ipset_adtfn adtfn = set->variant->adt[adt];
  432 + struct hash_ipportnet6_elem data = { .cidr = HOST_MASK };
  433 + u32 port, port_to;
  434 + u32 timeout = h->timeout;
  435 + int ret;
  436 +
  437 + if (unlikely(!tb[IPSET_ATTR_IP] || !tb[IPSET_ATTR_IP2] ||
  438 + !ip_set_attr_netorder(tb, IPSET_ATTR_PORT) ||
  439 + !ip_set_optattr_netorder(tb, IPSET_ATTR_PORT_TO) ||
  440 + !ip_set_optattr_netorder(tb, IPSET_ATTR_TIMEOUT) ||
  441 + tb[IPSET_ATTR_IP_TO] ||
  442 + tb[IPSET_ATTR_CIDR]))
  443 + return -IPSET_ERR_PROTOCOL;
  444 +
  445 + if (tb[IPSET_ATTR_LINENO])
  446 + *lineno = nla_get_u32(tb[IPSET_ATTR_LINENO]);
  447 +
  448 + ret = ip_set_get_ipaddr6(tb[IPSET_ATTR_IP], &data.ip);
  449 + if (ret)
  450 + return ret;
  451 +
  452 + ret = ip_set_get_ipaddr6(tb[IPSET_ATTR_IP2], &data.ip2);
  453 + if (ret)
  454 + return ret;
  455 +
  456 + if (tb[IPSET_ATTR_CIDR2])
  457 + data.cidr = nla_get_u8(tb[IPSET_ATTR_CIDR2]);
  458 +
  459 + if (!data.cidr)
  460 + return -IPSET_ERR_INVALID_CIDR;
  461 +
  462 + ip6_netmask(&data.ip2, data.cidr);
  463 +
  464 + if (tb[IPSET_ATTR_PORT])
  465 + data.port = nla_get_be16(tb[IPSET_ATTR_PORT]);
  466 + else
  467 + return -IPSET_ERR_PROTOCOL;
  468 +
  469 + if (tb[IPSET_ATTR_PROTO]) {
  470 + data.proto = nla_get_u8(tb[IPSET_ATTR_PROTO]);
  471 +
  472 + if (data.proto == 0)
  473 + return -IPSET_ERR_INVALID_PROTO;
  474 + } else
  475 + return -IPSET_ERR_MISSING_PROTO;
  476 +
  477 + switch (data.proto) {
  478 + case IPPROTO_UDP:
  479 + case IPPROTO_TCP:
  480 + case IPPROTO_ICMPV6:
  481 + break;
  482 + default:
  483 + data.port = 0;
  484 + break;
  485 + }
  486 +
  487 + if (tb[IPSET_ATTR_TIMEOUT]) {
  488 + if (!with_timeout(h->timeout))
  489 + return -IPSET_ERR_TIMEOUT;
  490 + timeout = ip_set_timeout_uget(tb[IPSET_ATTR_TIMEOUT]);
  491 + }
  492 +
  493 + if (adt == IPSET_TEST ||
  494 + !(data.proto == IPPROTO_TCP || data.proto == IPPROTO_UDP) ||
  495 + !tb[IPSET_ATTR_PORT_TO]) {
  496 + ret = adtfn(set, &data, timeout);
  497 + return ip_set_eexist(ret, flags) ? 0 : ret;
  498 + }
  499 +
  500 + port = ntohs(data.port);
  501 + port_to = ip_set_get_h16(tb[IPSET_ATTR_PORT_TO]);
  502 + if (port > port_to)
  503 + swap(port, port_to);
  504 +
  505 + for (; port <= port_to; port++) {
  506 + data.port = htons(port);
  507 + ret = adtfn(set, &data, timeout);
  508 +
  509 + if (ret && !ip_set_eexist(ret, flags))
  510 + return ret;
  511 + else
  512 + ret = 0;
  513 + }
  514 + return ret;
  515 +}
  516 +
  517 +/* Create hash:ip type of sets */
  518 +
  519 +static int
  520 +hash_ipportnet_create(struct ip_set *set, struct nlattr *tb[], u32 flags)
  521 +{
  522 + struct ip_set_hash *h;
  523 + u32 hashsize = IPSET_DEFAULT_HASHSIZE, maxelem = IPSET_DEFAULT_MAXELEM;
  524 + u8 hbits;
  525 +
  526 + if (!(set->family == AF_INET || set->family == AF_INET6))
  527 + return -IPSET_ERR_INVALID_FAMILY;
  528 +
  529 + if (unlikely(!ip_set_optattr_netorder(tb, IPSET_ATTR_HASHSIZE) ||
  530 + !ip_set_optattr_netorder(tb, IPSET_ATTR_MAXELEM) ||
  531 + !ip_set_optattr_netorder(tb, IPSET_ATTR_TIMEOUT)))
  532 + return -IPSET_ERR_PROTOCOL;
  533 +
  534 + if (tb[IPSET_ATTR_HASHSIZE]) {
  535 + hashsize = ip_set_get_h32(tb[IPSET_ATTR_HASHSIZE]);
  536 + if (hashsize < IPSET_MIMINAL_HASHSIZE)
  537 + hashsize = IPSET_MIMINAL_HASHSIZE;
  538 + }
  539 +
  540 + if (tb[IPSET_ATTR_MAXELEM])
  541 + maxelem = ip_set_get_h32(tb[IPSET_ATTR_MAXELEM]);
  542 +
  543 + h = kzalloc(sizeof(*h)
  544 + + sizeof(struct ip_set_hash_nets)
  545 + * (set->family == AF_INET ? 32 : 128), GFP_KERNEL);
  546 + if (!h)
  547 + return -ENOMEM;
  548 +
  549 + h->maxelem = maxelem;
  550 + get_random_bytes(&h->initval, sizeof(h->initval));
  551 + h->timeout = IPSET_NO_TIMEOUT;
  552 +
  553 + hbits = htable_bits(hashsize);
  554 + h->table = ip_set_alloc(
  555 + sizeof(struct htable)
  556 + + jhash_size(hbits) * sizeof(struct hbucket));
  557 + if (!h->table) {
  558 + kfree(h);
  559 + return -ENOMEM;
  560 + }
  561 + h->table->htable_bits = hbits;
  562 +
  563 + set->data = h;
  564 +
  565 + if (tb[IPSET_ATTR_TIMEOUT]) {
  566 + h->timeout = ip_set_timeout_uget(tb[IPSET_ATTR_TIMEOUT]);
  567 +
  568 + set->variant = set->family == AF_INET
  569 + ? &hash_ipportnet4_tvariant
  570 + : &hash_ipportnet6_tvariant;
  571 +
  572 + if (set->family == AF_INET)
  573 + hash_ipportnet4_gc_init(set);
  574 + else
  575 + hash_ipportnet6_gc_init(set);
  576 + } else {
  577 + set->variant = set->family == AF_INET
  578 + ? &hash_ipportnet4_variant : &hash_ipportnet6_variant;
  579 + }
  580 +
  581 + pr_debug("create %s hashsize %u (%u) maxelem %u: %p(%p)\n",
  582 + set->name, jhash_size(h->table->htable_bits),
  583 + h->table->htable_bits, h->maxelem, set->data, h->table);
  584 +
  585 + return 0;
  586 +}
  587 +
  588 +static struct ip_set_type hash_ipportnet_type __read_mostly = {
  589 + .name = "hash:ip,port,net",
  590 + .protocol = IPSET_PROTOCOL,
  591 + .features = IPSET_TYPE_IP | IPSET_TYPE_PORT | IPSET_TYPE_IP2,
  592 + .dimension = IPSET_DIM_THREE,
  593 + .family = AF_UNSPEC,
  594 + .revision = 0,
  595 + .create = hash_ipportnet_create,
  596 + .create_policy = {
  597 + [IPSET_ATTR_HASHSIZE] = { .type = NLA_U32 },
  598 + [IPSET_ATTR_MAXELEM] = { .type = NLA_U32 },
  599 + [IPSET_ATTR_PROBES] = { .type = NLA_U8 },
  600 + [IPSET_ATTR_RESIZE] = { .type = NLA_U8 },
  601 + [IPSET_ATTR_TIMEOUT] = { .type = NLA_U32 },
  602 + },
  603 + .adt_policy = {
  604 + [IPSET_ATTR_IP] = { .type = NLA_NESTED },
  605 + [IPSET_ATTR_IP_TO] = { .type = NLA_NESTED },
  606 + [IPSET_ATTR_IP2] = { .type = NLA_NESTED },
  607 + [IPSET_ATTR_PORT] = { .type = NLA_U16 },
  608 + [IPSET_ATTR_PORT_TO] = { .type = NLA_U16 },
  609 + [IPSET_ATTR_CIDR] = { .type = NLA_U8 },
  610 + [IPSET_ATTR_CIDR2] = { .type = NLA_U8 },
  611 + [IPSET_ATTR_PROTO] = { .type = NLA_U8 },
  612 + [IPSET_ATTR_TIMEOUT] = { .type = NLA_U32 },
  613 + [IPSET_ATTR_LINENO] = { .type = NLA_U32 },
  614 + },
  615 + .me = THIS_MODULE,
  616 +};
  617 +
  618 +static int __init
  619 +hash_ipportnet_init(void)
  620 +{
  621 + return ip_set_type_register(&hash_ipportnet_type);
  622 +}
  623 +
  624 +static void __exit
  625 +hash_ipportnet_fini(void)
  626 +{
  627 + ip_set_type_unregister(&hash_ipportnet_type);
  628 +}
  629 +
  630 +module_init(hash_ipportnet_init);
  631 +module_exit(hash_ipportnet_fini);