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