Commit 21f45020a3084f80fcdd5f056a0c6389f5406399

Authored by Jozsef Kadlecsik
Committed by Patrick McHardy
1 parent b38370299e

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

The module implements the hash:net,port type support in four flavours:
for IPv4 and IPv6, both without and with timeout support. The elements
are two dimensional: IPv4/IPv6 network address/prefix and protocol/port
pairs.

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

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

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