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