aboutsummaryrefslogtreecommitdiffstats
path: root/net/netfilter
diff options
context:
space:
mode:
authorJozsef Kadlecsik <kadlec@blackhole.kfki.hu>2011-02-01 09:51:00 -0500
committerPatrick McHardy <kaber@trash.net>2011-02-01 09:51:00 -0500
commit41d22f7b2e48c77175b62cec3797d7d7173a626e (patch)
treeb6760c287a3cc864fc16ed45b611bead7dc7523f /net/netfilter
parent5663bc30e6114b6ba88cc428619ede46a3246a7b (diff)
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>
Diffstat (limited to 'net/netfilter')
-rw-r--r--net/netfilter/ipset/Kconfig10
-rw-r--r--net/netfilter/ipset/Makefile1
-rw-r--r--net/netfilter/ipset/ip_set_hash_ipportnet.c631
3 files changed, 642 insertions, 0 deletions
diff --git a/net/netfilter/ipset/Kconfig b/net/netfilter/ipset/Kconfig
index e6935533425..e2fbaa9d902 100644
--- a/net/netfilter/ipset/Kconfig
+++ b/net/netfilter/ipset/Kconfig
@@ -79,4 +79,14 @@ config IP_SET_HASH_IPPORTIP
79 79
80 To compile it as a module, choose M here. If unsure, say N. 80 To compile it as a module, choose M here. If unsure, say N.
81 81
82config 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
82endif # IP_SET 92endif # IP_SET
diff --git a/net/netfilter/ipset/Makefile b/net/netfilter/ipset/Makefile
index e9ddb25189d..9c5d857511c 100644
--- a/net/netfilter/ipset/Makefile
+++ b/net/netfilter/ipset/Makefile
@@ -16,3 +16,4 @@ obj-$(CONFIG_IP_SET_BITMAP_PORT) += ip_set_bitmap_port.o
16obj-$(CONFIG_IP_SET_HASH_IP) += ip_set_hash_ip.o 16obj-$(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
diff --git a/net/netfilter/ipset/ip_set_hash_ipportnet.c b/net/netfilter/ipset/ip_set_hash_ipportnet.c
new file mode 100644
index 00000000000..8eacd8a46c1
--- /dev/null
+++ b/net/netfilter/ipset/ip_set_hash_ipportnet.c
@@ -0,0 +1,631 @@
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
31MODULE_LICENSE("GPL");
32MODULE_AUTHOR("Jozsef Kadlecsik <kadlec@blackhole.kfki.hu>");
33MODULE_DESCRIPTION("hash:ip,port,net type of IP sets");
34MODULE_ALIAS("ip_set_hash:ip,port,net");
35
36/* Type specific function prefix */
37#define TYPE hash_ipportnet
38
39static bool
40hash_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 */
48struct 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 */
57struct hash_ipportnet4_telem {
58 __be32 ip;
59 __be32 ip2;
60 __be16 port;
61 u8 cidr;
62 u8 proto;
63 unsigned long timeout;
64};
65
66static inline bool
67hash_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
77static inline bool
78hash_ipportnet4_data_isnull(const struct hash_ipportnet4_elem *elem)
79{
80 return elem->proto == 0;
81}
82
83static inline void
84hash_ipportnet4_data_copy(struct hash_ipportnet4_elem *dst,
85 const struct hash_ipportnet4_elem *src)
86{
87 memcpy(dst, src, sizeof(*dst));
88}
89
90static inline void
91hash_ipportnet4_data_netmask(struct hash_ipportnet4_elem *elem, u8 cidr)
92{
93 elem->ip2 &= ip_set_netmask(cidr);
94 elem->cidr = cidr;
95}
96
97static inline void
98hash_ipportnet4_data_zero_out(struct hash_ipportnet4_elem *elem)
99{
100 elem->proto = 0;
101}
102
103static bool
104hash_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
114nla_put_failure:
115 return 1;
116}
117
118static bool
119hash_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
135nla_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
146static int
147hash_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
171static int
172hash_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
283static bool
284hash_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
296struct 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
304struct 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
313static inline bool
314hash_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
324static inline bool
325hash_ipportnet6_data_isnull(const struct hash_ipportnet6_elem *elem)
326{
327 return elem->proto == 0;
328}
329
330static inline void
331hash_ipportnet6_data_copy(struct hash_ipportnet6_elem *dst,
332 const struct hash_ipportnet6_elem *src)
333{
334 memcpy(dst, src, sizeof(*dst));
335}
336
337static inline void
338hash_ipportnet6_data_zero_out(struct hash_ipportnet6_elem *elem)
339{
340 elem->proto = 0;
341}
342
343static inline void
344ip6_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
352static inline void
353hash_ipportnet6_data_netmask(struct hash_ipportnet6_elem *elem, u8 cidr)
354{
355 ip6_netmask(&elem->ip2, cidr);
356 elem->cidr = cidr;
357}
358
359static bool
360hash_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
370nla_put_failure:
371 return 1;
372}
373
374static bool
375hash_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
390nla_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
401static int
402hash_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
426static int
427hash_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
519static int
520hash_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
588static 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
618static int __init
619hash_ipportnet_init(void)
620{
621 return ip_set_type_register(&hash_ipportnet_type);
622}
623
624static void __exit
625hash_ipportnet_fini(void)
626{
627 ip_set_type_unregister(&hash_ipportnet_type);
628}
629
630module_init(hash_ipportnet_init);
631module_exit(hash_ipportnet_fini);