Commit | Line | Data |
---|---|---|
5d50e1d8 | 1 | /* Copyright (C) 2011-2013 Jozsef Kadlecsik <kadlec@blackhole.kfki.hu> |
e385357a JK |
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,iface 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/random.h> | |
16 | #include <linux/rbtree.h> | |
17 | #include <net/ip.h> | |
18 | #include <net/ipv6.h> | |
19 | #include <net/netlink.h> | |
20 | ||
21 | #include <linux/netfilter.h> | |
22 | #include <linux/netfilter/ipset/pfxlen.h> | |
23 | #include <linux/netfilter/ipset/ip_set.h> | |
e385357a JK |
24 | #include <linux/netfilter/ipset/ip_set_hash.h> |
25 | ||
10111a6e JK |
26 | #define REVISION_MIN 0 |
27 | /* 1 nomatch flag support added */ | |
00d71b27 JK |
28 | /* 2 /0 support added */ |
29 | #define REVISION_MAX 3 /* Counters support added */ | |
10111a6e | 30 | |
e385357a JK |
31 | MODULE_LICENSE("GPL"); |
32 | MODULE_AUTHOR("Jozsef Kadlecsik <kadlec@blackhole.kfki.hu>"); | |
10111a6e | 33 | IP_SET_MODULE_DESC("hash:net,iface", REVISION_MIN, REVISION_MAX); |
e385357a JK |
34 | MODULE_ALIAS("ip_set_hash:net,iface"); |
35 | ||
36 | /* Interface name rbtree */ | |
37 | ||
38 | struct iface_node { | |
39 | struct rb_node node; | |
40 | char iface[IFNAMSIZ]; | |
41 | }; | |
42 | ||
43 | #define iface_data(n) (rb_entry(n, struct iface_node, node)->iface) | |
44 | ||
e385357a JK |
45 | static void |
46 | rbtree_destroy(struct rb_root *root) | |
47 | { | |
48 | struct rb_node *p, *n = root->rb_node; | |
49 | struct iface_node *node; | |
50 | ||
51 | /* Non-recursive destroy, like in ext3 */ | |
52 | while (n) { | |
53 | if (n->rb_left) { | |
54 | n = n->rb_left; | |
55 | continue; | |
56 | } | |
57 | if (n->rb_right) { | |
58 | n = n->rb_right; | |
59 | continue; | |
60 | } | |
61 | p = rb_parent(n); | |
62 | node = rb_entry(n, struct iface_node, node); | |
63 | if (!p) | |
64 | *root = RB_ROOT; | |
65 | else if (p->rb_left == n) | |
66 | p->rb_left = NULL; | |
67 | else if (p->rb_right == n) | |
68 | p->rb_right = NULL; | |
69 | ||
70 | kfree(node); | |
71 | n = p; | |
72 | } | |
73 | } | |
74 | ||
75 | static int | |
76 | iface_test(struct rb_root *root, const char **iface) | |
77 | { | |
78 | struct rb_node *n = root->rb_node; | |
79 | ||
80 | while (n) { | |
81 | const char *d = iface_data(n); | |
ef5b6e12 | 82 | int res = strcmp(*iface, d); |
15b4d93f | 83 | |
e385357a JK |
84 | if (res < 0) |
85 | n = n->rb_left; | |
86 | else if (res > 0) | |
87 | n = n->rb_right; | |
88 | else { | |
89 | *iface = d; | |
90 | return 1; | |
91 | } | |
92 | } | |
93 | return 0; | |
94 | } | |
95 | ||
96 | static int | |
97 | iface_add(struct rb_root *root, const char **iface) | |
98 | { | |
99 | struct rb_node **n = &(root->rb_node), *p = NULL; | |
100 | struct iface_node *d; | |
15b4d93f | 101 | |
e385357a JK |
102 | while (*n) { |
103 | char *ifname = iface_data(*n); | |
ef5b6e12 | 104 | int res = strcmp(*iface, ifname); |
e385357a JK |
105 | |
106 | p = *n; | |
107 | if (res < 0) | |
108 | n = &((*n)->rb_left); | |
109 | else if (res > 0) | |
110 | n = &((*n)->rb_right); | |
111 | else { | |
112 | *iface = ifname; | |
113 | return 0; | |
114 | } | |
115 | } | |
116 | ||
117 | d = kzalloc(sizeof(*d), GFP_ATOMIC); | |
118 | if (!d) | |
119 | return -ENOMEM; | |
120 | strcpy(d->iface, *iface); | |
121 | ||
122 | rb_link_node(&d->node, p, n); | |
123 | rb_insert_color(&d->node, root); | |
124 | ||
125 | *iface = d->iface; | |
126 | return 0; | |
127 | } | |
128 | ||
129 | /* Type specific function prefix */ | |
5d50e1d8 JK |
130 | #define HTYPE hash_netiface |
131 | #define IP_SET_HASH_WITH_NETS | |
132 | #define IP_SET_HASH_WITH_RBTREE | |
133 | #define IP_SET_HASH_WITH_MULTI | |
e385357a JK |
134 | |
135 | #define STREQ(a, b) (strcmp(a, b) == 0) | |
136 | ||
5d50e1d8 | 137 | /* IPv4 variants */ |
e385357a | 138 | |
89dc79b7 JK |
139 | struct hash_netiface4_elem_hashed { |
140 | __be32 ip; | |
141 | u8 physdev; | |
142 | u8 cidr; | |
2a7cef2a | 143 | u8 nomatch; |
bd9087e0 | 144 | u8 elem; |
89dc79b7 JK |
145 | }; |
146 | ||
e385357a JK |
147 | /* Member elements without timeout */ |
148 | struct hash_netiface4_elem { | |
149 | __be32 ip; | |
e385357a JK |
150 | u8 physdev; |
151 | u8 cidr; | |
2a7cef2a | 152 | u8 nomatch; |
bd9087e0 | 153 | u8 elem; |
89dc79b7 | 154 | const char *iface; |
e385357a JK |
155 | }; |
156 | ||
5d50e1d8 | 157 | struct hash_netiface4t_elem { |
e385357a | 158 | __be32 ip; |
e385357a JK |
159 | u8 physdev; |
160 | u8 cidr; | |
2a7cef2a | 161 | u8 nomatch; |
bd9087e0 | 162 | u8 elem; |
89dc79b7 | 163 | const char *iface; |
e385357a JK |
164 | unsigned long timeout; |
165 | }; | |
166 | ||
00d71b27 JK |
167 | struct hash_netiface4c_elem { |
168 | __be32 ip; | |
169 | u8 physdev; | |
170 | u8 cidr; | |
171 | u8 nomatch; | |
172 | u8 elem; | |
173 | const char *iface; | |
174 | struct ip_set_counter counter; | |
175 | }; | |
176 | ||
177 | struct hash_netiface4ct_elem { | |
178 | __be32 ip; | |
179 | u8 physdev; | |
180 | u8 cidr; | |
181 | u8 nomatch; | |
182 | u8 elem; | |
183 | const char *iface; | |
184 | struct ip_set_counter counter; | |
185 | unsigned long timeout; | |
186 | }; | |
187 | ||
5d50e1d8 JK |
188 | /* Common functions */ |
189 | ||
e385357a JK |
190 | static inline bool |
191 | hash_netiface4_data_equal(const struct hash_netiface4_elem *ip1, | |
89dc79b7 JK |
192 | const struct hash_netiface4_elem *ip2, |
193 | u32 *multi) | |
e385357a JK |
194 | { |
195 | return ip1->ip == ip2->ip && | |
196 | ip1->cidr == ip2->cidr && | |
89dc79b7 | 197 | (++*multi) && |
e385357a JK |
198 | ip1->physdev == ip2->physdev && |
199 | ip1->iface == ip2->iface; | |
200 | } | |
201 | ||
5d50e1d8 JK |
202 | static inline int |
203 | hash_netiface4_do_data_match(const struct hash_netiface4_elem *elem) | |
e385357a | 204 | { |
5d50e1d8 | 205 | return elem->nomatch ? -ENOTEMPTY : 1; |
e385357a JK |
206 | } |
207 | ||
208 | static inline void | |
5d50e1d8 | 209 | hash_netiface4_data_set_flags(struct hash_netiface4_elem *elem, u32 flags) |
2a7cef2a | 210 | { |
5d50e1d8 | 211 | elem->nomatch = (flags >> 16) & IPSET_FLAG_NOMATCH; |
2a7cef2a JK |
212 | } |
213 | ||
214 | static inline void | |
5d50e1d8 | 215 | hash_netiface4_data_reset_flags(struct hash_netiface4_elem *elem, u8 *flags) |
2a7cef2a | 216 | { |
5d50e1d8 | 217 | swap(*flags, elem->nomatch); |
e385357a JK |
218 | } |
219 | ||
220 | static inline void | |
221 | hash_netiface4_data_netmask(struct hash_netiface4_elem *elem, u8 cidr) | |
222 | { | |
223 | elem->ip &= ip_set_netmask(cidr); | |
224 | elem->cidr = cidr; | |
225 | } | |
226 | ||
e385357a JK |
227 | static bool |
228 | hash_netiface4_data_list(struct sk_buff *skb, | |
229 | const struct hash_netiface4_elem *data) | |
230 | { | |
231 | u32 flags = data->physdev ? IPSET_FLAG_PHYSDEV : 0; | |
232 | ||
2a7cef2a JK |
233 | if (data->nomatch) |
234 | flags |= IPSET_FLAG_NOMATCH; | |
7cf7899d DM |
235 | if (nla_put_ipaddr4(skb, IPSET_ATTR_IP, data->ip) || |
236 | nla_put_u8(skb, IPSET_ATTR_CIDR, data->cidr) || | |
237 | nla_put_string(skb, IPSET_ATTR_IFACE, data->iface) || | |
238 | (flags && | |
239 | nla_put_net32(skb, IPSET_ATTR_CADT_FLAGS, htonl(flags)))) | |
240 | goto nla_put_failure; | |
e385357a JK |
241 | return 0; |
242 | ||
243 | nla_put_failure: | |
244 | return 1; | |
245 | } | |
246 | ||
5d50e1d8 JK |
247 | static inline void |
248 | hash_netiface4_data_next(struct hash_netiface4_elem *next, | |
249 | const struct hash_netiface4_elem *d) | |
e385357a | 250 | { |
5d50e1d8 | 251 | next->ip = d->ip; |
e385357a JK |
252 | } |
253 | ||
5d50e1d8 | 254 | #define MTYPE hash_netiface4 |
e385357a JK |
255 | #define PF 4 |
256 | #define HOST_MASK 32 | |
5d50e1d8 JK |
257 | #define HKEY_DATALEN sizeof(struct hash_netiface4_elem_hashed) |
258 | #include "ip_set_hash_gen.h" | |
e385357a JK |
259 | |
260 | static int | |
261 | hash_netiface4_kadt(struct ip_set *set, const struct sk_buff *skb, | |
262 | const struct xt_action_param *par, | |
5d50e1d8 | 263 | enum ipset_adt adt, struct ip_set_adt_opt *opt) |
e385357a | 264 | { |
5d50e1d8 | 265 | struct hash_netiface *h = set->data; |
e385357a | 266 | ipset_adtfn adtfn = set->variant->adt[adt]; |
5d50e1d8 | 267 | struct hash_netiface4_elem e = { |
bd9087e0 JK |
268 | .cidr = h->nets[0].cidr ? h->nets[0].cidr : HOST_MASK, |
269 | .elem = 1, | |
e385357a | 270 | }; |
5d50e1d8 | 271 | struct ip_set_ext ext = IP_SET_INIT_KEXT(skb, opt, h); |
e385357a JK |
272 | int ret; |
273 | ||
5d50e1d8 | 274 | if (e.cidr == 0) |
e385357a JK |
275 | return -EINVAL; |
276 | if (adt == IPSET_TEST) | |
5d50e1d8 | 277 | e.cidr = HOST_MASK; |
e385357a | 278 | |
5d50e1d8 JK |
279 | ip4addrptr(skb, opt->flags & IPSET_DIM_ONE_SRC, &e.ip); |
280 | e.ip &= ip_set_netmask(e.cidr); | |
e385357a JK |
281 | |
282 | #define IFACE(dir) (par->dir ? par->dir->name : NULL) | |
283 | #define PHYSDEV(dir) (nf_bridge->dir ? nf_bridge->dir->name : NULL) | |
284 | #define SRCDIR (opt->flags & IPSET_DIM_TWO_SRC) | |
285 | ||
286 | if (opt->cmdflags & IPSET_FLAG_PHYSDEV) { | |
287 | #ifdef CONFIG_BRIDGE_NETFILTER | |
288 | const struct nf_bridge_info *nf_bridge = skb->nf_bridge; | |
15b4d93f | 289 | |
e385357a JK |
290 | if (!nf_bridge) |
291 | return -EINVAL; | |
5d50e1d8 JK |
292 | e.iface = SRCDIR ? PHYSDEV(physindev) : PHYSDEV(physoutdev); |
293 | e.physdev = 1; | |
e385357a | 294 | #else |
5d50e1d8 | 295 | e.iface = NULL; |
e385357a JK |
296 | #endif |
297 | } else | |
5d50e1d8 | 298 | e.iface = SRCDIR ? IFACE(in) : IFACE(out); |
e385357a | 299 | |
5d50e1d8 | 300 | if (!e.iface) |
e385357a | 301 | return -EINVAL; |
5d50e1d8 | 302 | ret = iface_test(&h->rbtree, &e.iface); |
e385357a JK |
303 | if (adt == IPSET_ADD) { |
304 | if (!ret) { | |
5d50e1d8 | 305 | ret = iface_add(&h->rbtree, &e.iface); |
e385357a JK |
306 | if (ret) |
307 | return ret; | |
308 | } | |
309 | } else if (!ret) | |
310 | return ret; | |
311 | ||
5d50e1d8 | 312 | return adtfn(set, &e, &ext, &opt->ext, opt->cmdflags); |
e385357a JK |
313 | } |
314 | ||
315 | static int | |
316 | hash_netiface4_uadt(struct ip_set *set, struct nlattr *tb[], | |
317 | enum ipset_adt adt, u32 *lineno, u32 flags, bool retried) | |
318 | { | |
5d50e1d8 | 319 | struct hash_netiface *h = set->data; |
e385357a | 320 | ipset_adtfn adtfn = set->variant->adt[adt]; |
5d50e1d8 JK |
321 | struct hash_netiface4_elem e = { .cidr = HOST_MASK, .elem = 1 }; |
322 | struct ip_set_ext ext = IP_SET_INIT_UEXT(h); | |
e385357a | 323 | u32 ip = 0, ip_to, last; |
ef5b6e12 | 324 | char iface[IFNAMSIZ]; |
e385357a JK |
325 | int ret; |
326 | ||
327 | if (unlikely(!tb[IPSET_ATTR_IP] || | |
328 | !tb[IPSET_ATTR_IFACE] || | |
329 | !ip_set_optattr_netorder(tb, IPSET_ATTR_TIMEOUT) || | |
00d71b27 JK |
330 | !ip_set_optattr_netorder(tb, IPSET_ATTR_CADT_FLAGS) || |
331 | !ip_set_optattr_netorder(tb, IPSET_ATTR_PACKETS) || | |
332 | !ip_set_optattr_netorder(tb, IPSET_ATTR_BYTES))) | |
e385357a JK |
333 | return -IPSET_ERR_PROTOCOL; |
334 | ||
335 | if (tb[IPSET_ATTR_LINENO]) | |
336 | *lineno = nla_get_u32(tb[IPSET_ATTR_LINENO]); | |
337 | ||
5d50e1d8 JK |
338 | ret = ip_set_get_hostipaddr4(tb[IPSET_ATTR_IP], &ip) || |
339 | ip_set_get_extensions(set, tb, &ext); | |
e385357a JK |
340 | if (ret) |
341 | return ret; | |
342 | ||
343 | if (tb[IPSET_ATTR_CIDR]) { | |
5d50e1d8 JK |
344 | e.cidr = nla_get_u8(tb[IPSET_ATTR_CIDR]); |
345 | if (e.cidr > HOST_MASK) | |
15b4d93f | 346 | return -IPSET_ERR_INVALID_CIDR; |
e385357a JK |
347 | } |
348 | ||
15b4d93f | 349 | strcpy(iface, nla_data(tb[IPSET_ATTR_IFACE])); |
5d50e1d8 JK |
350 | e.iface = iface; |
351 | ret = iface_test(&h->rbtree, &e.iface); | |
e385357a JK |
352 | if (adt == IPSET_ADD) { |
353 | if (!ret) { | |
5d50e1d8 | 354 | ret = iface_add(&h->rbtree, &e.iface); |
e385357a JK |
355 | if (ret) |
356 | return ret; | |
357 | } | |
358 | } else if (!ret) | |
359 | return ret; | |
360 | ||
361 | if (tb[IPSET_ATTR_CADT_FLAGS]) { | |
15b4d93f JK |
362 | u32 cadt_flags = ip_set_get_h32(tb[IPSET_ATTR_CADT_FLAGS]); |
363 | if (cadt_flags & IPSET_FLAG_PHYSDEV) | |
5d50e1d8 | 364 | e.physdev = 1; |
43c56e59 JK |
365 | if (cadt_flags & IPSET_FLAG_NOMATCH) |
366 | flags |= (IPSET_FLAG_NOMATCH << 16); | |
e385357a | 367 | } |
e385357a | 368 | if (adt == IPSET_TEST || !tb[IPSET_ATTR_IP_TO]) { |
5d50e1d8 JK |
369 | e.ip = htonl(ip & ip_set_hostmask(e.cidr)); |
370 | ret = adtfn(set, &e, &ext, &ext, flags); | |
0f1799ba | 371 | return ip_set_enomatch(ret, flags, adt, set) ? -ret : |
43c56e59 | 372 | ip_set_eexist(ret, flags) ? 0 : ret; |
e385357a JK |
373 | } |
374 | ||
375 | if (tb[IPSET_ATTR_IP_TO]) { | |
376 | ret = ip_set_get_hostipaddr4(tb[IPSET_ATTR_IP_TO], &ip_to); | |
377 | if (ret) | |
378 | return ret; | |
379 | if (ip_to < ip) | |
380 | swap(ip, ip_to); | |
381 | if (ip + UINT_MAX == ip_to) | |
382 | return -IPSET_ERR_HASH_RANGE; | |
5d50e1d8 JK |
383 | } else |
384 | ip_set_mask_from_to(ip, ip_to, e.cidr); | |
e385357a JK |
385 | |
386 | if (retried) | |
6e27c9b4 | 387 | ip = ntohl(h->next.ip); |
e385357a | 388 | while (!after(ip, ip_to)) { |
5d50e1d8 JK |
389 | e.ip = htonl(ip); |
390 | last = ip_set_range_to_cidr(ip, ip_to, &e.cidr); | |
391 | ret = adtfn(set, &e, &ext, &ext, flags); | |
e385357a JK |
392 | |
393 | if (ret && !ip_set_eexist(ret, flags)) | |
394 | return ret; | |
395 | else | |
396 | ret = 0; | |
397 | ip = last + 1; | |
398 | } | |
399 | return ret; | |
400 | } | |
401 | ||
5d50e1d8 | 402 | /* IPv6 variants */ |
e385357a | 403 | |
89dc79b7 JK |
404 | struct hash_netiface6_elem_hashed { |
405 | union nf_inet_addr ip; | |
406 | u8 physdev; | |
407 | u8 cidr; | |
2a7cef2a | 408 | u8 nomatch; |
bd9087e0 | 409 | u8 elem; |
89dc79b7 JK |
410 | }; |
411 | ||
e385357a JK |
412 | struct hash_netiface6_elem { |
413 | union nf_inet_addr ip; | |
e385357a JK |
414 | u8 physdev; |
415 | u8 cidr; | |
2a7cef2a | 416 | u8 nomatch; |
bd9087e0 | 417 | u8 elem; |
89dc79b7 | 418 | const char *iface; |
e385357a JK |
419 | }; |
420 | ||
5d50e1d8 | 421 | struct hash_netiface6t_elem { |
e385357a | 422 | union nf_inet_addr ip; |
e385357a JK |
423 | u8 physdev; |
424 | u8 cidr; | |
2a7cef2a | 425 | u8 nomatch; |
bd9087e0 | 426 | u8 elem; |
89dc79b7 | 427 | const char *iface; |
e385357a JK |
428 | unsigned long timeout; |
429 | }; | |
430 | ||
00d71b27 JK |
431 | struct hash_netiface6c_elem { |
432 | union nf_inet_addr ip; | |
433 | u8 physdev; | |
434 | u8 cidr; | |
435 | u8 nomatch; | |
436 | u8 elem; | |
437 | const char *iface; | |
438 | struct ip_set_counter counter; | |
439 | }; | |
440 | ||
441 | struct hash_netiface6ct_elem { | |
442 | union nf_inet_addr ip; | |
443 | u8 physdev; | |
444 | u8 cidr; | |
445 | u8 nomatch; | |
446 | u8 elem; | |
447 | const char *iface; | |
448 | struct ip_set_counter counter; | |
449 | unsigned long timeout; | |
450 | }; | |
451 | ||
5d50e1d8 JK |
452 | /* Common functions */ |
453 | ||
e385357a JK |
454 | static inline bool |
455 | hash_netiface6_data_equal(const struct hash_netiface6_elem *ip1, | |
89dc79b7 JK |
456 | const struct hash_netiface6_elem *ip2, |
457 | u32 *multi) | |
e385357a | 458 | { |
29e3b160 | 459 | return ipv6_addr_equal(&ip1->ip.in6, &ip2->ip.in6) && |
e385357a | 460 | ip1->cidr == ip2->cidr && |
89dc79b7 | 461 | (++*multi) && |
e385357a JK |
462 | ip1->physdev == ip2->physdev && |
463 | ip1->iface == ip2->iface; | |
464 | } | |
465 | ||
3e0304a5 | 466 | static inline int |
5d50e1d8 | 467 | hash_netiface6_do_data_match(const struct hash_netiface6_elem *elem) |
2a7cef2a | 468 | { |
3e0304a5 | 469 | return elem->nomatch ? -ENOTEMPTY : 1; |
2a7cef2a JK |
470 | } |
471 | ||
6eb4c7e9 | 472 | static inline void |
5d50e1d8 | 473 | hash_netiface6_data_set_flags(struct hash_netiface6_elem *elem, u32 flags) |
6eb4c7e9 | 474 | { |
5d50e1d8 | 475 | elem->nomatch = (flags >> 16) & IPSET_FLAG_NOMATCH; |
6eb4c7e9 JK |
476 | } |
477 | ||
e385357a | 478 | static inline void |
5d50e1d8 | 479 | hash_netiface6_data_reset_flags(struct hash_netiface6_elem *elem, u8 *flags) |
e385357a | 480 | { |
5d50e1d8 | 481 | swap(*flags, elem->nomatch); |
e385357a JK |
482 | } |
483 | ||
e385357a JK |
484 | static inline void |
485 | hash_netiface6_data_netmask(struct hash_netiface6_elem *elem, u8 cidr) | |
486 | { | |
487 | ip6_netmask(&elem->ip, cidr); | |
488 | elem->cidr = cidr; | |
489 | } | |
490 | ||
491 | static bool | |
492 | hash_netiface6_data_list(struct sk_buff *skb, | |
493 | const struct hash_netiface6_elem *data) | |
494 | { | |
495 | u32 flags = data->physdev ? IPSET_FLAG_PHYSDEV : 0; | |
496 | ||
2a7cef2a JK |
497 | if (data->nomatch) |
498 | flags |= IPSET_FLAG_NOMATCH; | |
7cf7899d DM |
499 | if (nla_put_ipaddr6(skb, IPSET_ATTR_IP, &data->ip.in6) || |
500 | nla_put_u8(skb, IPSET_ATTR_CIDR, data->cidr) || | |
501 | nla_put_string(skb, IPSET_ATTR_IFACE, data->iface) || | |
502 | (flags && | |
503 | nla_put_net32(skb, IPSET_ATTR_CADT_FLAGS, htonl(flags)))) | |
504 | goto nla_put_failure; | |
e385357a JK |
505 | return 0; |
506 | ||
507 | nla_put_failure: | |
508 | return 1; | |
509 | } | |
510 | ||
5d50e1d8 JK |
511 | static inline void |
512 | hash_netiface6_data_next(struct hash_netiface4_elem *next, | |
513 | const struct hash_netiface6_elem *d) | |
e385357a | 514 | { |
e385357a JK |
515 | } |
516 | ||
5d50e1d8 | 517 | #undef MTYPE |
e385357a JK |
518 | #undef PF |
519 | #undef HOST_MASK | |
5d50e1d8 | 520 | #undef HKEY_DATALEN |
e385357a | 521 | |
5d50e1d8 | 522 | #define MTYPE hash_netiface6 |
e385357a JK |
523 | #define PF 6 |
524 | #define HOST_MASK 128 | |
5d50e1d8 JK |
525 | #define HKEY_DATALEN sizeof(struct hash_netiface6_elem_hashed) |
526 | #define IP_SET_EMIT_CREATE | |
527 | #include "ip_set_hash_gen.h" | |
e385357a JK |
528 | |
529 | static int | |
530 | hash_netiface6_kadt(struct ip_set *set, const struct sk_buff *skb, | |
531 | const struct xt_action_param *par, | |
5d50e1d8 | 532 | enum ipset_adt adt, struct ip_set_adt_opt *opt) |
e385357a | 533 | { |
5d50e1d8 | 534 | struct hash_netiface *h = set->data; |
e385357a | 535 | ipset_adtfn adtfn = set->variant->adt[adt]; |
5d50e1d8 | 536 | struct hash_netiface6_elem e = { |
bd9087e0 JK |
537 | .cidr = h->nets[0].cidr ? h->nets[0].cidr : HOST_MASK, |
538 | .elem = 1, | |
e385357a | 539 | }; |
5d50e1d8 | 540 | struct ip_set_ext ext = IP_SET_INIT_KEXT(skb, opt, h); |
e385357a JK |
541 | int ret; |
542 | ||
5d50e1d8 | 543 | if (e.cidr == 0) |
e385357a JK |
544 | return -EINVAL; |
545 | if (adt == IPSET_TEST) | |
5d50e1d8 | 546 | e.cidr = HOST_MASK; |
e385357a | 547 | |
5d50e1d8 JK |
548 | ip6addrptr(skb, opt->flags & IPSET_DIM_ONE_SRC, &e.ip.in6); |
549 | ip6_netmask(&e.ip, e.cidr); | |
e385357a JK |
550 | |
551 | if (opt->cmdflags & IPSET_FLAG_PHYSDEV) { | |
552 | #ifdef CONFIG_BRIDGE_NETFILTER | |
553 | const struct nf_bridge_info *nf_bridge = skb->nf_bridge; | |
15b4d93f | 554 | |
e385357a JK |
555 | if (!nf_bridge) |
556 | return -EINVAL; | |
5d50e1d8 JK |
557 | e.iface = SRCDIR ? PHYSDEV(physindev) : PHYSDEV(physoutdev); |
558 | e.physdev = 1; | |
e385357a | 559 | #else |
5d50e1d8 | 560 | e.iface = NULL; |
e385357a JK |
561 | #endif |
562 | } else | |
5d50e1d8 | 563 | e.iface = SRCDIR ? IFACE(in) : IFACE(out); |
e385357a | 564 | |
5d50e1d8 | 565 | if (!e.iface) |
e385357a | 566 | return -EINVAL; |
5d50e1d8 | 567 | ret = iface_test(&h->rbtree, &e.iface); |
e385357a JK |
568 | if (adt == IPSET_ADD) { |
569 | if (!ret) { | |
5d50e1d8 | 570 | ret = iface_add(&h->rbtree, &e.iface); |
e385357a JK |
571 | if (ret) |
572 | return ret; | |
573 | } | |
574 | } else if (!ret) | |
575 | return ret; | |
576 | ||
5d50e1d8 | 577 | return adtfn(set, &e, &ext, &opt->ext, opt->cmdflags); |
e385357a JK |
578 | } |
579 | ||
580 | static int | |
581 | hash_netiface6_uadt(struct ip_set *set, struct nlattr *tb[], | |
582 | enum ipset_adt adt, u32 *lineno, u32 flags, bool retried) | |
583 | { | |
5d50e1d8 | 584 | struct hash_netiface *h = set->data; |
e385357a | 585 | ipset_adtfn adtfn = set->variant->adt[adt]; |
5d50e1d8 JK |
586 | struct hash_netiface6_elem e = { .cidr = HOST_MASK, .elem = 1 }; |
587 | struct ip_set_ext ext = IP_SET_INIT_UEXT(h); | |
ef5b6e12 | 588 | char iface[IFNAMSIZ]; |
e385357a JK |
589 | int ret; |
590 | ||
591 | if (unlikely(!tb[IPSET_ATTR_IP] || | |
592 | !tb[IPSET_ATTR_IFACE] || | |
593 | !ip_set_optattr_netorder(tb, IPSET_ATTR_TIMEOUT) || | |
00d71b27 JK |
594 | !ip_set_optattr_netorder(tb, IPSET_ATTR_CADT_FLAGS) || |
595 | !ip_set_optattr_netorder(tb, IPSET_ATTR_PACKETS) || | |
596 | !ip_set_optattr_netorder(tb, IPSET_ATTR_BYTES))) | |
e385357a JK |
597 | return -IPSET_ERR_PROTOCOL; |
598 | if (unlikely(tb[IPSET_ATTR_IP_TO])) | |
599 | return -IPSET_ERR_HASH_RANGE_UNSUPPORTED; | |
600 | ||
601 | if (tb[IPSET_ATTR_LINENO]) | |
602 | *lineno = nla_get_u32(tb[IPSET_ATTR_LINENO]); | |
603 | ||
5d50e1d8 JK |
604 | ret = ip_set_get_ipaddr6(tb[IPSET_ATTR_IP], &e.ip) || |
605 | ip_set_get_extensions(set, tb, &ext); | |
e385357a JK |
606 | if (ret) |
607 | return ret; | |
608 | ||
609 | if (tb[IPSET_ATTR_CIDR]) | |
5d50e1d8 JK |
610 | e.cidr = nla_get_u8(tb[IPSET_ATTR_CIDR]); |
611 | if (e.cidr > HOST_MASK) | |
e385357a | 612 | return -IPSET_ERR_INVALID_CIDR; |
5d50e1d8 | 613 | ip6_netmask(&e.ip, e.cidr); |
e385357a | 614 | |
15b4d93f | 615 | strcpy(iface, nla_data(tb[IPSET_ATTR_IFACE])); |
5d50e1d8 JK |
616 | e.iface = iface; |
617 | ret = iface_test(&h->rbtree, &e.iface); | |
e385357a JK |
618 | if (adt == IPSET_ADD) { |
619 | if (!ret) { | |
5d50e1d8 | 620 | ret = iface_add(&h->rbtree, &e.iface); |
e385357a JK |
621 | if (ret) |
622 | return ret; | |
623 | } | |
624 | } else if (!ret) | |
625 | return ret; | |
626 | ||
627 | if (tb[IPSET_ATTR_CADT_FLAGS]) { | |
15b4d93f JK |
628 | u32 cadt_flags = ip_set_get_h32(tb[IPSET_ATTR_CADT_FLAGS]); |
629 | if (cadt_flags & IPSET_FLAG_PHYSDEV) | |
5d50e1d8 | 630 | e.physdev = 1; |
43c56e59 JK |
631 | if (cadt_flags & IPSET_FLAG_NOMATCH) |
632 | flags |= (IPSET_FLAG_NOMATCH << 16); | |
e385357a JK |
633 | } |
634 | ||
5d50e1d8 | 635 | ret = adtfn(set, &e, &ext, &ext, flags); |
e385357a | 636 | |
0f1799ba | 637 | return ip_set_enomatch(ret, flags, adt, set) ? -ret : |
43c56e59 | 638 | ip_set_eexist(ret, flags) ? 0 : ret; |
e385357a JK |
639 | } |
640 | ||
e385357a JK |
641 | static struct ip_set_type hash_netiface_type __read_mostly = { |
642 | .name = "hash:net,iface", | |
643 | .protocol = IPSET_PROTOCOL, | |
3e0304a5 JK |
644 | .features = IPSET_TYPE_IP | IPSET_TYPE_IFACE | |
645 | IPSET_TYPE_NOMATCH, | |
e385357a | 646 | .dimension = IPSET_DIM_TWO, |
c15f1c83 | 647 | .family = NFPROTO_UNSPEC, |
10111a6e JK |
648 | .revision_min = REVISION_MIN, |
649 | .revision_max = REVISION_MAX, | |
e385357a JK |
650 | .create = hash_netiface_create, |
651 | .create_policy = { | |
652 | [IPSET_ATTR_HASHSIZE] = { .type = NLA_U32 }, | |
653 | [IPSET_ATTR_MAXELEM] = { .type = NLA_U32 }, | |
654 | [IPSET_ATTR_PROBES] = { .type = NLA_U8 }, | |
655 | [IPSET_ATTR_RESIZE] = { .type = NLA_U8 }, | |
656 | [IPSET_ATTR_PROTO] = { .type = NLA_U8 }, | |
657 | [IPSET_ATTR_TIMEOUT] = { .type = NLA_U32 }, | |
5d50e1d8 | 658 | [IPSET_ATTR_CADT_FLAGS] = { .type = NLA_U32 }, |
e385357a JK |
659 | }, |
660 | .adt_policy = { | |
661 | [IPSET_ATTR_IP] = { .type = NLA_NESTED }, | |
662 | [IPSET_ATTR_IP_TO] = { .type = NLA_NESTED }, | |
663 | [IPSET_ATTR_IFACE] = { .type = NLA_NUL_STRING, | |
4a6dd664 | 664 | .len = IFNAMSIZ - 1 }, |
e385357a JK |
665 | [IPSET_ATTR_CADT_FLAGS] = { .type = NLA_U32 }, |
666 | [IPSET_ATTR_CIDR] = { .type = NLA_U8 }, | |
667 | [IPSET_ATTR_TIMEOUT] = { .type = NLA_U32 }, | |
668 | [IPSET_ATTR_LINENO] = { .type = NLA_U32 }, | |
00d71b27 JK |
669 | [IPSET_ATTR_BYTES] = { .type = NLA_U64 }, |
670 | [IPSET_ATTR_PACKETS] = { .type = NLA_U64 }, | |
e385357a JK |
671 | }, |
672 | .me = THIS_MODULE, | |
673 | }; | |
674 | ||
675 | static int __init | |
676 | hash_netiface_init(void) | |
677 | { | |
678 | return ip_set_type_register(&hash_netiface_type); | |
679 | } | |
680 | ||
681 | static void __exit | |
682 | hash_netiface_fini(void) | |
683 | { | |
684 | ip_set_type_unregister(&hash_netiface_type); | |
685 | } | |
686 | ||
687 | module_init(hash_netiface_init); | |
688 | module_exit(hash_netiface_fini); |