]>
Commit | Line | Data |
---|---|---|
d2912cb1 | 1 | // SPDX-License-Identifier: GPL-2.0-only |
1c5ba67d | 2 | /* Copyright (C) 2011-2013 Jozsef Kadlecsik <kadlec@netfilter.org> */ |
e385357a JK |
3 | |
4 | /* Kernel module implementing an IP set type: the hash:net,iface type */ | |
5 | ||
6 | #include <linux/jhash.h> | |
7 | #include <linux/module.h> | |
8 | #include <linux/ip.h> | |
9 | #include <linux/skbuff.h> | |
10 | #include <linux/errno.h> | |
11 | #include <linux/random.h> | |
e385357a JK |
12 | #include <net/ip.h> |
13 | #include <net/ipv6.h> | |
14 | #include <net/netlink.h> | |
15 | ||
16 | #include <linux/netfilter.h> | |
c737b7c4 | 17 | #include <linux/netfilter_bridge.h> |
e385357a JK |
18 | #include <linux/netfilter/ipset/pfxlen.h> |
19 | #include <linux/netfilter/ipset/ip_set.h> | |
e385357a JK |
20 | #include <linux/netfilter/ipset/ip_set_hash.h> |
21 | ||
35b8dcf8 JK |
22 | #define IPSET_TYPE_REV_MIN 0 |
23 | /* 1 nomatch flag support added */ | |
24 | /* 2 /0 support added */ | |
fda75c6d | 25 | /* 3 Counters support added */ |
07cf8f5a | 26 | /* 4 Comments support added */ |
af331419 | 27 | /* 5 Forceadd support added */ |
b6520fce | 28 | /* 6 skbinfo support added */ |
ccf0a4b7 | 29 | /* 7 interface wildcard support added */ |
3976ca10 | 30 | #define IPSET_TYPE_REV_MAX 8 /* bucketsize, initval support added */ |
10111a6e | 31 | |
e385357a | 32 | MODULE_LICENSE("GPL"); |
fe03d474 | 33 | MODULE_AUTHOR("Jozsef Kadlecsik <kadlec@netfilter.org>"); |
35b8dcf8 | 34 | IP_SET_MODULE_DESC("hash:net,iface", IPSET_TYPE_REV_MIN, IPSET_TYPE_REV_MAX); |
e385357a JK |
35 | MODULE_ALIAS("ip_set_hash:net,iface"); |
36 | ||
e385357a | 37 | /* Type specific function prefix */ |
5d50e1d8 JK |
38 | #define HTYPE hash_netiface |
39 | #define IP_SET_HASH_WITH_NETS | |
5d50e1d8 | 40 | #define IP_SET_HASH_WITH_MULTI |
59de79cf | 41 | #define IP_SET_HASH_WITH_NET0 |
e385357a | 42 | |
bd55389c | 43 | #define STRLCPY(a, b) strlcpy(a, b, IFNAMSIZ) |
e385357a | 44 | |
03c8b234 | 45 | /* IPv4 variant */ |
e385357a | 46 | |
89dc79b7 JK |
47 | struct hash_netiface4_elem_hashed { |
48 | __be32 ip; | |
49 | u8 physdev; | |
50 | u8 cidr; | |
2a7cef2a | 51 | u8 nomatch; |
bd9087e0 | 52 | u8 elem; |
89dc79b7 JK |
53 | }; |
54 | ||
03c8b234 | 55 | /* Member elements */ |
e385357a JK |
56 | struct hash_netiface4_elem { |
57 | __be32 ip; | |
e385357a JK |
58 | u8 physdev; |
59 | u8 cidr; | |
2a7cef2a | 60 | u8 nomatch; |
bd9087e0 | 61 | u8 elem; |
b6520fce | 62 | u8 wildcard; |
bd55389c | 63 | char iface[IFNAMSIZ]; |
e385357a JK |
64 | }; |
65 | ||
5d50e1d8 JK |
66 | /* Common functions */ |
67 | ||
8dea982a | 68 | static bool |
e385357a | 69 | hash_netiface4_data_equal(const struct hash_netiface4_elem *ip1, |
89dc79b7 JK |
70 | const struct hash_netiface4_elem *ip2, |
71 | u32 *multi) | |
e385357a JK |
72 | { |
73 | return ip1->ip == ip2->ip && | |
74 | ip1->cidr == ip2->cidr && | |
89dc79b7 | 75 | (++*multi) && |
e385357a | 76 | ip1->physdev == ip2->physdev && |
b6520fce KE |
77 | (ip1->wildcard ? |
78 | strncmp(ip1->iface, ip2->iface, strlen(ip1->iface)) == 0 : | |
79 | strcmp(ip1->iface, ip2->iface) == 0); | |
e385357a JK |
80 | } |
81 | ||
8dea982a | 82 | static int |
5d50e1d8 | 83 | hash_netiface4_do_data_match(const struct hash_netiface4_elem *elem) |
e385357a | 84 | { |
5d50e1d8 | 85 | return elem->nomatch ? -ENOTEMPTY : 1; |
e385357a JK |
86 | } |
87 | ||
8dea982a | 88 | static void |
5d50e1d8 | 89 | hash_netiface4_data_set_flags(struct hash_netiface4_elem *elem, u32 flags) |
2a7cef2a | 90 | { |
5d50e1d8 | 91 | elem->nomatch = (flags >> 16) & IPSET_FLAG_NOMATCH; |
2a7cef2a JK |
92 | } |
93 | ||
8dea982a | 94 | static void |
5d50e1d8 | 95 | hash_netiface4_data_reset_flags(struct hash_netiface4_elem *elem, u8 *flags) |
2a7cef2a | 96 | { |
5d50e1d8 | 97 | swap(*flags, elem->nomatch); |
e385357a JK |
98 | } |
99 | ||
8dea982a | 100 | static void |
e385357a JK |
101 | hash_netiface4_data_netmask(struct hash_netiface4_elem *elem, u8 cidr) |
102 | { | |
103 | elem->ip &= ip_set_netmask(cidr); | |
104 | elem->cidr = cidr; | |
105 | } | |
106 | ||
e385357a JK |
107 | static bool |
108 | hash_netiface4_data_list(struct sk_buff *skb, | |
109 | const struct hash_netiface4_elem *data) | |
110 | { | |
b6520fce KE |
111 | u32 flags = (data->physdev ? IPSET_FLAG_PHYSDEV : 0) | |
112 | (data->wildcard ? IPSET_FLAG_IFACE_WILDCARD : 0); | |
e385357a | 113 | |
2a7cef2a JK |
114 | if (data->nomatch) |
115 | flags |= IPSET_FLAG_NOMATCH; | |
7cf7899d DM |
116 | if (nla_put_ipaddr4(skb, IPSET_ATTR_IP, data->ip) || |
117 | nla_put_u8(skb, IPSET_ATTR_CIDR, data->cidr) || | |
118 | nla_put_string(skb, IPSET_ATTR_IFACE, data->iface) || | |
119 | (flags && | |
120 | nla_put_net32(skb, IPSET_ATTR_CADT_FLAGS, htonl(flags)))) | |
121 | goto nla_put_failure; | |
728a7e69 | 122 | return false; |
e385357a JK |
123 | |
124 | nla_put_failure: | |
728a7e69 | 125 | return true; |
e385357a JK |
126 | } |
127 | ||
8dea982a | 128 | static void |
5d50e1d8 JK |
129 | hash_netiface4_data_next(struct hash_netiface4_elem *next, |
130 | const struct hash_netiface4_elem *d) | |
e385357a | 131 | { |
5d50e1d8 | 132 | next->ip = d->ip; |
e385357a JK |
133 | } |
134 | ||
5d50e1d8 | 135 | #define MTYPE hash_netiface4 |
e385357a | 136 | #define HOST_MASK 32 |
5d50e1d8 JK |
137 | #define HKEY_DATALEN sizeof(struct hash_netiface4_elem_hashed) |
138 | #include "ip_set_hash_gen.h" | |
e385357a | 139 | |
c737b7c4 FW |
140 | #if IS_ENABLED(CONFIG_BRIDGE_NETFILTER) |
141 | static const char *get_physindev_name(const struct sk_buff *skb) | |
142 | { | |
143 | struct net_device *dev = nf_bridge_get_physindev(skb); | |
144 | ||
145 | return dev ? dev->name : NULL; | |
146 | } | |
147 | ||
ca0f6a5c | 148 | static const char *get_physoutdev_name(const struct sk_buff *skb) |
c737b7c4 FW |
149 | { |
150 | struct net_device *dev = nf_bridge_get_physoutdev(skb); | |
151 | ||
152 | return dev ? dev->name : NULL; | |
153 | } | |
154 | #endif | |
155 | ||
e385357a JK |
156 | static int |
157 | hash_netiface4_kadt(struct ip_set *set, const struct sk_buff *skb, | |
158 | const struct xt_action_param *par, | |
5d50e1d8 | 159 | enum ipset_adt adt, struct ip_set_adt_opt *opt) |
e385357a | 160 | { |
21956ab2 | 161 | struct hash_netiface4 *h = set->data; |
e385357a | 162 | ipset_adtfn adtfn = set->variant->adt[adt]; |
5d50e1d8 | 163 | struct hash_netiface4_elem e = { |
f690cbae | 164 | .cidr = INIT_CIDR(h->nets[0].cidr[0], HOST_MASK), |
bd9087e0 | 165 | .elem = 1, |
e385357a | 166 | }; |
ca134ce8 | 167 | struct ip_set_ext ext = IP_SET_INIT_KEXT(skb, opt, set); |
e385357a | 168 | |
e385357a | 169 | if (adt == IPSET_TEST) |
5d50e1d8 | 170 | e.cidr = HOST_MASK; |
e385357a | 171 | |
5d50e1d8 JK |
172 | ip4addrptr(skb, opt->flags & IPSET_DIM_ONE_SRC, &e.ip); |
173 | e.ip &= ip_set_netmask(e.cidr); | |
e385357a | 174 | |
613dbd95 | 175 | #define IFACE(dir) (par->state->dir ? par->state->dir->name : "") |
e385357a JK |
176 | #define SRCDIR (opt->flags & IPSET_DIM_TWO_SRC) |
177 | ||
178 | if (opt->cmdflags & IPSET_FLAG_PHYSDEV) { | |
1109a90c | 179 | #if IS_ENABLED(CONFIG_BRIDGE_NETFILTER) |
bd55389c | 180 | const char *eiface = SRCDIR ? get_physindev_name(skb) : |
ca0f6a5c | 181 | get_physoutdev_name(skb); |
15b4d93f | 182 | |
bd55389c | 183 | if (!eiface) |
e385357a | 184 | return -EINVAL; |
bd55389c | 185 | STRLCPY(e.iface, eiface); |
5d50e1d8 | 186 | e.physdev = 1; |
e385357a | 187 | #endif |
ca0f6a5c | 188 | } else { |
bd55389c | 189 | STRLCPY(e.iface, SRCDIR ? IFACE(in) : IFACE(out)); |
ca0f6a5c | 190 | } |
e385357a | 191 | |
bd55389c | 192 | if (strlen(e.iface) == 0) |
e385357a | 193 | return -EINVAL; |
5d50e1d8 | 194 | return adtfn(set, &e, &ext, &opt->ext, opt->cmdflags); |
e385357a JK |
195 | } |
196 | ||
197 | static int | |
198 | hash_netiface4_uadt(struct ip_set *set, struct nlattr *tb[], | |
199 | enum ipset_adt adt, u32 *lineno, u32 flags, bool retried) | |
200 | { | |
21956ab2 | 201 | struct hash_netiface4 *h = set->data; |
e385357a | 202 | ipset_adtfn adtfn = set->variant->adt[adt]; |
5d50e1d8 | 203 | struct hash_netiface4_elem e = { .cidr = HOST_MASK, .elem = 1 }; |
ca134ce8 | 204 | struct ip_set_ext ext = IP_SET_INIT_UEXT(set); |
aa67f109 | 205 | u32 ip = 0, ip_to = 0, ipn, n = 0; |
e385357a JK |
206 | int ret; |
207 | ||
a212e08e SP |
208 | if (tb[IPSET_ATTR_LINENO]) |
209 | *lineno = nla_get_u32(tb[IPSET_ATTR_LINENO]); | |
210 | ||
e385357a JK |
211 | if (unlikely(!tb[IPSET_ATTR_IP] || |
212 | !tb[IPSET_ATTR_IFACE] || | |
7dd37bc8 | 213 | !ip_set_optattr_netorder(tb, IPSET_ATTR_CADT_FLAGS))) |
e385357a JK |
214 | return -IPSET_ERR_PROTOCOL; |
215 | ||
8e55d2e5 SP |
216 | ret = ip_set_get_hostipaddr4(tb[IPSET_ATTR_IP], &ip); |
217 | if (ret) | |
218 | return ret; | |
219 | ||
220 | ret = ip_set_get_extensions(set, tb, &ext); | |
e385357a JK |
221 | if (ret) |
222 | return ret; | |
223 | ||
224 | if (tb[IPSET_ATTR_CIDR]) { | |
5d50e1d8 JK |
225 | e.cidr = nla_get_u8(tb[IPSET_ATTR_CIDR]); |
226 | if (e.cidr > HOST_MASK) | |
15b4d93f | 227 | return -IPSET_ERR_INVALID_CIDR; |
e385357a | 228 | } |
872f6903 | 229 | nla_strscpy(e.iface, tb[IPSET_ATTR_IFACE], IFNAMSIZ); |
e385357a JK |
230 | |
231 | if (tb[IPSET_ATTR_CADT_FLAGS]) { | |
15b4d93f | 232 | u32 cadt_flags = ip_set_get_h32(tb[IPSET_ATTR_CADT_FLAGS]); |
ca0f6a5c | 233 | |
15b4d93f | 234 | if (cadt_flags & IPSET_FLAG_PHYSDEV) |
5d50e1d8 | 235 | e.physdev = 1; |
43c56e59 JK |
236 | if (cadt_flags & IPSET_FLAG_NOMATCH) |
237 | flags |= (IPSET_FLAG_NOMATCH << 16); | |
b6520fce KE |
238 | if (cadt_flags & IPSET_FLAG_IFACE_WILDCARD) |
239 | e.wildcard = 1; | |
e385357a | 240 | } |
e385357a | 241 | if (adt == IPSET_TEST || !tb[IPSET_ATTR_IP_TO]) { |
5d50e1d8 JK |
242 | e.ip = htonl(ip & ip_set_hostmask(e.cidr)); |
243 | ret = adtfn(set, &e, &ext, &ext, flags); | |
0f1799ba | 244 | return ip_set_enomatch(ret, flags, adt, set) ? -ret : |
43c56e59 | 245 | ip_set_eexist(ret, flags) ? 0 : ret; |
e385357a JK |
246 | } |
247 | ||
248 | if (tb[IPSET_ATTR_IP_TO]) { | |
249 | ret = ip_set_get_hostipaddr4(tb[IPSET_ATTR_IP_TO], &ip_to); | |
250 | if (ret) | |
251 | return ret; | |
252 | if (ip_to < ip) | |
253 | swap(ip, ip_to); | |
254 | if (ip + UINT_MAX == ip_to) | |
255 | return -IPSET_ERR_HASH_RANGE; | |
ca0f6a5c | 256 | } else { |
5d50e1d8 | 257 | ip_set_mask_from_to(ip, ip_to, e.cidr); |
ca0f6a5c | 258 | } |
aa67f109 JK |
259 | ipn = ip; |
260 | do { | |
261 | ipn = ip_set_range_to_cidr(ipn, ip_to, &e.cidr); | |
262 | n++; | |
263 | } while (ipn++ < ip_to); | |
264 | ||
265 | if (n > IPSET_MAX_RANGE) | |
266 | return -ERANGE; | |
e385357a JK |
267 | |
268 | if (retried) | |
6e27c9b4 | 269 | ip = ntohl(h->next.ip); |
0b8d9073 | 270 | do { |
5d50e1d8 | 271 | e.ip = htonl(ip); |
0b8d9073 | 272 | ip = ip_set_range_to_cidr(ip, ip_to, &e.cidr); |
5d50e1d8 | 273 | ret = adtfn(set, &e, &ext, &ext, flags); |
e385357a JK |
274 | |
275 | if (ret && !ip_set_eexist(ret, flags)) | |
276 | return ret; | |
ca0f6a5c JK |
277 | |
278 | ret = 0; | |
0b8d9073 | 279 | } while (ip++ < ip_to); |
e385357a JK |
280 | return ret; |
281 | } | |
282 | ||
03c8b234 | 283 | /* IPv6 variant */ |
e385357a | 284 | |
89dc79b7 JK |
285 | struct hash_netiface6_elem_hashed { |
286 | union nf_inet_addr ip; | |
287 | u8 physdev; | |
288 | u8 cidr; | |
2a7cef2a | 289 | u8 nomatch; |
bd9087e0 | 290 | u8 elem; |
89dc79b7 JK |
291 | }; |
292 | ||
e385357a JK |
293 | struct hash_netiface6_elem { |
294 | union nf_inet_addr ip; | |
e385357a JK |
295 | u8 physdev; |
296 | u8 cidr; | |
2a7cef2a | 297 | u8 nomatch; |
bd9087e0 | 298 | u8 elem; |
b6520fce | 299 | u8 wildcard; |
bd55389c | 300 | char iface[IFNAMSIZ]; |
e385357a JK |
301 | }; |
302 | ||
5d50e1d8 JK |
303 | /* Common functions */ |
304 | ||
8dea982a | 305 | static bool |
e385357a | 306 | hash_netiface6_data_equal(const struct hash_netiface6_elem *ip1, |
89dc79b7 JK |
307 | const struct hash_netiface6_elem *ip2, |
308 | u32 *multi) | |
e385357a | 309 | { |
29e3b160 | 310 | return ipv6_addr_equal(&ip1->ip.in6, &ip2->ip.in6) && |
e385357a | 311 | ip1->cidr == ip2->cidr && |
89dc79b7 | 312 | (++*multi) && |
e385357a | 313 | ip1->physdev == ip2->physdev && |
b6520fce KE |
314 | (ip1->wildcard ? |
315 | strncmp(ip1->iface, ip2->iface, strlen(ip1->iface)) == 0 : | |
316 | strcmp(ip1->iface, ip2->iface) == 0); | |
e385357a JK |
317 | } |
318 | ||
8dea982a | 319 | static int |
5d50e1d8 | 320 | hash_netiface6_do_data_match(const struct hash_netiface6_elem *elem) |
2a7cef2a | 321 | { |
3e0304a5 | 322 | return elem->nomatch ? -ENOTEMPTY : 1; |
2a7cef2a JK |
323 | } |
324 | ||
8dea982a | 325 | static void |
5d50e1d8 | 326 | hash_netiface6_data_set_flags(struct hash_netiface6_elem *elem, u32 flags) |
6eb4c7e9 | 327 | { |
5d50e1d8 | 328 | elem->nomatch = (flags >> 16) & IPSET_FLAG_NOMATCH; |
6eb4c7e9 JK |
329 | } |
330 | ||
8dea982a | 331 | static void |
5d50e1d8 | 332 | hash_netiface6_data_reset_flags(struct hash_netiface6_elem *elem, u8 *flags) |
e385357a | 333 | { |
5d50e1d8 | 334 | swap(*flags, elem->nomatch); |
e385357a JK |
335 | } |
336 | ||
8dea982a | 337 | static void |
e385357a JK |
338 | hash_netiface6_data_netmask(struct hash_netiface6_elem *elem, u8 cidr) |
339 | { | |
340 | ip6_netmask(&elem->ip, cidr); | |
341 | elem->cidr = cidr; | |
342 | } | |
343 | ||
344 | static bool | |
345 | hash_netiface6_data_list(struct sk_buff *skb, | |
346 | const struct hash_netiface6_elem *data) | |
347 | { | |
b6520fce KE |
348 | u32 flags = (data->physdev ? IPSET_FLAG_PHYSDEV : 0) | |
349 | (data->wildcard ? IPSET_FLAG_IFACE_WILDCARD : 0); | |
e385357a | 350 | |
2a7cef2a JK |
351 | if (data->nomatch) |
352 | flags |= IPSET_FLAG_NOMATCH; | |
7cf7899d DM |
353 | if (nla_put_ipaddr6(skb, IPSET_ATTR_IP, &data->ip.in6) || |
354 | nla_put_u8(skb, IPSET_ATTR_CIDR, data->cidr) || | |
355 | nla_put_string(skb, IPSET_ATTR_IFACE, data->iface) || | |
356 | (flags && | |
357 | nla_put_net32(skb, IPSET_ATTR_CADT_FLAGS, htonl(flags)))) | |
358 | goto nla_put_failure; | |
728a7e69 | 359 | return false; |
e385357a JK |
360 | |
361 | nla_put_failure: | |
728a7e69 | 362 | return true; |
e385357a JK |
363 | } |
364 | ||
8dea982a | 365 | static void |
21956ab2 | 366 | hash_netiface6_data_next(struct hash_netiface6_elem *next, |
5d50e1d8 | 367 | const struct hash_netiface6_elem *d) |
e385357a | 368 | { |
e385357a JK |
369 | } |
370 | ||
5d50e1d8 | 371 | #undef MTYPE |
e385357a JK |
372 | #undef HOST_MASK |
373 | ||
5d50e1d8 | 374 | #define MTYPE hash_netiface6 |
e385357a | 375 | #define HOST_MASK 128 |
5d50e1d8 JK |
376 | #define HKEY_DATALEN sizeof(struct hash_netiface6_elem_hashed) |
377 | #define IP_SET_EMIT_CREATE | |
378 | #include "ip_set_hash_gen.h" | |
e385357a JK |
379 | |
380 | static int | |
381 | hash_netiface6_kadt(struct ip_set *set, const struct sk_buff *skb, | |
382 | const struct xt_action_param *par, | |
5d50e1d8 | 383 | enum ipset_adt adt, struct ip_set_adt_opt *opt) |
e385357a | 384 | { |
21956ab2 | 385 | struct hash_netiface6 *h = set->data; |
e385357a | 386 | ipset_adtfn adtfn = set->variant->adt[adt]; |
5d50e1d8 | 387 | struct hash_netiface6_elem e = { |
f690cbae | 388 | .cidr = INIT_CIDR(h->nets[0].cidr[0], HOST_MASK), |
bd9087e0 | 389 | .elem = 1, |
e385357a | 390 | }; |
ca134ce8 | 391 | struct ip_set_ext ext = IP_SET_INIT_KEXT(skb, opt, set); |
e385357a | 392 | |
e385357a | 393 | if (adt == IPSET_TEST) |
5d50e1d8 | 394 | e.cidr = HOST_MASK; |
e385357a | 395 | |
5d50e1d8 JK |
396 | ip6addrptr(skb, opt->flags & IPSET_DIM_ONE_SRC, &e.ip.in6); |
397 | ip6_netmask(&e.ip, e.cidr); | |
e385357a JK |
398 | |
399 | if (opt->cmdflags & IPSET_FLAG_PHYSDEV) { | |
1109a90c | 400 | #if IS_ENABLED(CONFIG_BRIDGE_NETFILTER) |
bd55389c | 401 | const char *eiface = SRCDIR ? get_physindev_name(skb) : |
ca0f6a5c JK |
402 | get_physoutdev_name(skb); |
403 | ||
bd55389c | 404 | if (!eiface) |
e385357a | 405 | return -EINVAL; |
bd55389c | 406 | STRLCPY(e.iface, eiface); |
5d50e1d8 | 407 | e.physdev = 1; |
e385357a | 408 | #endif |
ca0f6a5c | 409 | } else { |
bd55389c | 410 | STRLCPY(e.iface, SRCDIR ? IFACE(in) : IFACE(out)); |
ca0f6a5c | 411 | } |
e385357a | 412 | |
bd55389c | 413 | if (strlen(e.iface) == 0) |
e385357a | 414 | return -EINVAL; |
e385357a | 415 | |
5d50e1d8 | 416 | return adtfn(set, &e, &ext, &opt->ext, opt->cmdflags); |
e385357a JK |
417 | } |
418 | ||
419 | static int | |
420 | hash_netiface6_uadt(struct ip_set *set, struct nlattr *tb[], | |
ca0f6a5c | 421 | enum ipset_adt adt, u32 *lineno, u32 flags, bool retried) |
e385357a | 422 | { |
e385357a | 423 | ipset_adtfn adtfn = set->variant->adt[adt]; |
5d50e1d8 | 424 | struct hash_netiface6_elem e = { .cidr = HOST_MASK, .elem = 1 }; |
ca134ce8 | 425 | struct ip_set_ext ext = IP_SET_INIT_UEXT(set); |
e385357a JK |
426 | int ret; |
427 | ||
a212e08e SP |
428 | if (tb[IPSET_ATTR_LINENO]) |
429 | *lineno = nla_get_u32(tb[IPSET_ATTR_LINENO]); | |
430 | ||
e385357a JK |
431 | if (unlikely(!tb[IPSET_ATTR_IP] || |
432 | !tb[IPSET_ATTR_IFACE] || | |
7dd37bc8 | 433 | !ip_set_optattr_netorder(tb, IPSET_ATTR_CADT_FLAGS))) |
e385357a JK |
434 | return -IPSET_ERR_PROTOCOL; |
435 | if (unlikely(tb[IPSET_ATTR_IP_TO])) | |
436 | return -IPSET_ERR_HASH_RANGE_UNSUPPORTED; | |
437 | ||
8e55d2e5 SP |
438 | ret = ip_set_get_ipaddr6(tb[IPSET_ATTR_IP], &e.ip); |
439 | if (ret) | |
440 | return ret; | |
441 | ||
442 | ret = ip_set_get_extensions(set, tb, &ext); | |
e385357a JK |
443 | if (ret) |
444 | return ret; | |
445 | ||
aff22758 | 446 | if (tb[IPSET_ATTR_CIDR]) { |
5d50e1d8 | 447 | e.cidr = nla_get_u8(tb[IPSET_ATTR_CIDR]); |
aff22758 SP |
448 | if (e.cidr > HOST_MASK) |
449 | return -IPSET_ERR_INVALID_CIDR; | |
450 | } | |
451 | ||
5d50e1d8 | 452 | ip6_netmask(&e.ip, e.cidr); |
e385357a | 453 | |
872f6903 | 454 | nla_strscpy(e.iface, tb[IPSET_ATTR_IFACE], IFNAMSIZ); |
e385357a JK |
455 | |
456 | if (tb[IPSET_ATTR_CADT_FLAGS]) { | |
15b4d93f | 457 | u32 cadt_flags = ip_set_get_h32(tb[IPSET_ATTR_CADT_FLAGS]); |
ca0f6a5c | 458 | |
15b4d93f | 459 | if (cadt_flags & IPSET_FLAG_PHYSDEV) |
5d50e1d8 | 460 | e.physdev = 1; |
43c56e59 JK |
461 | if (cadt_flags & IPSET_FLAG_NOMATCH) |
462 | flags |= (IPSET_FLAG_NOMATCH << 16); | |
b6520fce KE |
463 | if (cadt_flags & IPSET_FLAG_IFACE_WILDCARD) |
464 | e.wildcard = 1; | |
e385357a JK |
465 | } |
466 | ||
5d50e1d8 | 467 | ret = adtfn(set, &e, &ext, &ext, flags); |
e385357a | 468 | |
0f1799ba | 469 | return ip_set_enomatch(ret, flags, adt, set) ? -ret : |
43c56e59 | 470 | ip_set_eexist(ret, flags) ? 0 : ret; |
e385357a JK |
471 | } |
472 | ||
e385357a JK |
473 | static struct ip_set_type hash_netiface_type __read_mostly = { |
474 | .name = "hash:net,iface", | |
475 | .protocol = IPSET_PROTOCOL, | |
3e0304a5 JK |
476 | .features = IPSET_TYPE_IP | IPSET_TYPE_IFACE | |
477 | IPSET_TYPE_NOMATCH, | |
e385357a | 478 | .dimension = IPSET_DIM_TWO, |
c15f1c83 | 479 | .family = NFPROTO_UNSPEC, |
35b8dcf8 JK |
480 | .revision_min = IPSET_TYPE_REV_MIN, |
481 | .revision_max = IPSET_TYPE_REV_MAX, | |
ccf0a4b7 | 482 | .create_flags[IPSET_TYPE_REV_MAX] = IPSET_CREATE_FLAG_BUCKETSIZE, |
e385357a JK |
483 | .create = hash_netiface_create, |
484 | .create_policy = { | |
485 | [IPSET_ATTR_HASHSIZE] = { .type = NLA_U32 }, | |
486 | [IPSET_ATTR_MAXELEM] = { .type = NLA_U32 }, | |
3976ca10 | 487 | [IPSET_ATTR_INITVAL] = { .type = NLA_U32 }, |
ccf0a4b7 | 488 | [IPSET_ATTR_BUCKETSIZE] = { .type = NLA_U8 }, |
e385357a JK |
489 | [IPSET_ATTR_RESIZE] = { .type = NLA_U8 }, |
490 | [IPSET_ATTR_PROTO] = { .type = NLA_U8 }, | |
491 | [IPSET_ATTR_TIMEOUT] = { .type = NLA_U32 }, | |
5d50e1d8 | 492 | [IPSET_ATTR_CADT_FLAGS] = { .type = NLA_U32 }, |
e385357a JK |
493 | }, |
494 | .adt_policy = { | |
495 | [IPSET_ATTR_IP] = { .type = NLA_NESTED }, | |
496 | [IPSET_ATTR_IP_TO] = { .type = NLA_NESTED }, | |
497 | [IPSET_ATTR_IFACE] = { .type = NLA_NUL_STRING, | |
4a6dd664 | 498 | .len = IFNAMSIZ - 1 }, |
e385357a JK |
499 | [IPSET_ATTR_CADT_FLAGS] = { .type = NLA_U32 }, |
500 | [IPSET_ATTR_CIDR] = { .type = NLA_U8 }, | |
501 | [IPSET_ATTR_TIMEOUT] = { .type = NLA_U32 }, | |
502 | [IPSET_ATTR_LINENO] = { .type = NLA_U32 }, | |
00d71b27 JK |
503 | [IPSET_ATTR_BYTES] = { .type = NLA_U64 }, |
504 | [IPSET_ATTR_PACKETS] = { .type = NLA_U64 }, | |
03726186 SP |
505 | [IPSET_ATTR_COMMENT] = { .type = NLA_NUL_STRING, |
506 | .len = IPSET_MAX_COMMENT_SIZE }, | |
af331419 AD |
507 | [IPSET_ATTR_SKBMARK] = { .type = NLA_U64 }, |
508 | [IPSET_ATTR_SKBPRIO] = { .type = NLA_U32 }, | |
509 | [IPSET_ATTR_SKBQUEUE] = { .type = NLA_U16 }, | |
e385357a JK |
510 | }, |
511 | .me = THIS_MODULE, | |
512 | }; | |
513 | ||
514 | static int __init | |
515 | hash_netiface_init(void) | |
516 | { | |
517 | return ip_set_type_register(&hash_netiface_type); | |
518 | } | |
519 | ||
520 | static void __exit | |
521 | hash_netiface_fini(void) | |
522 | { | |
18f84d41 | 523 | rcu_barrier(); |
e385357a JK |
524 | ip_set_type_unregister(&hash_netiface_type); |
525 | } | |
526 | ||
527 | module_init(hash_netiface_init); | |
528 | module_exit(hash_netiface_fini); |