aboutsummaryrefslogtreecommitdiffstats
path: root/net/netfilter
diff options
context:
space:
mode:
authorJozsef Kadlecsik <kadlec@blackhole.kfki.hu>2011-02-01 09:52:54 -0500
committerPatrick McHardy <kaber@trash.net>2011-02-01 09:52:54 -0500
commitb38370299eeaba4cf8a9e0c5c6acc2a1e049be23 (patch)
tree5e3c1ba43c08a8273f987ed23c3642c4d44bb1fd /net/netfilter
parent41d22f7b2e48c77175b62cec3797d7d7173a626e (diff)
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>
Diffstat (limited to 'net/netfilter')
-rw-r--r--net/netfilter/ipset/Kconfig9
-rw-r--r--net/netfilter/ipset/Makefile1
-rw-r--r--net/netfilter/ipset/ip_set_hash_net.c461
3 files changed, 471 insertions, 0 deletions
diff --git a/net/netfilter/ipset/Kconfig b/net/netfilter/ipset/Kconfig
index e2fbaa9d902..8d85de00e9e 100644
--- a/net/netfilter/ipset/Kconfig
+++ b/net/netfilter/ipset/Kconfig
@@ -89,4 +89,13 @@ config IP_SET_HASH_IPPORTNET
89 89
90 To compile it as a module, choose M here. If unsure, say N. 90 To compile it as a module, choose M here. If unsure, say N.
91 91
92config 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
92endif # IP_SET 101endif # IP_SET
diff --git a/net/netfilter/ipset/Makefile b/net/netfilter/ipset/Makefile
index 9c5d857511c..fd5eeb6a7e5 100644
--- a/net/netfilter/ipset/Makefile
+++ b/net/netfilter/ipset/Makefile
@@ -17,3 +17,4 @@ obj-$(CONFIG_IP_SET_HASH_IP) += ip_set_hash_ip.o
17obj-$(CONFIG_IP_SET_HASH_IPPORT) += ip_set_hash_ipport.o 17obj-$(CONFIG_IP_SET_HASH_IPPORT) += ip_set_hash_ipport.o
18obj-$(CONFIG_IP_SET_HASH_IPPORTIP) += ip_set_hash_ipportip.o 18obj-$(CONFIG_IP_SET_HASH_IPPORTIP) += ip_set_hash_ipportip.o
19obj-$(CONFIG_IP_SET_HASH_IPPORTNET) += ip_set_hash_ipportnet.o 19obj-$(CONFIG_IP_SET_HASH_IPPORTNET) += ip_set_hash_ipportnet.o
20obj-$(CONFIG_IP_SET_HASH_NET) += ip_set_hash_net.o
diff --git a/net/netfilter/ipset/ip_set_hash_net.c b/net/netfilter/ipset/ip_set_hash_net.c
new file mode 100644
index 00000000000..fb0e6a68d13
--- /dev/null
+++ b/net/netfilter/ipset/ip_set_hash_net.c
@@ -0,0 +1,461 @@
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
29MODULE_LICENSE("GPL");
30MODULE_AUTHOR("Jozsef Kadlecsik <kadlec@blackhole.kfki.hu>");
31MODULE_DESCRIPTION("hash:net type of IP sets");
32MODULE_ALIAS("ip_set_hash:net");
33
34/* Type specific function prefix */
35#define TYPE hash_net
36
37static bool
38hash_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 */
46struct hash_net4_elem {
47 __be32 ip;
48 u16 padding0;
49 u8 padding1;
50 u8 cidr;
51};
52
53/* Member elements with timeout support */
54struct hash_net4_telem {
55 __be32 ip;
56 u16 padding0;
57 u8 padding1;
58 u8 cidr;
59 unsigned long timeout;
60};
61
62static inline bool
63hash_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
69static inline bool
70hash_net4_data_isnull(const struct hash_net4_elem *elem)
71{
72 return elem->cidr == 0;
73}
74
75static inline void
76hash_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
83static inline void
84hash_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 */
91static inline void
92hash_net4_data_zero_out(struct hash_net4_elem *elem)
93{
94 elem->cidr = 0;
95}
96
97static bool
98hash_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
104nla_put_failure:
105 return 1;
106}
107
108static bool
109hash_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
121nla_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
131static int
132hash_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
150static int
151hash_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
190static bool
191hash_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
203struct hash_net6_elem {
204 union nf_inet_addr ip;
205 u16 padding0;
206 u8 padding1;
207 u8 cidr;
208};
209
210struct hash_net6_telem {
211 union nf_inet_addr ip;
212 u16 padding0;
213 u8 padding1;
214 u8 cidr;
215 unsigned long timeout;
216};
217
218static inline bool
219hash_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
226static inline bool
227hash_net6_data_isnull(const struct hash_net6_elem *elem)
228{
229 return elem->cidr == 0;
230}
231
232static inline void
233hash_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
240static inline void
241hash_net6_data_zero_out(struct hash_net6_elem *elem)
242{
243 elem->cidr = 0;
244}
245
246static inline void
247ip6_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
255static inline void
256hash_net6_data_netmask(struct hash_net6_elem *elem, u8 cidr)
257{
258 ip6_netmask(&elem->ip, cidr);
259 elem->cidr = cidr;
260}
261
262static bool
263hash_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
269nla_put_failure:
270 return 1;
271}
272
273static bool
274hash_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
285nla_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
296static int
297hash_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
315static int
316hash_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
357static int
358hash_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
425static 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
448static int __init
449hash_net_init(void)
450{
451 return ip_set_type_register(&hash_net_type);
452}
453
454static void __exit
455hash_net_fini(void)
456{
457 ip_set_type_unregister(&hash_net_type);
458}
459
460module_init(hash_net_init);
461module_exit(hash_net_fini);