1 | // SPDX-License-Identifier: GPL-2.0 |
2 | /* |
3 | * IPv6 Address Label subsystem |
4 | * for the IPv6 "Default" Source Address Selection |
5 | * |
6 | * Copyright (C)2007 USAGI/WIDE Project |
7 | */ |
8 | /* |
9 | * Author: |
10 | * YOSHIFUJI Hideaki @ USAGI/WIDE Project <yoshfuji@linux-ipv6.org> |
11 | */ |
12 | |
13 | #include <linux/kernel.h> |
14 | #include <linux/list.h> |
15 | #include <linux/rcupdate.h> |
16 | #include <linux/in6.h> |
17 | #include <linux/slab.h> |
18 | #include <net/addrconf.h> |
19 | #include <linux/if_addrlabel.h> |
20 | #include <linux/netlink.h> |
21 | #include <linux/rtnetlink.h> |
22 | |
23 | #if 0 |
24 | #define ADDRLABEL(x...) printk(x) |
25 | #else |
26 | #define ADDRLABEL(x...) do { ; } while (0) |
27 | #endif |
28 | |
29 | /* |
30 | * Policy Table |
31 | */ |
32 | struct ip6addrlbl_entry { |
33 | struct in6_addr prefix; |
34 | int prefixlen; |
35 | int ifindex; |
36 | int addrtype; |
37 | u32 label; |
38 | struct hlist_node list; |
39 | struct rcu_head rcu; |
40 | }; |
41 | |
42 | /* |
43 | * Default policy table (RFC6724 + extensions) |
44 | * |
45 | * prefix addr_type label |
46 | * ------------------------------------------------------------------------- |
47 | * ::1/128 LOOPBACK 0 |
48 | * ::/0 N/A 1 |
49 | * 2002::/16 N/A 2 |
50 | * ::/96 COMPATv4 3 |
51 | * ::ffff:0:0/96 V4MAPPED 4 |
52 | * fc00::/7 N/A 5 ULA (RFC 4193) |
53 | * 2001::/32 N/A 6 Teredo (RFC 4380) |
54 | * 2001:10::/28 N/A 7 ORCHID (RFC 4843) |
55 | * fec0::/10 N/A 11 Site-local |
56 | * (deprecated by RFC3879) |
57 | * 3ffe::/16 N/A 12 6bone |
58 | * |
59 | * Note: 0xffffffff is used if we do not have any policies. |
60 | * Note: Labels for ULA and 6to4 are different from labels listed in RFC6724. |
61 | */ |
62 | |
63 | #define IPV6_ADDR_LABEL_DEFAULT 0xffffffffUL |
64 | |
65 | static const __net_initconst struct ip6addrlbl_init_table |
66 | { |
67 | const struct in6_addr *prefix; |
68 | int prefixlen; |
69 | u32 label; |
70 | } ip6addrlbl_init_table[] = { |
71 | { /* ::/0 */ |
72 | .prefix = &in6addr_any, |
73 | .label = 1, |
74 | }, { /* fc00::/7 */ |
75 | .prefix = &(struct in6_addr){ { { 0xfc } } } , |
76 | .prefixlen = 7, |
77 | .label = 5, |
78 | }, { /* fec0::/10 */ |
79 | .prefix = &(struct in6_addr){ { { 0xfe, 0xc0 } } }, |
80 | .prefixlen = 10, |
81 | .label = 11, |
82 | }, { /* 2002::/16 */ |
83 | .prefix = &(struct in6_addr){ { { 0x20, 0x02 } } }, |
84 | .prefixlen = 16, |
85 | .label = 2, |
86 | }, { /* 3ffe::/16 */ |
87 | .prefix = &(struct in6_addr){ { { 0x3f, 0xfe } } }, |
88 | .prefixlen = 16, |
89 | .label = 12, |
90 | }, { /* 2001::/32 */ |
91 | .prefix = &(struct in6_addr){ { { 0x20, 0x01 } } }, |
92 | .prefixlen = 32, |
93 | .label = 6, |
94 | }, { /* 2001:10::/28 */ |
95 | .prefix = &(struct in6_addr){ { { 0x20, 0x01, 0x00, 0x10 } } }, |
96 | .prefixlen = 28, |
97 | .label = 7, |
98 | }, { /* ::ffff:0:0 */ |
99 | .prefix = &(struct in6_addr){ { { [10] = 0xff, [11] = 0xff } } }, |
100 | .prefixlen = 96, |
101 | .label = 4, |
102 | }, { /* ::/96 */ |
103 | .prefix = &in6addr_any, |
104 | .prefixlen = 96, |
105 | .label = 3, |
106 | }, { /* ::1/128 */ |
107 | .prefix = &in6addr_loopback, |
108 | .prefixlen = 128, |
109 | .label = 0, |
110 | } |
111 | }; |
112 | |
113 | /* Find label */ |
114 | static bool __ip6addrlbl_match(const struct ip6addrlbl_entry *p, |
115 | const struct in6_addr *addr, |
116 | int addrtype, int ifindex) |
117 | { |
118 | if (p->ifindex && p->ifindex != ifindex) |
119 | return false; |
120 | if (p->addrtype && p->addrtype != addrtype) |
121 | return false; |
122 | if (!ipv6_prefix_equal(addr1: addr, addr2: &p->prefix, prefixlen: p->prefixlen)) |
123 | return false; |
124 | return true; |
125 | } |
126 | |
127 | static struct ip6addrlbl_entry *__ipv6_addr_label(struct net *net, |
128 | const struct in6_addr *addr, |
129 | int type, int ifindex) |
130 | { |
131 | struct ip6addrlbl_entry *p; |
132 | |
133 | hlist_for_each_entry_rcu(p, &net->ipv6.ip6addrlbl_table.head, list) { |
134 | if (__ip6addrlbl_match(p, addr, addrtype: type, ifindex)) |
135 | return p; |
136 | } |
137 | return NULL; |
138 | } |
139 | |
140 | u32 ipv6_addr_label(struct net *net, |
141 | const struct in6_addr *addr, int type, int ifindex) |
142 | { |
143 | u32 label; |
144 | struct ip6addrlbl_entry *p; |
145 | |
146 | type &= IPV6_ADDR_MAPPED | IPV6_ADDR_COMPATv4 | IPV6_ADDR_LOOPBACK; |
147 | |
148 | rcu_read_lock(); |
149 | p = __ipv6_addr_label(net, addr, type, ifindex); |
150 | label = p ? p->label : IPV6_ADDR_LABEL_DEFAULT; |
151 | rcu_read_unlock(); |
152 | |
153 | ADDRLABEL(KERN_DEBUG "%s(addr=%pI6, type=%d, ifindex=%d) => %08x\n" , |
154 | __func__, addr, type, ifindex, label); |
155 | |
156 | return label; |
157 | } |
158 | |
159 | /* allocate one entry */ |
160 | static struct ip6addrlbl_entry *ip6addrlbl_alloc(const struct in6_addr *prefix, |
161 | int prefixlen, int ifindex, |
162 | u32 label) |
163 | { |
164 | struct ip6addrlbl_entry *newp; |
165 | int addrtype; |
166 | |
167 | ADDRLABEL(KERN_DEBUG "%s(prefix=%pI6, prefixlen=%d, ifindex=%d, label=%u)\n" , |
168 | __func__, prefix, prefixlen, ifindex, (unsigned int)label); |
169 | |
170 | addrtype = ipv6_addr_type(addr: prefix) & (IPV6_ADDR_MAPPED | IPV6_ADDR_COMPATv4 | IPV6_ADDR_LOOPBACK); |
171 | |
172 | switch (addrtype) { |
173 | case IPV6_ADDR_MAPPED: |
174 | if (prefixlen > 96) |
175 | return ERR_PTR(error: -EINVAL); |
176 | if (prefixlen < 96) |
177 | addrtype = 0; |
178 | break; |
179 | case IPV6_ADDR_COMPATv4: |
180 | if (prefixlen != 96) |
181 | addrtype = 0; |
182 | break; |
183 | case IPV6_ADDR_LOOPBACK: |
184 | if (prefixlen != 128) |
185 | addrtype = 0; |
186 | break; |
187 | } |
188 | |
189 | newp = kmalloc(size: sizeof(*newp), GFP_KERNEL); |
190 | if (!newp) |
191 | return ERR_PTR(error: -ENOMEM); |
192 | |
193 | ipv6_addr_prefix(pfx: &newp->prefix, addr: prefix, plen: prefixlen); |
194 | newp->prefixlen = prefixlen; |
195 | newp->ifindex = ifindex; |
196 | newp->addrtype = addrtype; |
197 | newp->label = label; |
198 | INIT_HLIST_NODE(h: &newp->list); |
199 | return newp; |
200 | } |
201 | |
202 | /* add a label */ |
203 | static int __ip6addrlbl_add(struct net *net, struct ip6addrlbl_entry *newp, |
204 | int replace) |
205 | { |
206 | struct ip6addrlbl_entry *last = NULL, *p = NULL; |
207 | struct hlist_node *n; |
208 | int ret = 0; |
209 | |
210 | ADDRLABEL(KERN_DEBUG "%s(newp=%p, replace=%d)\n" , __func__, newp, |
211 | replace); |
212 | |
213 | hlist_for_each_entry_safe(p, n, &net->ipv6.ip6addrlbl_table.head, list) { |
214 | if (p->prefixlen == newp->prefixlen && |
215 | p->ifindex == newp->ifindex && |
216 | ipv6_addr_equal(a1: &p->prefix, a2: &newp->prefix)) { |
217 | if (!replace) { |
218 | ret = -EEXIST; |
219 | goto out; |
220 | } |
221 | hlist_replace_rcu(old: &p->list, new: &newp->list); |
222 | kfree_rcu(p, rcu); |
223 | goto out; |
224 | } else if ((p->prefixlen == newp->prefixlen && !p->ifindex) || |
225 | (p->prefixlen < newp->prefixlen)) { |
226 | hlist_add_before_rcu(n: &newp->list, next: &p->list); |
227 | goto out; |
228 | } |
229 | last = p; |
230 | } |
231 | if (last) |
232 | hlist_add_behind_rcu(n: &newp->list, prev: &last->list); |
233 | else |
234 | hlist_add_head_rcu(n: &newp->list, h: &net->ipv6.ip6addrlbl_table.head); |
235 | out: |
236 | if (!ret) |
237 | net->ipv6.ip6addrlbl_table.seq++; |
238 | return ret; |
239 | } |
240 | |
241 | /* add a label */ |
242 | static int ip6addrlbl_add(struct net *net, |
243 | const struct in6_addr *prefix, int prefixlen, |
244 | int ifindex, u32 label, int replace) |
245 | { |
246 | struct ip6addrlbl_entry *newp; |
247 | int ret = 0; |
248 | |
249 | ADDRLABEL(KERN_DEBUG "%s(prefix=%pI6, prefixlen=%d, ifindex=%d, label=%u, replace=%d)\n" , |
250 | __func__, prefix, prefixlen, ifindex, (unsigned int)label, |
251 | replace); |
252 | |
253 | newp = ip6addrlbl_alloc(prefix, prefixlen, ifindex, label); |
254 | if (IS_ERR(ptr: newp)) |
255 | return PTR_ERR(ptr: newp); |
256 | spin_lock(lock: &net->ipv6.ip6addrlbl_table.lock); |
257 | ret = __ip6addrlbl_add(net, newp, replace); |
258 | spin_unlock(lock: &net->ipv6.ip6addrlbl_table.lock); |
259 | if (ret) |
260 | kfree(objp: newp); |
261 | return ret; |
262 | } |
263 | |
264 | /* remove a label */ |
265 | static int __ip6addrlbl_del(struct net *net, |
266 | const struct in6_addr *prefix, int prefixlen, |
267 | int ifindex) |
268 | { |
269 | struct ip6addrlbl_entry *p = NULL; |
270 | struct hlist_node *n; |
271 | int ret = -ESRCH; |
272 | |
273 | ADDRLABEL(KERN_DEBUG "%s(prefix=%pI6, prefixlen=%d, ifindex=%d)\n" , |
274 | __func__, prefix, prefixlen, ifindex); |
275 | |
276 | hlist_for_each_entry_safe(p, n, &net->ipv6.ip6addrlbl_table.head, list) { |
277 | if (p->prefixlen == prefixlen && |
278 | p->ifindex == ifindex && |
279 | ipv6_addr_equal(a1: &p->prefix, a2: prefix)) { |
280 | hlist_del_rcu(n: &p->list); |
281 | kfree_rcu(p, rcu); |
282 | ret = 0; |
283 | break; |
284 | } |
285 | } |
286 | return ret; |
287 | } |
288 | |
289 | static int ip6addrlbl_del(struct net *net, |
290 | const struct in6_addr *prefix, int prefixlen, |
291 | int ifindex) |
292 | { |
293 | struct in6_addr prefix_buf; |
294 | int ret; |
295 | |
296 | ADDRLABEL(KERN_DEBUG "%s(prefix=%pI6, prefixlen=%d, ifindex=%d)\n" , |
297 | __func__, prefix, prefixlen, ifindex); |
298 | |
299 | ipv6_addr_prefix(pfx: &prefix_buf, addr: prefix, plen: prefixlen); |
300 | spin_lock(lock: &net->ipv6.ip6addrlbl_table.lock); |
301 | ret = __ip6addrlbl_del(net, prefix: &prefix_buf, prefixlen, ifindex); |
302 | spin_unlock(lock: &net->ipv6.ip6addrlbl_table.lock); |
303 | return ret; |
304 | } |
305 | |
306 | /* add default label */ |
307 | static int __net_init ip6addrlbl_net_init(struct net *net) |
308 | { |
309 | struct ip6addrlbl_entry *p = NULL; |
310 | struct hlist_node *n; |
311 | int err; |
312 | int i; |
313 | |
314 | ADDRLABEL(KERN_DEBUG "%s\n" , __func__); |
315 | |
316 | spin_lock_init(&net->ipv6.ip6addrlbl_table.lock); |
317 | INIT_HLIST_HEAD(&net->ipv6.ip6addrlbl_table.head); |
318 | |
319 | for (i = 0; i < ARRAY_SIZE(ip6addrlbl_init_table); i++) { |
320 | err = ip6addrlbl_add(net, |
321 | prefix: ip6addrlbl_init_table[i].prefix, |
322 | prefixlen: ip6addrlbl_init_table[i].prefixlen, |
323 | ifindex: 0, |
324 | label: ip6addrlbl_init_table[i].label, replace: 0); |
325 | if (err) |
326 | goto err_ip6addrlbl_add; |
327 | } |
328 | return 0; |
329 | |
330 | err_ip6addrlbl_add: |
331 | hlist_for_each_entry_safe(p, n, &net->ipv6.ip6addrlbl_table.head, list) { |
332 | hlist_del_rcu(n: &p->list); |
333 | kfree_rcu(p, rcu); |
334 | } |
335 | return err; |
336 | } |
337 | |
338 | static void __net_exit ip6addrlbl_net_exit(struct net *net) |
339 | { |
340 | struct ip6addrlbl_entry *p = NULL; |
341 | struct hlist_node *n; |
342 | |
343 | /* Remove all labels belonging to the exiting net */ |
344 | spin_lock(lock: &net->ipv6.ip6addrlbl_table.lock); |
345 | hlist_for_each_entry_safe(p, n, &net->ipv6.ip6addrlbl_table.head, list) { |
346 | hlist_del_rcu(n: &p->list); |
347 | kfree_rcu(p, rcu); |
348 | } |
349 | spin_unlock(lock: &net->ipv6.ip6addrlbl_table.lock); |
350 | } |
351 | |
352 | static struct pernet_operations ipv6_addr_label_ops = { |
353 | .init = ip6addrlbl_net_init, |
354 | .exit = ip6addrlbl_net_exit, |
355 | }; |
356 | |
357 | int __init ipv6_addr_label_init(void) |
358 | { |
359 | return register_pernet_subsys(&ipv6_addr_label_ops); |
360 | } |
361 | |
362 | void ipv6_addr_label_cleanup(void) |
363 | { |
364 | unregister_pernet_subsys(&ipv6_addr_label_ops); |
365 | } |
366 | |
367 | static const struct nla_policy ifal_policy[IFAL_MAX+1] = { |
368 | [IFAL_ADDRESS] = { .len = sizeof(struct in6_addr), }, |
369 | [IFAL_LABEL] = { .len = sizeof(u32), }, |
370 | }; |
371 | |
372 | static bool addrlbl_ifindex_exists(struct net *net, int ifindex) |
373 | { |
374 | |
375 | struct net_device *dev; |
376 | |
377 | rcu_read_lock(); |
378 | dev = dev_get_by_index_rcu(net, ifindex); |
379 | rcu_read_unlock(); |
380 | |
381 | return dev != NULL; |
382 | } |
383 | |
384 | static int ip6addrlbl_newdel(struct sk_buff *skb, struct nlmsghdr *nlh, |
385 | struct netlink_ext_ack *extack) |
386 | { |
387 | struct net *net = sock_net(sk: skb->sk); |
388 | struct ifaddrlblmsg *ifal; |
389 | struct nlattr *tb[IFAL_MAX+1]; |
390 | struct in6_addr *pfx; |
391 | u32 label; |
392 | int err = 0; |
393 | |
394 | err = nlmsg_parse_deprecated(nlh, hdrlen: sizeof(*ifal), tb, IFAL_MAX, |
395 | policy: ifal_policy, extack); |
396 | if (err < 0) |
397 | return err; |
398 | |
399 | ifal = nlmsg_data(nlh); |
400 | |
401 | if (ifal->ifal_family != AF_INET6 || |
402 | ifal->ifal_prefixlen > 128) |
403 | return -EINVAL; |
404 | |
405 | if (!tb[IFAL_ADDRESS]) |
406 | return -EINVAL; |
407 | pfx = nla_data(nla: tb[IFAL_ADDRESS]); |
408 | |
409 | if (!tb[IFAL_LABEL]) |
410 | return -EINVAL; |
411 | label = nla_get_u32(nla: tb[IFAL_LABEL]); |
412 | if (label == IPV6_ADDR_LABEL_DEFAULT) |
413 | return -EINVAL; |
414 | |
415 | switch (nlh->nlmsg_type) { |
416 | case RTM_NEWADDRLABEL: |
417 | if (ifal->ifal_index && |
418 | !addrlbl_ifindex_exists(net, ifindex: ifal->ifal_index)) |
419 | return -EINVAL; |
420 | |
421 | err = ip6addrlbl_add(net, prefix: pfx, prefixlen: ifal->ifal_prefixlen, |
422 | ifindex: ifal->ifal_index, label, |
423 | replace: nlh->nlmsg_flags & NLM_F_REPLACE); |
424 | break; |
425 | case RTM_DELADDRLABEL: |
426 | err = ip6addrlbl_del(net, prefix: pfx, prefixlen: ifal->ifal_prefixlen, |
427 | ifindex: ifal->ifal_index); |
428 | break; |
429 | default: |
430 | err = -EOPNOTSUPP; |
431 | } |
432 | return err; |
433 | } |
434 | |
435 | static void ip6addrlbl_putmsg(struct nlmsghdr *nlh, |
436 | int prefixlen, int ifindex, u32 lseq) |
437 | { |
438 | struct ifaddrlblmsg *ifal = nlmsg_data(nlh); |
439 | ifal->ifal_family = AF_INET6; |
440 | ifal->__ifal_reserved = 0; |
441 | ifal->ifal_prefixlen = prefixlen; |
442 | ifal->ifal_flags = 0; |
443 | ifal->ifal_index = ifindex; |
444 | ifal->ifal_seq = lseq; |
445 | }; |
446 | |
447 | static int ip6addrlbl_fill(struct sk_buff *skb, |
448 | struct ip6addrlbl_entry *p, |
449 | u32 lseq, |
450 | u32 portid, u32 seq, int event, |
451 | unsigned int flags) |
452 | { |
453 | struct nlmsghdr *nlh = nlmsg_put(skb, portid, seq, type: event, |
454 | payload: sizeof(struct ifaddrlblmsg), flags); |
455 | if (!nlh) |
456 | return -EMSGSIZE; |
457 | |
458 | ip6addrlbl_putmsg(nlh, prefixlen: p->prefixlen, ifindex: p->ifindex, lseq); |
459 | |
460 | if (nla_put_in6_addr(skb, attrtype: IFAL_ADDRESS, addr: &p->prefix) < 0 || |
461 | nla_put_u32(skb, attrtype: IFAL_LABEL, value: p->label) < 0) { |
462 | nlmsg_cancel(skb, nlh); |
463 | return -EMSGSIZE; |
464 | } |
465 | |
466 | nlmsg_end(skb, nlh); |
467 | return 0; |
468 | } |
469 | |
470 | static int ip6addrlbl_valid_dump_req(const struct nlmsghdr *nlh, |
471 | struct netlink_ext_ack *extack) |
472 | { |
473 | struct ifaddrlblmsg *ifal; |
474 | |
475 | if (nlh->nlmsg_len < nlmsg_msg_size(payload: sizeof(*ifal))) { |
476 | NL_SET_ERR_MSG_MOD(extack, "Invalid header for address label dump request" ); |
477 | return -EINVAL; |
478 | } |
479 | |
480 | ifal = nlmsg_data(nlh); |
481 | if (ifal->__ifal_reserved || ifal->ifal_prefixlen || |
482 | ifal->ifal_flags || ifal->ifal_index || ifal->ifal_seq) { |
483 | NL_SET_ERR_MSG_MOD(extack, "Invalid values in header for address label dump request" ); |
484 | return -EINVAL; |
485 | } |
486 | |
487 | if (nlmsg_attrlen(nlh, hdrlen: sizeof(*ifal))) { |
488 | NL_SET_ERR_MSG_MOD(extack, "Invalid data after header for address label dump request" ); |
489 | return -EINVAL; |
490 | } |
491 | |
492 | return 0; |
493 | } |
494 | |
495 | static int ip6addrlbl_dump(struct sk_buff *skb, struct netlink_callback *cb) |
496 | { |
497 | const struct nlmsghdr *nlh = cb->nlh; |
498 | struct net *net = sock_net(sk: skb->sk); |
499 | struct ip6addrlbl_entry *p; |
500 | int idx = 0, s_idx = cb->args[0]; |
501 | int err; |
502 | |
503 | if (cb->strict_check) { |
504 | err = ip6addrlbl_valid_dump_req(nlh, extack: cb->extack); |
505 | if (err < 0) |
506 | return err; |
507 | } |
508 | |
509 | rcu_read_lock(); |
510 | hlist_for_each_entry_rcu(p, &net->ipv6.ip6addrlbl_table.head, list) { |
511 | if (idx >= s_idx) { |
512 | err = ip6addrlbl_fill(skb, p, |
513 | lseq: net->ipv6.ip6addrlbl_table.seq, |
514 | NETLINK_CB(cb->skb).portid, |
515 | seq: nlh->nlmsg_seq, |
516 | RTM_NEWADDRLABEL, |
517 | NLM_F_MULTI); |
518 | if (err < 0) |
519 | break; |
520 | } |
521 | idx++; |
522 | } |
523 | rcu_read_unlock(); |
524 | cb->args[0] = idx; |
525 | return skb->len; |
526 | } |
527 | |
528 | static inline int ip6addrlbl_msgsize(void) |
529 | { |
530 | return NLMSG_ALIGN(sizeof(struct ifaddrlblmsg)) |
531 | + nla_total_size(payload: 16) /* IFAL_ADDRESS */ |
532 | + nla_total_size(payload: 4); /* IFAL_LABEL */ |
533 | } |
534 | |
535 | static int ip6addrlbl_valid_get_req(struct sk_buff *skb, |
536 | const struct nlmsghdr *nlh, |
537 | struct nlattr **tb, |
538 | struct netlink_ext_ack *extack) |
539 | { |
540 | struct ifaddrlblmsg *ifal; |
541 | int i, err; |
542 | |
543 | if (nlh->nlmsg_len < nlmsg_msg_size(payload: sizeof(*ifal))) { |
544 | NL_SET_ERR_MSG_MOD(extack, "Invalid header for addrlabel get request" ); |
545 | return -EINVAL; |
546 | } |
547 | |
548 | if (!netlink_strict_get_check(skb)) |
549 | return nlmsg_parse_deprecated(nlh, hdrlen: sizeof(*ifal), tb, |
550 | IFAL_MAX, policy: ifal_policy, extack); |
551 | |
552 | ifal = nlmsg_data(nlh); |
553 | if (ifal->__ifal_reserved || ifal->ifal_flags || ifal->ifal_seq) { |
554 | NL_SET_ERR_MSG_MOD(extack, "Invalid values in header for addrlabel get request" ); |
555 | return -EINVAL; |
556 | } |
557 | |
558 | err = nlmsg_parse_deprecated_strict(nlh, hdrlen: sizeof(*ifal), tb, IFAL_MAX, |
559 | policy: ifal_policy, extack); |
560 | if (err) |
561 | return err; |
562 | |
563 | for (i = 0; i <= IFAL_MAX; i++) { |
564 | if (!tb[i]) |
565 | continue; |
566 | |
567 | switch (i) { |
568 | case IFAL_ADDRESS: |
569 | break; |
570 | default: |
571 | NL_SET_ERR_MSG_MOD(extack, "Unsupported attribute in addrlabel get request" ); |
572 | return -EINVAL; |
573 | } |
574 | } |
575 | |
576 | return 0; |
577 | } |
578 | |
579 | static int ip6addrlbl_get(struct sk_buff *in_skb, struct nlmsghdr *nlh, |
580 | struct netlink_ext_ack *extack) |
581 | { |
582 | struct net *net = sock_net(sk: in_skb->sk); |
583 | struct ifaddrlblmsg *ifal; |
584 | struct nlattr *tb[IFAL_MAX+1]; |
585 | struct in6_addr *addr; |
586 | u32 lseq; |
587 | int err = 0; |
588 | struct ip6addrlbl_entry *p; |
589 | struct sk_buff *skb; |
590 | |
591 | err = ip6addrlbl_valid_get_req(skb: in_skb, nlh, tb, extack); |
592 | if (err < 0) |
593 | return err; |
594 | |
595 | ifal = nlmsg_data(nlh); |
596 | |
597 | if (ifal->ifal_family != AF_INET6 || |
598 | ifal->ifal_prefixlen != 128) |
599 | return -EINVAL; |
600 | |
601 | if (ifal->ifal_index && |
602 | !addrlbl_ifindex_exists(net, ifindex: ifal->ifal_index)) |
603 | return -EINVAL; |
604 | |
605 | if (!tb[IFAL_ADDRESS]) |
606 | return -EINVAL; |
607 | addr = nla_data(nla: tb[IFAL_ADDRESS]); |
608 | |
609 | skb = nlmsg_new(payload: ip6addrlbl_msgsize(), GFP_KERNEL); |
610 | if (!skb) |
611 | return -ENOBUFS; |
612 | |
613 | err = -ESRCH; |
614 | |
615 | rcu_read_lock(); |
616 | p = __ipv6_addr_label(net, addr, type: ipv6_addr_type(addr), ifindex: ifal->ifal_index); |
617 | lseq = net->ipv6.ip6addrlbl_table.seq; |
618 | if (p) |
619 | err = ip6addrlbl_fill(skb, p, lseq, |
620 | NETLINK_CB(in_skb).portid, |
621 | seq: nlh->nlmsg_seq, |
622 | RTM_NEWADDRLABEL, flags: 0); |
623 | rcu_read_unlock(); |
624 | |
625 | if (err < 0) { |
626 | WARN_ON(err == -EMSGSIZE); |
627 | kfree_skb(skb); |
628 | } else { |
629 | err = rtnl_unicast(skb, net, NETLINK_CB(in_skb).portid); |
630 | } |
631 | return err; |
632 | } |
633 | |
634 | int __init ipv6_addr_label_rtnl_register(void) |
635 | { |
636 | int ret; |
637 | |
638 | ret = rtnl_register_module(THIS_MODULE, PF_INET6, RTM_NEWADDRLABEL, |
639 | ip6addrlbl_newdel, |
640 | NULL, flags: RTNL_FLAG_DOIT_UNLOCKED); |
641 | if (ret < 0) |
642 | return ret; |
643 | ret = rtnl_register_module(THIS_MODULE, PF_INET6, RTM_DELADDRLABEL, |
644 | ip6addrlbl_newdel, |
645 | NULL, flags: RTNL_FLAG_DOIT_UNLOCKED); |
646 | if (ret < 0) |
647 | return ret; |
648 | ret = rtnl_register_module(THIS_MODULE, PF_INET6, RTM_GETADDRLABEL, |
649 | ip6addrlbl_get, |
650 | ip6addrlbl_dump, flags: RTNL_FLAG_DOIT_UNLOCKED); |
651 | return ret; |
652 | } |
653 | |