Commit b38370299eeaba4cf8a9e0c5c6acc2a1e049be23

Authored by Jozsef Kadlecsik
Committed by Patrick McHardy
1 parent 41d22f7b2e

netfilter: ipset: hash:net set type support

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

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

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

net/netfilter/ipset/Kconfig
... ... @@ -89,5 +89,14 @@
89 89  
90 90 To compile it as a module, choose M here. If unsure, say N.
91 91  
  92 +config IP_SET_HASH_NET
  93 + tristate "hash:net set support"
  94 + depends on IP_SET
  95 + help
  96 + This option adds the hash:net set type support, by which
  97 + one can store IPv4/IPv6 network address/prefix elements in a set.
  98 +
  99 + To compile it as a module, choose M here. If unsure, say N.
  100 +
92 101 endif # IP_SET
net/netfilter/ipset/Makefile
... ... @@ -17,4 +17,5 @@
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 19 obj-$(CONFIG_IP_SET_HASH_IPPORTNET) += ip_set_hash_ipportnet.o
  20 +obj-$(CONFIG_IP_SET_HASH_NET) += ip_set_hash_net.o
net/netfilter/ipset/ip_set_hash_net.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 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_hash.h>
  28 +
  29 +MODULE_LICENSE("GPL");
  30 +MODULE_AUTHOR("Jozsef Kadlecsik <kadlec@blackhole.kfki.hu>");
  31 +MODULE_DESCRIPTION("hash:net type of IP sets");
  32 +MODULE_ALIAS("ip_set_hash:net");
  33 +
  34 +/* Type specific function prefix */
  35 +#define TYPE hash_net
  36 +
  37 +static bool
  38 +hash_net_same_set(const struct ip_set *a, const struct ip_set *b);
  39 +
  40 +#define hash_net4_same_set hash_net_same_set
  41 +#define hash_net6_same_set hash_net_same_set
  42 +
  43 +/* The type variant functions: IPv4 */
  44 +
  45 +/* Member elements without timeout */
  46 +struct hash_net4_elem {
  47 + __be32 ip;
  48 + u16 padding0;
  49 + u8 padding1;
  50 + u8 cidr;
  51 +};
  52 +
  53 +/* Member elements with timeout support */
  54 +struct hash_net4_telem {
  55 + __be32 ip;
  56 + u16 padding0;
  57 + u8 padding1;
  58 + u8 cidr;
  59 + unsigned long timeout;
  60 +};
  61 +
  62 +static inline bool
  63 +hash_net4_data_equal(const struct hash_net4_elem *ip1,
  64 + const struct hash_net4_elem *ip2)
  65 +{
  66 + return ip1->ip == ip2->ip && ip1->cidr == ip2->cidr;
  67 +}
  68 +
  69 +static inline bool
  70 +hash_net4_data_isnull(const struct hash_net4_elem *elem)
  71 +{
  72 + return elem->cidr == 0;
  73 +}
  74 +
  75 +static inline void
  76 +hash_net4_data_copy(struct hash_net4_elem *dst,
  77 + const struct hash_net4_elem *src)
  78 +{
  79 + dst->ip = src->ip;
  80 + dst->cidr = src->cidr;
  81 +}
  82 +
  83 +static inline void
  84 +hash_net4_data_netmask(struct hash_net4_elem *elem, u8 cidr)
  85 +{
  86 + elem->ip &= ip_set_netmask(cidr);
  87 + elem->cidr = cidr;
  88 +}
  89 +
  90 +/* Zero CIDR values cannot be stored */
  91 +static inline void
  92 +hash_net4_data_zero_out(struct hash_net4_elem *elem)
  93 +{
  94 + elem->cidr = 0;
  95 +}
  96 +
  97 +static bool
  98 +hash_net4_data_list(struct sk_buff *skb, const struct hash_net4_elem *data)
  99 +{
  100 + NLA_PUT_IPADDR4(skb, IPSET_ATTR_IP, data->ip);
  101 + NLA_PUT_U8(skb, IPSET_ATTR_CIDR, data->cidr);
  102 + return 0;
  103 +
  104 +nla_put_failure:
  105 + return 1;
  106 +}
  107 +
  108 +static bool
  109 +hash_net4_data_tlist(struct sk_buff *skb, const struct hash_net4_elem *data)
  110 +{
  111 + const struct hash_net4_telem *tdata =
  112 + (const struct hash_net4_telem *)data;
  113 +
  114 + NLA_PUT_IPADDR4(skb, IPSET_ATTR_IP, tdata->ip);
  115 + NLA_PUT_U8(skb, IPSET_ATTR_CIDR, tdata->cidr);
  116 + NLA_PUT_NET32(skb, IPSET_ATTR_TIMEOUT,
  117 + htonl(ip_set_timeout_get(tdata->timeout)));
  118 +
  119 + return 0;
  120 +
  121 +nla_put_failure:
  122 + return 1;
  123 +}
  124 +
  125 +#define IP_SET_HASH_WITH_NETS
  126 +
  127 +#define PF 4
  128 +#define HOST_MASK 32
  129 +#include <linux/netfilter/ipset/ip_set_ahash.h>
  130 +
  131 +static int
  132 +hash_net4_kadt(struct ip_set *set, const struct sk_buff *skb,
  133 + enum ipset_adt adt, u8 pf, u8 dim, u8 flags)
  134 +{
  135 + const struct ip_set_hash *h = set->data;
  136 + ipset_adtfn adtfn = set->variant->adt[adt];
  137 + struct hash_net4_elem data = { .cidr = h->nets[0].cidr || HOST_MASK };
  138 +
  139 + if (data.cidr == 0)
  140 + return -EINVAL;
  141 + if (adt == IPSET_TEST)
  142 + data.cidr = HOST_MASK;
  143 +
  144 + ip4addrptr(skb, flags & IPSET_DIM_ONE_SRC, &data.ip);
  145 + data.ip &= ip_set_netmask(data.cidr);
  146 +
  147 + return adtfn(set, &data, h->timeout);
  148 +}
  149 +
  150 +static int
  151 +hash_net4_uadt(struct ip_set *set, struct nlattr *tb[],
  152 + enum ipset_adt adt, u32 *lineno, u32 flags)
  153 +{
  154 + const struct ip_set_hash *h = set->data;
  155 + ipset_adtfn adtfn = set->variant->adt[adt];
  156 + struct hash_net4_elem data = { .cidr = HOST_MASK };
  157 + u32 timeout = h->timeout;
  158 + int ret;
  159 +
  160 + if (unlikely(!tb[IPSET_ATTR_IP] ||
  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_CIDR])
  172 + data.cidr = nla_get_u8(tb[IPSET_ATTR_CIDR]);
  173 +
  174 + if (!data.cidr)
  175 + return -IPSET_ERR_INVALID_CIDR;
  176 +
  177 + data.ip &= ip_set_netmask(data.cidr);
  178 +
  179 + if (tb[IPSET_ATTR_TIMEOUT]) {
  180 + if (!with_timeout(h->timeout))
  181 + return -IPSET_ERR_TIMEOUT;
  182 + timeout = ip_set_timeout_uget(tb[IPSET_ATTR_TIMEOUT]);
  183 + }
  184 +
  185 + ret = adtfn(set, &data, timeout);
  186 +
  187 + return ip_set_eexist(ret, flags) ? 0 : ret;
  188 +}
  189 +
  190 +static bool
  191 +hash_net_same_set(const struct ip_set *a, const struct ip_set *b)
  192 +{
  193 + const struct ip_set_hash *x = a->data;
  194 + const struct ip_set_hash *y = b->data;
  195 +
  196 + /* Resizing changes htable_bits, so we ignore it */
  197 + return x->maxelem == y->maxelem &&
  198 + x->timeout == y->timeout;
  199 +}
  200 +
  201 +/* The type variant functions: IPv6 */
  202 +
  203 +struct hash_net6_elem {
  204 + union nf_inet_addr ip;
  205 + u16 padding0;
  206 + u8 padding1;
  207 + u8 cidr;
  208 +};
  209 +
  210 +struct hash_net6_telem {
  211 + union nf_inet_addr ip;
  212 + u16 padding0;
  213 + u8 padding1;
  214 + u8 cidr;
  215 + unsigned long timeout;
  216 +};
  217 +
  218 +static inline bool
  219 +hash_net6_data_equal(const struct hash_net6_elem *ip1,
  220 + const struct hash_net6_elem *ip2)
  221 +{
  222 + return ipv6_addr_cmp(&ip1->ip.in6, &ip2->ip.in6) == 0 &&
  223 + ip1->cidr == ip2->cidr;
  224 +}
  225 +
  226 +static inline bool
  227 +hash_net6_data_isnull(const struct hash_net6_elem *elem)
  228 +{
  229 + return elem->cidr == 0;
  230 +}
  231 +
  232 +static inline void
  233 +hash_net6_data_copy(struct hash_net6_elem *dst,
  234 + const struct hash_net6_elem *src)
  235 +{
  236 + ipv6_addr_copy(&dst->ip.in6, &src->ip.in6);
  237 + dst->cidr = src->cidr;
  238 +}
  239 +
  240 +static inline void
  241 +hash_net6_data_zero_out(struct hash_net6_elem *elem)
  242 +{
  243 + elem->cidr = 0;
  244 +}
  245 +
  246 +static inline void
  247 +ip6_netmask(union nf_inet_addr *ip, u8 prefix)
  248 +{
  249 + ip->ip6[0] &= ip_set_netmask6(prefix)[0];
  250 + ip->ip6[1] &= ip_set_netmask6(prefix)[1];
  251 + ip->ip6[2] &= ip_set_netmask6(prefix)[2];
  252 + ip->ip6[3] &= ip_set_netmask6(prefix)[3];
  253 +}
  254 +
  255 +static inline void
  256 +hash_net6_data_netmask(struct hash_net6_elem *elem, u8 cidr)
  257 +{
  258 + ip6_netmask(&elem->ip, cidr);
  259 + elem->cidr = cidr;
  260 +}
  261 +
  262 +static bool
  263 +hash_net6_data_list(struct sk_buff *skb, const struct hash_net6_elem *data)
  264 +{
  265 + NLA_PUT_IPADDR6(skb, IPSET_ATTR_IP, &data->ip);
  266 + NLA_PUT_U8(skb, IPSET_ATTR_CIDR, data->cidr);
  267 + return 0;
  268 +
  269 +nla_put_failure:
  270 + return 1;
  271 +}
  272 +
  273 +static bool
  274 +hash_net6_data_tlist(struct sk_buff *skb, const struct hash_net6_elem *data)
  275 +{
  276 + const struct hash_net6_telem *e =
  277 + (const struct hash_net6_telem *)data;
  278 +
  279 + NLA_PUT_IPADDR6(skb, IPSET_ATTR_IP, &e->ip);
  280 + NLA_PUT_U8(skb, IPSET_ATTR_CIDR, e->cidr);
  281 + NLA_PUT_NET32(skb, IPSET_ATTR_TIMEOUT,
  282 + htonl(ip_set_timeout_get(e->timeout)));
  283 + return 0;
  284 +
  285 +nla_put_failure:
  286 + return 1;
  287 +}
  288 +
  289 +#undef PF
  290 +#undef HOST_MASK
  291 +
  292 +#define PF 6
  293 +#define HOST_MASK 128
  294 +#include <linux/netfilter/ipset/ip_set_ahash.h>
  295 +
  296 +static int
  297 +hash_net6_kadt(struct ip_set *set, const struct sk_buff *skb,
  298 + enum ipset_adt adt, u8 pf, u8 dim, u8 flags)
  299 +{
  300 + const struct ip_set_hash *h = set->data;
  301 + ipset_adtfn adtfn = set->variant->adt[adt];
  302 + struct hash_net6_elem data = { .cidr = h->nets[0].cidr || HOST_MASK };
  303 +
  304 + if (data.cidr == 0)
  305 + return -EINVAL;
  306 + if (adt == IPSET_TEST)
  307 + data.cidr = HOST_MASK;
  308 +
  309 + ip6addrptr(skb, flags & IPSET_DIM_ONE_SRC, &data.ip.in6);
  310 + ip6_netmask(&data.ip, data.cidr);
  311 +
  312 + return adtfn(set, &data, h->timeout);
  313 +}
  314 +
  315 +static int
  316 +hash_net6_uadt(struct ip_set *set, struct nlattr *tb[],
  317 + enum ipset_adt adt, u32 *lineno, u32 flags)
  318 +{
  319 + const struct ip_set_hash *h = set->data;
  320 + ipset_adtfn adtfn = set->variant->adt[adt];
  321 + struct hash_net6_elem data = { .cidr = HOST_MASK };
  322 + u32 timeout = h->timeout;
  323 + int ret;
  324 +
  325 + if (unlikely(!tb[IPSET_ATTR_IP] ||
  326 + !ip_set_optattr_netorder(tb, IPSET_ATTR_TIMEOUT)))
  327 + return -IPSET_ERR_PROTOCOL;
  328 +
  329 + if (tb[IPSET_ATTR_LINENO])
  330 + *lineno = nla_get_u32(tb[IPSET_ATTR_LINENO]);
  331 +
  332 + ret = ip_set_get_ipaddr6(tb[IPSET_ATTR_IP], &data.ip);
  333 + if (ret)
  334 + return ret;
  335 +
  336 + if (tb[IPSET_ATTR_CIDR])
  337 + data.cidr = nla_get_u8(tb[IPSET_ATTR_CIDR]);
  338 +
  339 + if (!data.cidr)
  340 + return -IPSET_ERR_INVALID_CIDR;
  341 +
  342 + ip6_netmask(&data.ip, data.cidr);
  343 +
  344 + if (tb[IPSET_ATTR_TIMEOUT]) {
  345 + if (!with_timeout(h->timeout))
  346 + return -IPSET_ERR_TIMEOUT;
  347 + timeout = ip_set_timeout_uget(tb[IPSET_ATTR_TIMEOUT]);
  348 + }
  349 +
  350 + ret = adtfn(set, &data, timeout);
  351 +
  352 + return ip_set_eexist(ret, flags) ? 0 : ret;
  353 +}
  354 +
  355 +/* Create hash:ip type of sets */
  356 +
  357 +static int
  358 +hash_net_create(struct ip_set *set, struct nlattr *tb[], u32 flags)
  359 +{
  360 + u32 hashsize = IPSET_DEFAULT_HASHSIZE, maxelem = IPSET_DEFAULT_MAXELEM;
  361 + struct ip_set_hash *h;
  362 + u8 hbits;
  363 +
  364 + if (!(set->family == AF_INET || set->family == AF_INET6))
  365 + return -IPSET_ERR_INVALID_FAMILY;
  366 +
  367 + if (unlikely(!ip_set_optattr_netorder(tb, IPSET_ATTR_HASHSIZE) ||
  368 + !ip_set_optattr_netorder(tb, IPSET_ATTR_MAXELEM) ||
  369 + !ip_set_optattr_netorder(tb, IPSET_ATTR_TIMEOUT)))
  370 + return -IPSET_ERR_PROTOCOL;
  371 +
  372 + if (tb[IPSET_ATTR_HASHSIZE]) {
  373 + hashsize = ip_set_get_h32(tb[IPSET_ATTR_HASHSIZE]);
  374 + if (hashsize < IPSET_MIMINAL_HASHSIZE)
  375 + hashsize = IPSET_MIMINAL_HASHSIZE;
  376 + }
  377 +
  378 + if (tb[IPSET_ATTR_MAXELEM])
  379 + maxelem = ip_set_get_h32(tb[IPSET_ATTR_MAXELEM]);
  380 +
  381 + h = kzalloc(sizeof(*h)
  382 + + sizeof(struct ip_set_hash_nets)
  383 + * (set->family == AF_INET ? 32 : 128), GFP_KERNEL);
  384 + if (!h)
  385 + return -ENOMEM;
  386 +
  387 + h->maxelem = maxelem;
  388 + get_random_bytes(&h->initval, sizeof(h->initval));
  389 + h->timeout = IPSET_NO_TIMEOUT;
  390 +
  391 + hbits = htable_bits(hashsize);
  392 + h->table = ip_set_alloc(
  393 + sizeof(struct htable)
  394 + + jhash_size(hbits) * sizeof(struct hbucket));
  395 + if (!h->table) {
  396 + kfree(h);
  397 + return -ENOMEM;
  398 + }
  399 + h->table->htable_bits = hbits;
  400 +
  401 + set->data = h;
  402 +
  403 + if (tb[IPSET_ATTR_TIMEOUT]) {
  404 + h->timeout = ip_set_timeout_uget(tb[IPSET_ATTR_TIMEOUT]);
  405 +
  406 + set->variant = set->family == AF_INET
  407 + ? &hash_net4_tvariant : &hash_net6_tvariant;
  408 +
  409 + if (set->family == AF_INET)
  410 + hash_net4_gc_init(set);
  411 + else
  412 + hash_net6_gc_init(set);
  413 + } else {
  414 + set->variant = set->family == AF_INET
  415 + ? &hash_net4_variant : &hash_net6_variant;
  416 + }
  417 +
  418 + pr_debug("create %s hashsize %u (%u) maxelem %u: %p(%p)\n",
  419 + set->name, jhash_size(h->table->htable_bits),
  420 + h->table->htable_bits, h->maxelem, set->data, h->table);
  421 +
  422 + return 0;
  423 +}
  424 +
  425 +static struct ip_set_type hash_net_type __read_mostly = {
  426 + .name = "hash:net",
  427 + .protocol = IPSET_PROTOCOL,
  428 + .features = IPSET_TYPE_IP,
  429 + .dimension = IPSET_DIM_ONE,
  430 + .family = AF_UNSPEC,
  431 + .revision = 0,
  432 + .create = hash_net_create,
  433 + .create_policy = {
  434 + [IPSET_ATTR_HASHSIZE] = { .type = NLA_U32 },
  435 + [IPSET_ATTR_MAXELEM] = { .type = NLA_U32 },
  436 + [IPSET_ATTR_PROBES] = { .type = NLA_U8 },
  437 + [IPSET_ATTR_RESIZE] = { .type = NLA_U8 },
  438 + [IPSET_ATTR_TIMEOUT] = { .type = NLA_U32 },
  439 + },
  440 + .adt_policy = {
  441 + [IPSET_ATTR_IP] = { .type = NLA_NESTED },
  442 + [IPSET_ATTR_CIDR] = { .type = NLA_U8 },
  443 + [IPSET_ATTR_TIMEOUT] = { .type = NLA_U32 },
  444 + },
  445 + .me = THIS_MODULE,
  446 +};
  447 +
  448 +static int __init
  449 +hash_net_init(void)
  450 +{
  451 + return ip_set_type_register(&hash_net_type);
  452 +}
  453 +
  454 +static void __exit
  455 +hash_net_fini(void)
  456 +{
  457 + ip_set_type_unregister(&hash_net_type);
  458 +}
  459 +
  460 +module_init(hash_net_init);
  461 +module_exit(hash_net_fini);