Commit | Line | Data |
---|---|---|
72205fc6 JK |
1 | /* Copyright (C) 2000-2002 Joakim Axelsson <gozem@linux.nu> |
2 | * Patrick Schaaf <bof@bof.de> | |
b0da3905 | 3 | * Copyright (C) 2003-2013 Jozsef Kadlecsik <kadlec@blackhole.kfki.hu> |
72205fc6 JK |
4 | * |
5 | * This program is free software; you can redistribute it and/or modify | |
6 | * it under the terms of the GNU General Public License version 2 as | |
7 | * published by the Free Software Foundation. | |
8 | */ | |
9 | ||
10 | /* Kernel module implementing an IP set type: the bitmap:ip type */ | |
11 | ||
12 | #include <linux/module.h> | |
13 | #include <linux/ip.h> | |
14 | #include <linux/skbuff.h> | |
15 | #include <linux/errno.h> | |
72205fc6 JK |
16 | #include <linux/bitops.h> |
17 | #include <linux/spinlock.h> | |
18 | #include <linux/netlink.h> | |
19 | #include <linux/jiffies.h> | |
20 | #include <linux/timer.h> | |
21 | #include <net/netlink.h> | |
22 | #include <net/tcp.h> | |
23 | ||
24 | #include <linux/netfilter/ipset/pfxlen.h> | |
25 | #include <linux/netfilter/ipset/ip_set.h> | |
26 | #include <linux/netfilter/ipset/ip_set_bitmap.h> | |
72205fc6 | 27 | |
35b8dcf8 | 28 | #define IPSET_TYPE_REV_MIN 0 |
b90cb8ba | 29 | /* 1 Counter support added */ |
39d1ecf1 AD |
30 | /* 2 Comment support added */ |
31 | #define IPSET_TYPE_REV_MAX 3 /* skbinfo support added */ | |
10111a6e | 32 | |
72205fc6 JK |
33 | MODULE_LICENSE("GPL"); |
34 | MODULE_AUTHOR("Jozsef Kadlecsik <kadlec@blackhole.kfki.hu>"); | |
35b8dcf8 | 35 | IP_SET_MODULE_DESC("bitmap:ip", IPSET_TYPE_REV_MIN, IPSET_TYPE_REV_MAX); |
72205fc6 JK |
36 | MODULE_ALIAS("ip_set_bitmap:ip"); |
37 | ||
b0da3905 | 38 | #define MTYPE bitmap_ip |
cabfd139 | 39 | #define HOST_MASK 32 |
b0da3905 | 40 | |
72205fc6 JK |
41 | /* Type structure */ |
42 | struct bitmap_ip { | |
43 | void *members; /* the set members */ | |
44 | u32 first_ip; /* host byte order, included in range */ | |
45 | u32 last_ip; /* host byte order, included in range */ | |
46 | u32 elements; /* number of max elements in the set */ | |
47 | u32 hosts; /* number of hosts in a subnet */ | |
48 | size_t memsize; /* members size */ | |
49 | u8 netmask; /* subnet netmask */ | |
72205fc6 | 50 | struct timer_list gc; /* garbage collection */ |
95ad1f4a JK |
51 | unsigned char extensions[0] /* data extensions */ |
52 | __aligned(__alignof__(u64)); | |
72205fc6 JK |
53 | }; |
54 | ||
b0da3905 JK |
55 | /* ADT structure for generic function args */ |
56 | struct bitmap_ip_adt_elem { | |
57 | u16 id; | |
58 | }; | |
72205fc6 JK |
59 | |
60 | static inline u32 | |
61 | ip_to_id(const struct bitmap_ip *m, u32 ip) | |
62 | { | |
ca0f6a5c | 63 | return ((ip & ip_set_hostmask(m->netmask)) - m->first_ip) / m->hosts; |
72205fc6 JK |
64 | } |
65 | ||
b0da3905 | 66 | /* Common functions */ |
72205fc6 | 67 | |
b0da3905 | 68 | static inline int |
ca134ce8 JK |
69 | bitmap_ip_do_test(const struct bitmap_ip_adt_elem *e, |
70 | struct bitmap_ip *map, size_t dsize) | |
72205fc6 | 71 | { |
b0da3905 | 72 | return !!test_bit(e->id, map->members); |
72205fc6 JK |
73 | } |
74 | ||
b0da3905 | 75 | static inline int |
ca134ce8 | 76 | bitmap_ip_gc_test(u16 id, const struct bitmap_ip *map, size_t dsize) |
72205fc6 | 77 | { |
b0da3905 | 78 | return !!test_bit(id, map->members); |
72205fc6 JK |
79 | } |
80 | ||
b0da3905 JK |
81 | static inline int |
82 | bitmap_ip_do_add(const struct bitmap_ip_adt_elem *e, struct bitmap_ip *map, | |
ca134ce8 | 83 | u32 flags, size_t dsize) |
72205fc6 | 84 | { |
96f51428 | 85 | return !!test_bit(e->id, map->members); |
72205fc6 JK |
86 | } |
87 | ||
b0da3905 JK |
88 | static inline int |
89 | bitmap_ip_do_del(const struct bitmap_ip_adt_elem *e, struct bitmap_ip *map) | |
72205fc6 | 90 | { |
b0da3905 | 91 | return !test_and_clear_bit(e->id, map->members); |
72205fc6 JK |
92 | } |
93 | ||
b0da3905 | 94 | static inline int |
ca134ce8 JK |
95 | bitmap_ip_do_list(struct sk_buff *skb, const struct bitmap_ip *map, u32 id, |
96 | size_t dsize) | |
72205fc6 | 97 | { |
b0da3905 JK |
98 | return nla_put_ipaddr4(skb, IPSET_ATTR_IP, |
99 | htonl(map->first_ip + id * map->hosts)); | |
72205fc6 JK |
100 | } |
101 | ||
b0da3905 JK |
102 | static inline int |
103 | bitmap_ip_do_head(struct sk_buff *skb, const struct bitmap_ip *map) | |
72205fc6 | 104 | { |
b0da3905 JK |
105 | return nla_put_ipaddr4(skb, IPSET_ATTR_IP, htonl(map->first_ip)) || |
106 | nla_put_ipaddr4(skb, IPSET_ATTR_IP_TO, htonl(map->last_ip)) || | |
107 | (map->netmask != 32 && | |
108 | nla_put_u8(skb, IPSET_ATTR_NETMASK, map->netmask)); | |
72205fc6 JK |
109 | } |
110 | ||
111 | static int | |
112 | bitmap_ip_kadt(struct ip_set *set, const struct sk_buff *skb, | |
b66554cf | 113 | const struct xt_action_param *par, |
b0da3905 | 114 | enum ipset_adt adt, struct ip_set_adt_opt *opt) |
72205fc6 JK |
115 | { |
116 | struct bitmap_ip *map = set->data; | |
117 | ipset_adtfn adtfn = set->variant->adt[adt]; | |
94729f8a | 118 | struct bitmap_ip_adt_elem e = { .id = 0 }; |
ca134ce8 | 119 | struct ip_set_ext ext = IP_SET_INIT_KEXT(skb, opt, set); |
72205fc6 JK |
120 | u32 ip; |
121 | ||
ac8cc925 | 122 | ip = ntohl(ip4addr(skb, opt->flags & IPSET_DIM_ONE_SRC)); |
72205fc6 JK |
123 | if (ip < map->first_ip || ip > map->last_ip) |
124 | return -IPSET_ERR_BITMAP_RANGE; | |
125 | ||
b0da3905 | 126 | e.id = ip_to_id(map, ip); |
72205fc6 | 127 | |
b0da3905 | 128 | return adtfn(set, &e, &ext, &opt->ext, opt->cmdflags); |
72205fc6 JK |
129 | } |
130 | ||
131 | static int | |
132 | bitmap_ip_uadt(struct ip_set *set, struct nlattr *tb[], | |
3d14b171 | 133 | enum ipset_adt adt, u32 *lineno, u32 flags, bool retried) |
72205fc6 JK |
134 | { |
135 | struct bitmap_ip *map = set->data; | |
136 | ipset_adtfn adtfn = set->variant->adt[adt]; | |
20b2fab4 | 137 | u32 ip = 0, ip_to = 0; |
94729f8a | 138 | struct bitmap_ip_adt_elem e = { .id = 0 }; |
ca134ce8 | 139 | struct ip_set_ext ext = IP_SET_INIT_UEXT(set); |
72205fc6 JK |
140 | int ret = 0; |
141 | ||
72205fc6 JK |
142 | if (tb[IPSET_ATTR_LINENO]) |
143 | *lineno = nla_get_u32(tb[IPSET_ATTR_LINENO]); | |
144 | ||
a212e08e SP |
145 | if (unlikely(!tb[IPSET_ATTR_IP])) |
146 | return -IPSET_ERR_PROTOCOL; | |
147 | ||
8e55d2e5 SP |
148 | ret = ip_set_get_hostipaddr4(tb[IPSET_ATTR_IP], &ip); |
149 | if (ret) | |
150 | return ret; | |
151 | ||
152 | ret = ip_set_get_extensions(set, tb, &ext); | |
72205fc6 JK |
153 | if (ret) |
154 | return ret; | |
155 | ||
156 | if (ip < map->first_ip || ip > map->last_ip) | |
157 | return -IPSET_ERR_BITMAP_RANGE; | |
158 | ||
72205fc6 | 159 | if (adt == IPSET_TEST) { |
b0da3905 JK |
160 | e.id = ip_to_id(map, ip); |
161 | return adtfn(set, &e, &ext, &ext, flags); | |
72205fc6 JK |
162 | } |
163 | ||
164 | if (tb[IPSET_ATTR_IP_TO]) { | |
165 | ret = ip_set_get_hostipaddr4(tb[IPSET_ATTR_IP_TO], &ip_to); | |
166 | if (ret) | |
167 | return ret; | |
168 | if (ip > ip_to) { | |
169 | swap(ip, ip_to); | |
170 | if (ip < map->first_ip) | |
171 | return -IPSET_ERR_BITMAP_RANGE; | |
172 | } | |
173 | } else if (tb[IPSET_ATTR_CIDR]) { | |
174 | u8 cidr = nla_get_u8(tb[IPSET_ATTR_CIDR]); | |
175 | ||
cabfd139 | 176 | if (!cidr || cidr > HOST_MASK) |
72205fc6 | 177 | return -IPSET_ERR_INVALID_CIDR; |
e6146e86 | 178 | ip_set_mask_from_to(ip, ip_to, cidr); |
ca0f6a5c | 179 | } else { |
72205fc6 | 180 | ip_to = ip; |
ca0f6a5c | 181 | } |
72205fc6 JK |
182 | |
183 | if (ip_to > map->last_ip) | |
184 | return -IPSET_ERR_BITMAP_RANGE; | |
185 | ||
186 | for (; !before(ip_to, ip); ip += map->hosts) { | |
b0da3905 JK |
187 | e.id = ip_to_id(map, ip); |
188 | ret = adtfn(set, &e, &ext, &ext, flags); | |
72205fc6 JK |
189 | |
190 | if (ret && !ip_set_eexist(ret, flags)) | |
191 | return ret; | |
ca0f6a5c JK |
192 | |
193 | ret = 0; | |
72205fc6 JK |
194 | } |
195 | return ret; | |
196 | } | |
197 | ||
72205fc6 JK |
198 | static bool |
199 | bitmap_ip_same_set(const struct ip_set *a, const struct ip_set *b) | |
200 | { | |
201 | const struct bitmap_ip *x = a->data; | |
202 | const struct bitmap_ip *y = b->data; | |
203 | ||
204 | return x->first_ip == y->first_ip && | |
205 | x->last_ip == y->last_ip && | |
206 | x->netmask == y->netmask && | |
ca134ce8 | 207 | a->timeout == b->timeout && |
b0da3905 | 208 | a->extensions == b->extensions; |
72205fc6 JK |
209 | } |
210 | ||
b0da3905 | 211 | /* Plain variant */ |
72205fc6 | 212 | |
b0da3905 | 213 | struct bitmap_ip_elem { |
72205fc6 JK |
214 | }; |
215 | ||
b0da3905 | 216 | #include "ip_set_bitmap_gen.h" |
72205fc6 JK |
217 | |
218 | /* Create bitmap:ip type of sets */ | |
219 | ||
220 | static bool | |
221 | init_map_ip(struct ip_set *set, struct bitmap_ip *map, | |
222 | u32 first_ip, u32 last_ip, | |
223 | u32 elements, u32 hosts, u8 netmask) | |
224 | { | |
225 | map->members = ip_set_alloc(map->memsize); | |
226 | if (!map->members) | |
227 | return false; | |
228 | map->first_ip = first_ip; | |
229 | map->last_ip = last_ip; | |
230 | map->elements = elements; | |
231 | map->hosts = hosts; | |
232 | map->netmask = netmask; | |
ca134ce8 | 233 | set->timeout = IPSET_NO_TIMEOUT; |
72205fc6 JK |
234 | |
235 | set->data = map; | |
c15f1c83 | 236 | set->family = NFPROTO_IPV4; |
72205fc6 JK |
237 | |
238 | return true; | |
239 | } | |
240 | ||
241 | static int | |
1785e8f4 VL |
242 | bitmap_ip_create(struct net *net, struct ip_set *set, struct nlattr *tb[], |
243 | u32 flags) | |
72205fc6 JK |
244 | { |
245 | struct bitmap_ip *map; | |
03c8b234 | 246 | u32 first_ip = 0, last_ip = 0, hosts; |
b9fed748 | 247 | u64 elements; |
72205fc6 JK |
248 | u8 netmask = 32; |
249 | int ret; | |
250 | ||
251 | if (unlikely(!tb[IPSET_ATTR_IP] || | |
f48d19db JK |
252 | !ip_set_optattr_netorder(tb, IPSET_ATTR_TIMEOUT) || |
253 | !ip_set_optattr_netorder(tb, IPSET_ATTR_CADT_FLAGS))) | |
72205fc6 JK |
254 | return -IPSET_ERR_PROTOCOL; |
255 | ||
256 | ret = ip_set_get_hostipaddr4(tb[IPSET_ATTR_IP], &first_ip); | |
257 | if (ret) | |
258 | return ret; | |
259 | ||
260 | if (tb[IPSET_ATTR_IP_TO]) { | |
261 | ret = ip_set_get_hostipaddr4(tb[IPSET_ATTR_IP_TO], &last_ip); | |
262 | if (ret) | |
263 | return ret; | |
264 | if (first_ip > last_ip) { | |
265 | u32 tmp = first_ip; | |
266 | ||
267 | first_ip = last_ip; | |
268 | last_ip = tmp; | |
269 | } | |
270 | } else if (tb[IPSET_ATTR_CIDR]) { | |
271 | u8 cidr = nla_get_u8(tb[IPSET_ATTR_CIDR]); | |
272 | ||
cabfd139 | 273 | if (cidr >= HOST_MASK) |
72205fc6 | 274 | return -IPSET_ERR_INVALID_CIDR; |
e6146e86 | 275 | ip_set_mask_from_to(first_ip, last_ip, cidr); |
ca0f6a5c | 276 | } else { |
72205fc6 | 277 | return -IPSET_ERR_PROTOCOL; |
ca0f6a5c | 278 | } |
72205fc6 JK |
279 | |
280 | if (tb[IPSET_ATTR_NETMASK]) { | |
281 | netmask = nla_get_u8(tb[IPSET_ATTR_NETMASK]); | |
282 | ||
cabfd139 | 283 | if (netmask > HOST_MASK) |
72205fc6 JK |
284 | return -IPSET_ERR_INVALID_NETMASK; |
285 | ||
286 | first_ip &= ip_set_hostmask(netmask); | |
287 | last_ip |= ~ip_set_hostmask(netmask); | |
288 | } | |
289 | ||
290 | if (netmask == 32) { | |
291 | hosts = 1; | |
b9fed748 | 292 | elements = (u64)last_ip - first_ip + 1; |
72205fc6 JK |
293 | } else { |
294 | u8 mask_bits; | |
295 | u32 mask; | |
296 | ||
297 | mask = range_to_mask(first_ip, last_ip, &mask_bits); | |
298 | ||
299 | if ((!mask && (first_ip || last_ip != 0xFFFFFFFF)) || | |
300 | netmask <= mask_bits) | |
301 | return -IPSET_ERR_BITMAP_RANGE; | |
302 | ||
303 | pr_debug("mask_bits %u, netmask %u\n", mask_bits, netmask); | |
304 | hosts = 2 << (32 - netmask - 1); | |
305 | elements = 2 << (netmask - mask_bits - 1); | |
306 | } | |
307 | if (elements > IPSET_BITMAP_MAX_RANGE + 1) | |
308 | return -IPSET_ERR_BITMAP_RANGE_SIZE; | |
309 | ||
b9fed748 JK |
310 | pr_debug("hosts %u, elements %llu\n", |
311 | hosts, (unsigned long long)elements); | |
72205fc6 | 312 | |
95ad1f4a JK |
313 | set->dsize = ip_set_elem_len(set, tb, 0, 0); |
314 | map = ip_set_alloc(sizeof(*map) + elements * set->dsize); | |
72205fc6 JK |
315 | if (!map) |
316 | return -ENOMEM; | |
317 | ||
b0da3905 JK |
318 | map->memsize = bitmap_bytes(0, elements - 1); |
319 | set->variant = &bitmap_ip; | |
03c8b234 JK |
320 | if (!init_map_ip(set, map, first_ip, last_ip, |
321 | elements, hosts, netmask)) { | |
322 | kfree(map); | |
323 | return -ENOMEM; | |
324 | } | |
325 | if (tb[IPSET_ATTR_TIMEOUT]) { | |
ca134ce8 | 326 | set->timeout = ip_set_timeout_uget(tb[IPSET_ATTR_TIMEOUT]); |
b0da3905 | 327 | bitmap_ip_gc_init(set, bitmap_ip_gc); |
72205fc6 JK |
328 | } |
329 | return 0; | |
330 | } | |
331 | ||
332 | static struct ip_set_type bitmap_ip_type __read_mostly = { | |
333 | .name = "bitmap:ip", | |
334 | .protocol = IPSET_PROTOCOL, | |
335 | .features = IPSET_TYPE_IP, | |
336 | .dimension = IPSET_DIM_ONE, | |
c15f1c83 | 337 | .family = NFPROTO_IPV4, |
35b8dcf8 JK |
338 | .revision_min = IPSET_TYPE_REV_MIN, |
339 | .revision_max = IPSET_TYPE_REV_MAX, | |
72205fc6 JK |
340 | .create = bitmap_ip_create, |
341 | .create_policy = { | |
342 | [IPSET_ATTR_IP] = { .type = NLA_NESTED }, | |
343 | [IPSET_ATTR_IP_TO] = { .type = NLA_NESTED }, | |
344 | [IPSET_ATTR_CIDR] = { .type = NLA_U8 }, | |
345 | [IPSET_ATTR_NETMASK] = { .type = NLA_U8 }, | |
346 | [IPSET_ATTR_TIMEOUT] = { .type = NLA_U32 }, | |
f48d19db | 347 | [IPSET_ATTR_CADT_FLAGS] = { .type = NLA_U32 }, |
72205fc6 JK |
348 | }, |
349 | .adt_policy = { | |
350 | [IPSET_ATTR_IP] = { .type = NLA_NESTED }, | |
351 | [IPSET_ATTR_IP_TO] = { .type = NLA_NESTED }, | |
352 | [IPSET_ATTR_CIDR] = { .type = NLA_U8 }, | |
353 | [IPSET_ATTR_TIMEOUT] = { .type = NLA_U32 }, | |
354 | [IPSET_ATTR_LINENO] = { .type = NLA_U32 }, | |
f48d19db JK |
355 | [IPSET_ATTR_BYTES] = { .type = NLA_U64 }, |
356 | [IPSET_ATTR_PACKETS] = { .type = NLA_U64 }, | |
03726186 SP |
357 | [IPSET_ATTR_COMMENT] = { .type = NLA_NUL_STRING, |
358 | .len = IPSET_MAX_COMMENT_SIZE }, | |
39d1ecf1 AD |
359 | [IPSET_ATTR_SKBMARK] = { .type = NLA_U64 }, |
360 | [IPSET_ATTR_SKBPRIO] = { .type = NLA_U32 }, | |
361 | [IPSET_ATTR_SKBQUEUE] = { .type = NLA_U16 }, | |
72205fc6 JK |
362 | }, |
363 | .me = THIS_MODULE, | |
364 | }; | |
365 | ||
366 | static int __init | |
367 | bitmap_ip_init(void) | |
368 | { | |
369 | return ip_set_type_register(&bitmap_ip_type); | |
370 | } | |
371 | ||
372 | static void __exit | |
373 | bitmap_ip_fini(void) | |
374 | { | |
96f51428 | 375 | rcu_barrier(); |
72205fc6 JK |
376 | ip_set_type_unregister(&bitmap_ip_type); |
377 | } | |
378 | ||
379 | module_init(bitmap_ip_init); | |
380 | module_exit(bitmap_ip_fini); |