diff options
author | Jozsef Kadlecsik <kadlec@blackhole.kfki.hu> | 2011-02-01 09:39:52 -0500 |
---|---|---|
committer | Patrick McHardy <kaber@trash.net> | 2011-02-01 09:39:52 -0500 |
commit | 07896ed37b94599a1b8ea97f4bd5766be71390f4 (patch) | |
tree | aa9c456290230380b2618735787b13ddd46b5697 | |
parent | 6c027889696a7a694b0e2f6e3cabadefec7553b6 (diff) |
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>
-rw-r--r-- | net/netfilter/ipset/Kconfig | 9 | ||||
-rw-r--r-- | net/netfilter/ipset/Makefile | 1 | ||||
-rw-r--r-- | net/netfilter/ipset/ip_set_hash_ipport.c | 547 |
3 files changed, 557 insertions, 0 deletions
diff --git a/net/netfilter/ipset/Kconfig b/net/netfilter/ipset/Kconfig index 194d89caeb3d..325b5312a47d 100644 --- a/net/netfilter/ipset/Kconfig +++ b/net/netfilter/ipset/Kconfig | |||
@@ -60,4 +60,13 @@ config IP_SET_HASH_IP | |||
60 | 60 | ||
61 | To compile it as a module, choose M here. If unsure, say N. | 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 | endif # IP_SET | 72 | endif # IP_SET |
diff --git a/net/netfilter/ipset/Makefile b/net/netfilter/ipset/Makefile index 5cbf00c41a98..6a3663e5cef1 100644 --- a/net/netfilter/ipset/Makefile +++ b/net/netfilter/ipset/Makefile | |||
@@ -14,3 +14,4 @@ obj-$(CONFIG_IP_SET_BITMAP_PORT) += ip_set_bitmap_port.o | |||
14 | 14 | ||
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 | ||
diff --git a/net/netfilter/ipset/ip_set_hash_ipport.c b/net/netfilter/ipset/ip_set_hash_ipport.c new file mode 100644 index 000000000000..d9b192818e58 --- /dev/null +++ b/net/netfilter/ipset/ip_set_hash_ipport.c | |||
@@ -0,0 +1,547 @@ | |||
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); | ||