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