Commit 07896ed37b94599a1b8ea97f4bd5766be71390f4

Authored by Jozsef Kadlecsik
Committed by Patrick McHardy
1 parent 6c02788969

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

The module implements the hash:ip,port type support in four flavours:
for IPv4 and IPv6, both without and with timeout support. The elements
are two dimensional: IPv4/IPv6 address and protocol/port pairs. The port
is interpeted for TCP, UPD, ICMP and ICMPv6 (at the latters as type/code
of course).

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

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

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