Commit 543261907dc3c4e90845acfcd602ebdbfdfcb4f0
Committed by
Patrick McHardy
1 parent
de76021a1b
netfilter: ipset; bitmap:port set type support
The module implements the bitmap:port type in two flavours, without and with timeout support to store TCP/UDP ports from a range. Signed-off-by: Jozsef Kadlecsik <kadlec@blackhole.kfki.hu> Signed-off-by: Patrick McHardy <kaber@trash.net>
Showing 3 changed files with 530 additions and 0 deletions Side-by-side Diff
net/netfilter/ipset/Kconfig
... | ... | @@ -41,5 +41,14 @@ |
41 | 41 | |
42 | 42 | To compile it as a module, choose M here. If unsure, say N. |
43 | 43 | |
44 | +config IP_SET_BITMAP_PORT | |
45 | + tristate "bitmap:port set support" | |
46 | + depends on IP_SET | |
47 | + help | |
48 | + This option adds the bitmap:port set type support, by which one | |
49 | + can store TCP/UDP port numbers from a range. | |
50 | + | |
51 | + To compile it as a module, choose M here. If unsure, say N. | |
52 | + | |
44 | 53 | endif # IP_SET |
net/netfilter/ipset/Makefile
net/netfilter/ipset/ip_set_bitmap_port.c
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 bitmap:port type */ | |
9 | + | |
10 | +#include <linux/module.h> | |
11 | +#include <linux/ip.h> | |
12 | +#include <linux/tcp.h> | |
13 | +#include <linux/udp.h> | |
14 | +#include <linux/skbuff.h> | |
15 | +#include <linux/errno.h> | |
16 | +#include <linux/uaccess.h> | |
17 | +#include <linux/bitops.h> | |
18 | +#include <linux/spinlock.h> | |
19 | +#include <linux/netlink.h> | |
20 | +#include <linux/jiffies.h> | |
21 | +#include <linux/timer.h> | |
22 | +#include <net/netlink.h> | |
23 | + | |
24 | +#include <linux/netfilter/ipset/ip_set.h> | |
25 | +#include <linux/netfilter/ipset/ip_set_bitmap.h> | |
26 | +#include <linux/netfilter/ipset/ip_set_getport.h> | |
27 | +#define IP_SET_BITMAP_TIMEOUT | |
28 | +#include <linux/netfilter/ipset/ip_set_timeout.h> | |
29 | + | |
30 | +MODULE_LICENSE("GPL"); | |
31 | +MODULE_AUTHOR("Jozsef Kadlecsik <kadlec@blackhole.kfki.hu>"); | |
32 | +MODULE_DESCRIPTION("bitmap:port type of IP sets"); | |
33 | +MODULE_ALIAS("ip_set_bitmap:port"); | |
34 | + | |
35 | +/* Type structure */ | |
36 | +struct bitmap_port { | |
37 | + void *members; /* the set members */ | |
38 | + u16 first_port; /* host byte order, included in range */ | |
39 | + u16 last_port; /* host byte order, included in range */ | |
40 | + size_t memsize; /* members size */ | |
41 | + u32 timeout; /* timeout parameter */ | |
42 | + struct timer_list gc; /* garbage collection */ | |
43 | +}; | |
44 | + | |
45 | +/* Base variant */ | |
46 | + | |
47 | +static int | |
48 | +bitmap_port_test(struct ip_set *set, void *value, u32 timeout) | |
49 | +{ | |
50 | + const struct bitmap_port *map = set->data; | |
51 | + u16 id = *(u16 *)value; | |
52 | + | |
53 | + return !!test_bit(id, map->members); | |
54 | +} | |
55 | + | |
56 | +static int | |
57 | +bitmap_port_add(struct ip_set *set, void *value, u32 timeout) | |
58 | +{ | |
59 | + struct bitmap_port *map = set->data; | |
60 | + u16 id = *(u16 *)value; | |
61 | + | |
62 | + if (test_and_set_bit(id, map->members)) | |
63 | + return -IPSET_ERR_EXIST; | |
64 | + | |
65 | + return 0; | |
66 | +} | |
67 | + | |
68 | +static int | |
69 | +bitmap_port_del(struct ip_set *set, void *value, u32 timeout) | |
70 | +{ | |
71 | + struct bitmap_port *map = set->data; | |
72 | + u16 id = *(u16 *)value; | |
73 | + | |
74 | + if (!test_and_clear_bit(id, map->members)) | |
75 | + return -IPSET_ERR_EXIST; | |
76 | + | |
77 | + return 0; | |
78 | +} | |
79 | + | |
80 | +static int | |
81 | +bitmap_port_list(const struct ip_set *set, | |
82 | + struct sk_buff *skb, struct netlink_callback *cb) | |
83 | +{ | |
84 | + const struct bitmap_port *map = set->data; | |
85 | + struct nlattr *atd, *nested; | |
86 | + u16 id, first = cb->args[2]; | |
87 | + u16 last = map->last_port - map->first_port; | |
88 | + | |
89 | + atd = ipset_nest_start(skb, IPSET_ATTR_ADT); | |
90 | + if (!atd) | |
91 | + return -EMSGSIZE; | |
92 | + for (; cb->args[2] <= last; cb->args[2]++) { | |
93 | + id = cb->args[2]; | |
94 | + if (!test_bit(id, map->members)) | |
95 | + continue; | |
96 | + nested = ipset_nest_start(skb, IPSET_ATTR_DATA); | |
97 | + if (!nested) { | |
98 | + if (id == first) { | |
99 | + nla_nest_cancel(skb, atd); | |
100 | + return -EMSGSIZE; | |
101 | + } else | |
102 | + goto nla_put_failure; | |
103 | + } | |
104 | + NLA_PUT_NET16(skb, IPSET_ATTR_PORT, | |
105 | + htons(map->first_port + id)); | |
106 | + ipset_nest_end(skb, nested); | |
107 | + } | |
108 | + ipset_nest_end(skb, atd); | |
109 | + /* Set listing finished */ | |
110 | + cb->args[2] = 0; | |
111 | + | |
112 | + return 0; | |
113 | + | |
114 | +nla_put_failure: | |
115 | + nla_nest_cancel(skb, nested); | |
116 | + ipset_nest_end(skb, atd); | |
117 | + if (unlikely(id == first)) { | |
118 | + cb->args[2] = 0; | |
119 | + return -EMSGSIZE; | |
120 | + } | |
121 | + return 0; | |
122 | +} | |
123 | + | |
124 | +/* Timeout variant */ | |
125 | + | |
126 | +static int | |
127 | +bitmap_port_ttest(struct ip_set *set, void *value, u32 timeout) | |
128 | +{ | |
129 | + const struct bitmap_port *map = set->data; | |
130 | + const unsigned long *members = map->members; | |
131 | + u16 id = *(u16 *)value; | |
132 | + | |
133 | + return ip_set_timeout_test(members[id]); | |
134 | +} | |
135 | + | |
136 | +static int | |
137 | +bitmap_port_tadd(struct ip_set *set, void *value, u32 timeout) | |
138 | +{ | |
139 | + struct bitmap_port *map = set->data; | |
140 | + unsigned long *members = map->members; | |
141 | + u16 id = *(u16 *)value; | |
142 | + | |
143 | + if (ip_set_timeout_test(members[id])) | |
144 | + return -IPSET_ERR_EXIST; | |
145 | + | |
146 | + members[id] = ip_set_timeout_set(timeout); | |
147 | + | |
148 | + return 0; | |
149 | +} | |
150 | + | |
151 | +static int | |
152 | +bitmap_port_tdel(struct ip_set *set, void *value, u32 timeout) | |
153 | +{ | |
154 | + struct bitmap_port *map = set->data; | |
155 | + unsigned long *members = map->members; | |
156 | + u16 id = *(u16 *)value; | |
157 | + int ret = -IPSET_ERR_EXIST; | |
158 | + | |
159 | + if (ip_set_timeout_test(members[id])) | |
160 | + ret = 0; | |
161 | + | |
162 | + members[id] = IPSET_ELEM_UNSET; | |
163 | + return ret; | |
164 | +} | |
165 | + | |
166 | +static int | |
167 | +bitmap_port_tlist(const struct ip_set *set, | |
168 | + struct sk_buff *skb, struct netlink_callback *cb) | |
169 | +{ | |
170 | + const struct bitmap_port *map = set->data; | |
171 | + struct nlattr *adt, *nested; | |
172 | + u16 id, first = cb->args[2]; | |
173 | + u16 last = map->last_port - map->first_port; | |
174 | + const unsigned long *members = map->members; | |
175 | + | |
176 | + adt = ipset_nest_start(skb, IPSET_ATTR_ADT); | |
177 | + if (!adt) | |
178 | + return -EMSGSIZE; | |
179 | + for (; cb->args[2] <= last; cb->args[2]++) { | |
180 | + id = cb->args[2]; | |
181 | + if (!ip_set_timeout_test(members[id])) | |
182 | + continue; | |
183 | + nested = ipset_nest_start(skb, IPSET_ATTR_DATA); | |
184 | + if (!nested) { | |
185 | + if (id == first) { | |
186 | + nla_nest_cancel(skb, adt); | |
187 | + return -EMSGSIZE; | |
188 | + } else | |
189 | + goto nla_put_failure; | |
190 | + } | |
191 | + NLA_PUT_NET16(skb, IPSET_ATTR_PORT, | |
192 | + htons(map->first_port + id)); | |
193 | + NLA_PUT_NET32(skb, IPSET_ATTR_TIMEOUT, | |
194 | + htonl(ip_set_timeout_get(members[id]))); | |
195 | + ipset_nest_end(skb, nested); | |
196 | + } | |
197 | + ipset_nest_end(skb, adt); | |
198 | + | |
199 | + /* Set listing finished */ | |
200 | + cb->args[2] = 0; | |
201 | + | |
202 | + return 0; | |
203 | + | |
204 | +nla_put_failure: | |
205 | + nla_nest_cancel(skb, nested); | |
206 | + ipset_nest_end(skb, adt); | |
207 | + if (unlikely(id == first)) { | |
208 | + cb->args[2] = 0; | |
209 | + return -EMSGSIZE; | |
210 | + } | |
211 | + return 0; | |
212 | +} | |
213 | + | |
214 | +static int | |
215 | +bitmap_port_kadt(struct ip_set *set, const struct sk_buff *skb, | |
216 | + enum ipset_adt adt, u8 pf, u8 dim, u8 flags) | |
217 | +{ | |
218 | + struct bitmap_port *map = set->data; | |
219 | + ipset_adtfn adtfn = set->variant->adt[adt]; | |
220 | + __be16 __port; | |
221 | + u16 port = 0; | |
222 | + | |
223 | + if (!ip_set_get_ip_port(skb, pf, flags & IPSET_DIM_ONE_SRC, &__port)) | |
224 | + return -EINVAL; | |
225 | + | |
226 | + port = ntohs(__port); | |
227 | + | |
228 | + if (port < map->first_port || port > map->last_port) | |
229 | + return -IPSET_ERR_BITMAP_RANGE; | |
230 | + | |
231 | + port -= map->first_port; | |
232 | + | |
233 | + return adtfn(set, &port, map->timeout); | |
234 | +} | |
235 | + | |
236 | +static int | |
237 | +bitmap_port_uadt(struct ip_set *set, struct nlattr *tb[], | |
238 | + enum ipset_adt adt, u32 *lineno, u32 flags) | |
239 | +{ | |
240 | + struct bitmap_port *map = set->data; | |
241 | + ipset_adtfn adtfn = set->variant->adt[adt]; | |
242 | + u32 timeout = map->timeout; | |
243 | + u32 port; /* wraparound */ | |
244 | + u16 id, port_to; | |
245 | + int ret = 0; | |
246 | + | |
247 | + if (unlikely(!ip_set_attr_netorder(tb, IPSET_ATTR_PORT) || | |
248 | + !ip_set_optattr_netorder(tb, IPSET_ATTR_PORT_TO) || | |
249 | + !ip_set_optattr_netorder(tb, IPSET_ATTR_TIMEOUT))) | |
250 | + return -IPSET_ERR_PROTOCOL; | |
251 | + | |
252 | + if (tb[IPSET_ATTR_LINENO]) | |
253 | + *lineno = nla_get_u32(tb[IPSET_ATTR_LINENO]); | |
254 | + | |
255 | + port = ip_set_get_h16(tb[IPSET_ATTR_PORT]); | |
256 | + if (port < map->first_port || port > map->last_port) | |
257 | + return -IPSET_ERR_BITMAP_RANGE; | |
258 | + | |
259 | + if (tb[IPSET_ATTR_TIMEOUT]) { | |
260 | + if (!with_timeout(map->timeout)) | |
261 | + return -IPSET_ERR_TIMEOUT; | |
262 | + timeout = ip_set_timeout_uget(tb[IPSET_ATTR_TIMEOUT]); | |
263 | + } | |
264 | + | |
265 | + if (adt == IPSET_TEST) { | |
266 | + id = port - map->first_port; | |
267 | + return adtfn(set, &id, timeout); | |
268 | + } | |
269 | + | |
270 | + if (tb[IPSET_ATTR_PORT_TO]) { | |
271 | + port_to = ip_set_get_h16(tb[IPSET_ATTR_PORT_TO]); | |
272 | + if (port > port_to) { | |
273 | + swap(port, port_to); | |
274 | + if (port < map->first_port) | |
275 | + return -IPSET_ERR_BITMAP_RANGE; | |
276 | + } | |
277 | + } else | |
278 | + port_to = port; | |
279 | + | |
280 | + if (port_to > map->last_port) | |
281 | + return -IPSET_ERR_BITMAP_RANGE; | |
282 | + | |
283 | + for (; port <= port_to; port++) { | |
284 | + id = port - map->first_port; | |
285 | + ret = adtfn(set, &id, timeout); | |
286 | + | |
287 | + if (ret && !ip_set_eexist(ret, flags)) | |
288 | + return ret; | |
289 | + else | |
290 | + ret = 0; | |
291 | + } | |
292 | + return ret; | |
293 | +} | |
294 | + | |
295 | +static void | |
296 | +bitmap_port_destroy(struct ip_set *set) | |
297 | +{ | |
298 | + struct bitmap_port *map = set->data; | |
299 | + | |
300 | + if (with_timeout(map->timeout)) | |
301 | + del_timer_sync(&map->gc); | |
302 | + | |
303 | + ip_set_free(map->members); | |
304 | + kfree(map); | |
305 | + | |
306 | + set->data = NULL; | |
307 | +} | |
308 | + | |
309 | +static void | |
310 | +bitmap_port_flush(struct ip_set *set) | |
311 | +{ | |
312 | + struct bitmap_port *map = set->data; | |
313 | + | |
314 | + memset(map->members, 0, map->memsize); | |
315 | +} | |
316 | + | |
317 | +static int | |
318 | +bitmap_port_head(struct ip_set *set, struct sk_buff *skb) | |
319 | +{ | |
320 | + const struct bitmap_port *map = set->data; | |
321 | + struct nlattr *nested; | |
322 | + | |
323 | + nested = ipset_nest_start(skb, IPSET_ATTR_DATA); | |
324 | + if (!nested) | |
325 | + goto nla_put_failure; | |
326 | + NLA_PUT_NET16(skb, IPSET_ATTR_PORT, htons(map->first_port)); | |
327 | + NLA_PUT_NET16(skb, IPSET_ATTR_PORT_TO, htons(map->last_port)); | |
328 | + NLA_PUT_NET32(skb, IPSET_ATTR_REFERENCES, | |
329 | + htonl(atomic_read(&set->ref) - 1)); | |
330 | + NLA_PUT_NET32(skb, IPSET_ATTR_MEMSIZE, | |
331 | + htonl(sizeof(*map) + map->memsize)); | |
332 | + if (with_timeout(map->timeout)) | |
333 | + NLA_PUT_NET32(skb, IPSET_ATTR_TIMEOUT, htonl(map->timeout)); | |
334 | + ipset_nest_end(skb, nested); | |
335 | + | |
336 | + return 0; | |
337 | +nla_put_failure: | |
338 | + return -EMSGSIZE; | |
339 | +} | |
340 | + | |
341 | +static bool | |
342 | +bitmap_port_same_set(const struct ip_set *a, const struct ip_set *b) | |
343 | +{ | |
344 | + const struct bitmap_port *x = a->data; | |
345 | + const struct bitmap_port *y = b->data; | |
346 | + | |
347 | + return x->first_port == y->first_port && | |
348 | + x->last_port == y->last_port && | |
349 | + x->timeout == y->timeout; | |
350 | +} | |
351 | + | |
352 | +static const struct ip_set_type_variant bitmap_port = { | |
353 | + .kadt = bitmap_port_kadt, | |
354 | + .uadt = bitmap_port_uadt, | |
355 | + .adt = { | |
356 | + [IPSET_ADD] = bitmap_port_add, | |
357 | + [IPSET_DEL] = bitmap_port_del, | |
358 | + [IPSET_TEST] = bitmap_port_test, | |
359 | + }, | |
360 | + .destroy = bitmap_port_destroy, | |
361 | + .flush = bitmap_port_flush, | |
362 | + .head = bitmap_port_head, | |
363 | + .list = bitmap_port_list, | |
364 | + .same_set = bitmap_port_same_set, | |
365 | +}; | |
366 | + | |
367 | +static const struct ip_set_type_variant bitmap_tport = { | |
368 | + .kadt = bitmap_port_kadt, | |
369 | + .uadt = bitmap_port_uadt, | |
370 | + .adt = { | |
371 | + [IPSET_ADD] = bitmap_port_tadd, | |
372 | + [IPSET_DEL] = bitmap_port_tdel, | |
373 | + [IPSET_TEST] = bitmap_port_ttest, | |
374 | + }, | |
375 | + .destroy = bitmap_port_destroy, | |
376 | + .flush = bitmap_port_flush, | |
377 | + .head = bitmap_port_head, | |
378 | + .list = bitmap_port_tlist, | |
379 | + .same_set = bitmap_port_same_set, | |
380 | +}; | |
381 | + | |
382 | +static void | |
383 | +bitmap_port_gc(unsigned long ul_set) | |
384 | +{ | |
385 | + struct ip_set *set = (struct ip_set *) ul_set; | |
386 | + struct bitmap_port *map = set->data; | |
387 | + unsigned long *table = map->members; | |
388 | + u32 id; /* wraparound */ | |
389 | + u16 last = map->last_port - map->first_port; | |
390 | + | |
391 | + /* We run parallel with other readers (test element) | |
392 | + * but adding/deleting new entries is locked out */ | |
393 | + read_lock_bh(&set->lock); | |
394 | + for (id = 0; id <= last; id++) | |
395 | + if (ip_set_timeout_expired(table[id])) | |
396 | + table[id] = IPSET_ELEM_UNSET; | |
397 | + read_unlock_bh(&set->lock); | |
398 | + | |
399 | + map->gc.expires = jiffies + IPSET_GC_PERIOD(map->timeout) * HZ; | |
400 | + add_timer(&map->gc); | |
401 | +} | |
402 | + | |
403 | +static void | |
404 | +bitmap_port_gc_init(struct ip_set *set) | |
405 | +{ | |
406 | + struct bitmap_port *map = set->data; | |
407 | + | |
408 | + init_timer(&map->gc); | |
409 | + map->gc.data = (unsigned long) set; | |
410 | + map->gc.function = bitmap_port_gc; | |
411 | + map->gc.expires = jiffies + IPSET_GC_PERIOD(map->timeout) * HZ; | |
412 | + add_timer(&map->gc); | |
413 | +} | |
414 | + | |
415 | +/* Create bitmap:ip type of sets */ | |
416 | + | |
417 | +static bool | |
418 | +init_map_port(struct ip_set *set, struct bitmap_port *map, | |
419 | + u16 first_port, u16 last_port) | |
420 | +{ | |
421 | + map->members = ip_set_alloc(map->memsize); | |
422 | + if (!map->members) | |
423 | + return false; | |
424 | + map->first_port = first_port; | |
425 | + map->last_port = last_port; | |
426 | + map->timeout = IPSET_NO_TIMEOUT; | |
427 | + | |
428 | + set->data = map; | |
429 | + set->family = AF_UNSPEC; | |
430 | + | |
431 | + return true; | |
432 | +} | |
433 | + | |
434 | +static int | |
435 | +bitmap_port_create(struct ip_set *set, struct nlattr *tb[], | |
436 | + u32 flags) | |
437 | +{ | |
438 | + struct bitmap_port *map; | |
439 | + u16 first_port, last_port; | |
440 | + | |
441 | + if (unlikely(!ip_set_attr_netorder(tb, IPSET_ATTR_PORT) || | |
442 | + !ip_set_attr_netorder(tb, IPSET_ATTR_PORT_TO) || | |
443 | + !ip_set_optattr_netorder(tb, IPSET_ATTR_TIMEOUT))) | |
444 | + return -IPSET_ERR_PROTOCOL; | |
445 | + | |
446 | + first_port = ip_set_get_h16(tb[IPSET_ATTR_PORT]); | |
447 | + last_port = ip_set_get_h16(tb[IPSET_ATTR_PORT_TO]); | |
448 | + if (first_port > last_port) { | |
449 | + u16 tmp = first_port; | |
450 | + | |
451 | + first_port = last_port; | |
452 | + last_port = tmp; | |
453 | + } | |
454 | + | |
455 | + map = kzalloc(sizeof(*map), GFP_KERNEL); | |
456 | + if (!map) | |
457 | + return -ENOMEM; | |
458 | + | |
459 | + if (tb[IPSET_ATTR_TIMEOUT]) { | |
460 | + map->memsize = (last_port - first_port + 1) | |
461 | + * sizeof(unsigned long); | |
462 | + | |
463 | + if (!init_map_port(set, map, first_port, last_port)) { | |
464 | + kfree(map); | |
465 | + return -ENOMEM; | |
466 | + } | |
467 | + | |
468 | + map->timeout = ip_set_timeout_uget(tb[IPSET_ATTR_TIMEOUT]); | |
469 | + set->variant = &bitmap_tport; | |
470 | + | |
471 | + bitmap_port_gc_init(set); | |
472 | + } else { | |
473 | + map->memsize = bitmap_bytes(0, last_port - first_port); | |
474 | + pr_debug("memsize: %zu\n", map->memsize); | |
475 | + if (!init_map_port(set, map, first_port, last_port)) { | |
476 | + kfree(map); | |
477 | + return -ENOMEM; | |
478 | + } | |
479 | + | |
480 | + set->variant = &bitmap_port; | |
481 | + } | |
482 | + return 0; | |
483 | +} | |
484 | + | |
485 | +static struct ip_set_type bitmap_port_type = { | |
486 | + .name = "bitmap:port", | |
487 | + .protocol = IPSET_PROTOCOL, | |
488 | + .features = IPSET_TYPE_PORT, | |
489 | + .dimension = IPSET_DIM_ONE, | |
490 | + .family = AF_UNSPEC, | |
491 | + .revision = 0, | |
492 | + .create = bitmap_port_create, | |
493 | + .create_policy = { | |
494 | + [IPSET_ATTR_PORT] = { .type = NLA_U16 }, | |
495 | + [IPSET_ATTR_PORT_TO] = { .type = NLA_U16 }, | |
496 | + [IPSET_ATTR_TIMEOUT] = { .type = NLA_U32 }, | |
497 | + }, | |
498 | + .adt_policy = { | |
499 | + [IPSET_ATTR_PORT] = { .type = NLA_U16 }, | |
500 | + [IPSET_ATTR_PORT_TO] = { .type = NLA_U16 }, | |
501 | + [IPSET_ATTR_TIMEOUT] = { .type = NLA_U32 }, | |
502 | + [IPSET_ATTR_LINENO] = { .type = NLA_U32 }, | |
503 | + }, | |
504 | + .me = THIS_MODULE, | |
505 | +}; | |
506 | + | |
507 | +static int __init | |
508 | +bitmap_port_init(void) | |
509 | +{ | |
510 | + return ip_set_type_register(&bitmap_port_type); | |
511 | +} | |
512 | + | |
513 | +static void __exit | |
514 | +bitmap_port_fini(void) | |
515 | +{ | |
516 | + ip_set_type_unregister(&bitmap_port_type); | |
517 | +} | |
518 | + | |
519 | +module_init(bitmap_port_init); | |
520 | +module_exit(bitmap_port_fini); |