Branch data Line data Source code
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 : 3 : static bool __ip6addrlbl_match(const struct ip6addrlbl_entry *p,
115 : : const struct in6_addr *addr,
116 : : int addrtype, int ifindex)
117 : : {
118 : 3 : if (p->ifindex && p->ifindex != ifindex)
119 : : return false;
120 : 3 : if (p->addrtype && p->addrtype != addrtype)
121 : : return false;
122 : 3 : if (!ipv6_prefix_equal(addr, &p->prefix, p->prefixlen))
123 : : return false;
124 : 3 : return true;
125 : : }
126 : :
127 : 3 : 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 : 3 : hlist_for_each_entry_rcu(p, &net->ipv6.ip6addrlbl_table.head, list) {
134 : 3 : if (__ip6addrlbl_match(p, addr, type, ifindex))
135 : 3 : return p;
136 : : }
137 : : return NULL;
138 : : }
139 : :
140 : 3 : 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 : 3 : type &= IPV6_ADDR_MAPPED | IPV6_ADDR_COMPATv4 | IPV6_ADDR_LOOPBACK;
147 : :
148 : : rcu_read_lock();
149 : 3 : p = __ipv6_addr_label(net, addr, type, ifindex);
150 : 3 : 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 : 3 : return label;
157 : : }
158 : :
159 : : /* allocate one entry */
160 : 3 : 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 : 3 : addrtype = ipv6_addr_type(prefix) & (IPV6_ADDR_MAPPED | IPV6_ADDR_COMPATv4 | IPV6_ADDR_LOOPBACK);
171 : :
172 : 3 : switch (addrtype) {
173 : : case IPV6_ADDR_MAPPED:
174 : 3 : if (prefixlen > 96)
175 : : return ERR_PTR(-EINVAL);
176 : 3 : if (prefixlen < 96)
177 : : addrtype = 0;
178 : : break;
179 : : case IPV6_ADDR_COMPATv4:
180 : 0 : if (prefixlen != 96)
181 : : addrtype = 0;
182 : : break;
183 : : case IPV6_ADDR_LOOPBACK:
184 : 3 : if (prefixlen != 128)
185 : : addrtype = 0;
186 : : break;
187 : : }
188 : :
189 : : newp = kmalloc(sizeof(*newp), GFP_KERNEL);
190 : 3 : if (!newp)
191 : : return ERR_PTR(-ENOMEM);
192 : :
193 : 3 : ipv6_addr_prefix(&newp->prefix, prefix, prefixlen);
194 : 3 : newp->prefixlen = prefixlen;
195 : 3 : newp->ifindex = ifindex;
196 : 3 : newp->addrtype = addrtype;
197 : 3 : newp->label = label;
198 : : INIT_HLIST_NODE(&newp->list);
199 : 3 : return newp;
200 : : }
201 : :
202 : : /* add a label */
203 : 3 : 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 : 3 : hlist_for_each_entry_safe(p, n, &net->ipv6.ip6addrlbl_table.head, list) {
214 : 3 : if (p->prefixlen == newp->prefixlen &&
215 : 3 : p->ifindex == newp->ifindex &&
216 : : ipv6_addr_equal(&p->prefix, &newp->prefix)) {
217 : 0 : if (!replace) {
218 : : ret = -EEXIST;
219 : : goto out;
220 : : }
221 : 0 : hlist_replace_rcu(&p->list, &newp->list);
222 : 0 : kfree_rcu(p, rcu);
223 : : goto out;
224 : 3 : } else if ((p->prefixlen == newp->prefixlen && !p->ifindex) ||
225 : : (p->prefixlen < newp->prefixlen)) {
226 : 3 : hlist_add_before_rcu(&newp->list, &p->list);
227 : : goto out;
228 : : }
229 : : last = p;
230 : : }
231 : 3 : if (last)
232 : 0 : hlist_add_behind_rcu(&newp->list, &last->list);
233 : : else
234 : 3 : hlist_add_head_rcu(&newp->list, &net->ipv6.ip6addrlbl_table.head);
235 : : out:
236 : 3 : if (!ret)
237 : 3 : net->ipv6.ip6addrlbl_table.seq++;
238 : 3 : return ret;
239 : : }
240 : :
241 : : /* add a label */
242 : 3 : 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 : 3 : newp = ip6addrlbl_alloc(prefix, prefixlen, ifindex, label);
254 : 3 : if (IS_ERR(newp))
255 : 0 : return PTR_ERR(newp);
256 : : spin_lock(&net->ipv6.ip6addrlbl_table.lock);
257 : 3 : ret = __ip6addrlbl_add(net, newp, replace);
258 : : spin_unlock(&net->ipv6.ip6addrlbl_table.lock);
259 : 3 : if (ret)
260 : 0 : kfree(newp);
261 : 3 : return ret;
262 : : }
263 : :
264 : : /* remove a label */
265 : 0 : 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 : 0 : hlist_for_each_entry_safe(p, n, &net->ipv6.ip6addrlbl_table.head, list) {
277 : 0 : if (p->prefixlen == prefixlen &&
278 : 0 : p->ifindex == ifindex &&
279 : : ipv6_addr_equal(&p->prefix, prefix)) {
280 : : hlist_del_rcu(&p->list);
281 : 0 : kfree_rcu(p, rcu);
282 : : ret = 0;
283 : : break;
284 : : }
285 : : }
286 : 0 : return ret;
287 : : }
288 : :
289 : 0 : 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 : 0 : ipv6_addr_prefix(&prefix_buf, prefix, prefixlen);
300 : : spin_lock(&net->ipv6.ip6addrlbl_table.lock);
301 : 0 : ret = __ip6addrlbl_del(net, &prefix_buf, prefixlen, ifindex);
302 : : spin_unlock(&net->ipv6.ip6addrlbl_table.lock);
303 : 0 : return ret;
304 : : }
305 : :
306 : : /* add default label */
307 : 3 : static int __net_init ip6addrlbl_net_init(struct net *net)
308 : : {
309 : : int err = 0;
310 : : int i;
311 : :
312 : : ADDRLABEL(KERN_DEBUG "%s\n", __func__);
313 : :
314 : 3 : spin_lock_init(&net->ipv6.ip6addrlbl_table.lock);
315 : 3 : INIT_HLIST_HEAD(&net->ipv6.ip6addrlbl_table.head);
316 : :
317 : 3 : for (i = 0; i < ARRAY_SIZE(ip6addrlbl_init_table); i++) {
318 : 3 : int ret = ip6addrlbl_add(net,
319 : : ip6addrlbl_init_table[i].prefix,
320 : : ip6addrlbl_init_table[i].prefixlen,
321 : : 0,
322 : : ip6addrlbl_init_table[i].label, 0);
323 : : /* XXX: should we free all rules when we catch an error? */
324 : 3 : if (ret && (!err || err != -ENOMEM))
325 : : err = ret;
326 : : }
327 : 3 : return err;
328 : : }
329 : :
330 : 1 : static void __net_exit ip6addrlbl_net_exit(struct net *net)
331 : : {
332 : : struct ip6addrlbl_entry *p = NULL;
333 : : struct hlist_node *n;
334 : :
335 : : /* Remove all labels belonging to the exiting net */
336 : : spin_lock(&net->ipv6.ip6addrlbl_table.lock);
337 : 1 : hlist_for_each_entry_safe(p, n, &net->ipv6.ip6addrlbl_table.head, list) {
338 : : hlist_del_rcu(&p->list);
339 : 1 : kfree_rcu(p, rcu);
340 : : }
341 : : spin_unlock(&net->ipv6.ip6addrlbl_table.lock);
342 : 1 : }
343 : :
344 : : static struct pernet_operations ipv6_addr_label_ops = {
345 : : .init = ip6addrlbl_net_init,
346 : : .exit = ip6addrlbl_net_exit,
347 : : };
348 : :
349 : 3 : int __init ipv6_addr_label_init(void)
350 : : {
351 : 3 : return register_pernet_subsys(&ipv6_addr_label_ops);
352 : : }
353 : :
354 : 0 : void ipv6_addr_label_cleanup(void)
355 : : {
356 : 0 : unregister_pernet_subsys(&ipv6_addr_label_ops);
357 : 0 : }
358 : :
359 : : static const struct nla_policy ifal_policy[IFAL_MAX+1] = {
360 : : [IFAL_ADDRESS] = { .len = sizeof(struct in6_addr), },
361 : : [IFAL_LABEL] = { .len = sizeof(u32), },
362 : : };
363 : :
364 : : static bool addrlbl_ifindex_exists(struct net *net, int ifindex)
365 : : {
366 : :
367 : : struct net_device *dev;
368 : :
369 : : rcu_read_lock();
370 : 0 : dev = dev_get_by_index_rcu(net, ifindex);
371 : : rcu_read_unlock();
372 : :
373 : : return dev != NULL;
374 : : }
375 : :
376 : 0 : static int ip6addrlbl_newdel(struct sk_buff *skb, struct nlmsghdr *nlh,
377 : : struct netlink_ext_ack *extack)
378 : : {
379 : 0 : struct net *net = sock_net(skb->sk);
380 : : struct ifaddrlblmsg *ifal;
381 : : struct nlattr *tb[IFAL_MAX+1];
382 : : struct in6_addr *pfx;
383 : : u32 label;
384 : : int err = 0;
385 : :
386 : : err = nlmsg_parse_deprecated(nlh, sizeof(*ifal), tb, IFAL_MAX,
387 : : ifal_policy, extack);
388 : 0 : if (err < 0)
389 : : return err;
390 : :
391 : : ifal = nlmsg_data(nlh);
392 : :
393 : 0 : if (ifal->ifal_family != AF_INET6 ||
394 : 0 : ifal->ifal_prefixlen > 128)
395 : : return -EINVAL;
396 : :
397 : 0 : if (!tb[IFAL_ADDRESS])
398 : : return -EINVAL;
399 : : pfx = nla_data(tb[IFAL_ADDRESS]);
400 : :
401 : 0 : if (!tb[IFAL_LABEL])
402 : : return -EINVAL;
403 : : label = nla_get_u32(tb[IFAL_LABEL]);
404 : 0 : if (label == IPV6_ADDR_LABEL_DEFAULT)
405 : : return -EINVAL;
406 : :
407 : 0 : switch (nlh->nlmsg_type) {
408 : : case RTM_NEWADDRLABEL:
409 : 0 : if (ifal->ifal_index &&
410 : 0 : !addrlbl_ifindex_exists(net, ifal->ifal_index))
411 : : return -EINVAL;
412 : :
413 : 0 : err = ip6addrlbl_add(net, pfx, ifal->ifal_prefixlen,
414 : 0 : ifal->ifal_index, label,
415 : 0 : nlh->nlmsg_flags & NLM_F_REPLACE);
416 : 0 : break;
417 : : case RTM_DELADDRLABEL:
418 : 0 : err = ip6addrlbl_del(net, pfx, ifal->ifal_prefixlen,
419 : 0 : ifal->ifal_index);
420 : 0 : break;
421 : : default:
422 : : err = -EOPNOTSUPP;
423 : : }
424 : 0 : return err;
425 : : }
426 : :
427 : : static void ip6addrlbl_putmsg(struct nlmsghdr *nlh,
428 : : int prefixlen, int ifindex, u32 lseq)
429 : : {
430 : : struct ifaddrlblmsg *ifal = nlmsg_data(nlh);
431 : 0 : ifal->ifal_family = AF_INET6;
432 : 0 : ifal->ifal_prefixlen = prefixlen;
433 : 0 : ifal->ifal_flags = 0;
434 : 0 : ifal->ifal_index = ifindex;
435 : 0 : ifal->ifal_seq = lseq;
436 : : };
437 : :
438 : 0 : static int ip6addrlbl_fill(struct sk_buff *skb,
439 : : struct ip6addrlbl_entry *p,
440 : : u32 lseq,
441 : : u32 portid, u32 seq, int event,
442 : : unsigned int flags)
443 : : {
444 : 0 : struct nlmsghdr *nlh = nlmsg_put(skb, portid, seq, event,
445 : : sizeof(struct ifaddrlblmsg), flags);
446 : 0 : if (!nlh)
447 : : return -EMSGSIZE;
448 : :
449 : 0 : ip6addrlbl_putmsg(nlh, p->prefixlen, p->ifindex, lseq);
450 : :
451 : 0 : if (nla_put_in6_addr(skb, IFAL_ADDRESS, &p->prefix) < 0 ||
452 : 0 : nla_put_u32(skb, IFAL_LABEL, p->label) < 0) {
453 : : nlmsg_cancel(skb, nlh);
454 : 0 : return -EMSGSIZE;
455 : : }
456 : :
457 : : nlmsg_end(skb, nlh);
458 : 0 : return 0;
459 : : }
460 : :
461 : 0 : static int ip6addrlbl_valid_dump_req(const struct nlmsghdr *nlh,
462 : : struct netlink_ext_ack *extack)
463 : : {
464 : : struct ifaddrlblmsg *ifal;
465 : :
466 : 0 : if (nlh->nlmsg_len < nlmsg_msg_size(sizeof(*ifal))) {
467 : 0 : NL_SET_ERR_MSG_MOD(extack, "Invalid header for address label dump request");
468 : : return -EINVAL;
469 : : }
470 : :
471 : : ifal = nlmsg_data(nlh);
472 : 0 : if (ifal->__ifal_reserved || ifal->ifal_prefixlen ||
473 : 0 : ifal->ifal_flags || ifal->ifal_index || ifal->ifal_seq) {
474 : 0 : NL_SET_ERR_MSG_MOD(extack, "Invalid values in header for address label dump request");
475 : : return -EINVAL;
476 : : }
477 : :
478 : 0 : if (nlmsg_attrlen(nlh, sizeof(*ifal))) {
479 : 0 : NL_SET_ERR_MSG_MOD(extack, "Invalid data after header for address label dump request");
480 : : return -EINVAL;
481 : : }
482 : :
483 : : return 0;
484 : : }
485 : :
486 : 0 : static int ip6addrlbl_dump(struct sk_buff *skb, struct netlink_callback *cb)
487 : : {
488 : 0 : const struct nlmsghdr *nlh = cb->nlh;
489 : 0 : struct net *net = sock_net(skb->sk);
490 : : struct ip6addrlbl_entry *p;
491 : 0 : int idx = 0, s_idx = cb->args[0];
492 : : int err;
493 : :
494 : 0 : if (cb->strict_check) {
495 : 0 : err = ip6addrlbl_valid_dump_req(nlh, cb->extack);
496 : 0 : if (err < 0)
497 : : return err;
498 : : }
499 : :
500 : : rcu_read_lock();
501 : 0 : hlist_for_each_entry_rcu(p, &net->ipv6.ip6addrlbl_table.head, list) {
502 : 0 : if (idx >= s_idx) {
503 : 0 : err = ip6addrlbl_fill(skb, p,
504 : : net->ipv6.ip6addrlbl_table.seq,
505 : 0 : NETLINK_CB(cb->skb).portid,
506 : : nlh->nlmsg_seq,
507 : : RTM_NEWADDRLABEL,
508 : : NLM_F_MULTI);
509 : 0 : if (err < 0)
510 : : break;
511 : : }
512 : 0 : idx++;
513 : : }
514 : : rcu_read_unlock();
515 : 0 : cb->args[0] = idx;
516 : 0 : return skb->len;
517 : : }
518 : :
519 : : static inline int ip6addrlbl_msgsize(void)
520 : : {
521 : : return NLMSG_ALIGN(sizeof(struct ifaddrlblmsg))
522 : : + nla_total_size(16) /* IFAL_ADDRESS */
523 : : + nla_total_size(4); /* IFAL_LABEL */
524 : : }
525 : :
526 : 0 : static int ip6addrlbl_valid_get_req(struct sk_buff *skb,
527 : : const struct nlmsghdr *nlh,
528 : : struct nlattr **tb,
529 : : struct netlink_ext_ack *extack)
530 : : {
531 : : struct ifaddrlblmsg *ifal;
532 : : int i, err;
533 : :
534 : 0 : if (nlh->nlmsg_len < nlmsg_msg_size(sizeof(*ifal))) {
535 : 0 : NL_SET_ERR_MSG_MOD(extack, "Invalid header for addrlabel get request");
536 : : return -EINVAL;
537 : : }
538 : :
539 : 0 : if (!netlink_strict_get_check(skb))
540 : 0 : return nlmsg_parse_deprecated(nlh, sizeof(*ifal), tb,
541 : : IFAL_MAX, ifal_policy, extack);
542 : :
543 : : ifal = nlmsg_data(nlh);
544 : 0 : if (ifal->__ifal_reserved || ifal->ifal_flags || ifal->ifal_seq) {
545 : 0 : NL_SET_ERR_MSG_MOD(extack, "Invalid values in header for addrlabel get request");
546 : : return -EINVAL;
547 : : }
548 : :
549 : : err = nlmsg_parse_deprecated_strict(nlh, sizeof(*ifal), tb, IFAL_MAX,
550 : : ifal_policy, extack);
551 : 0 : if (err)
552 : : return err;
553 : :
554 : 0 : for (i = 0; i <= IFAL_MAX; i++) {
555 : 0 : if (!tb[i])
556 : 0 : continue;
557 : :
558 : 0 : switch (i) {
559 : : case IFAL_ADDRESS:
560 : : break;
561 : : default:
562 : 0 : NL_SET_ERR_MSG_MOD(extack, "Unsupported attribute in addrlabel get request");
563 : : return -EINVAL;
564 : : }
565 : : }
566 : :
567 : : return 0;
568 : : }
569 : :
570 : 0 : static int ip6addrlbl_get(struct sk_buff *in_skb, struct nlmsghdr *nlh,
571 : : struct netlink_ext_ack *extack)
572 : : {
573 : 0 : struct net *net = sock_net(in_skb->sk);
574 : : struct ifaddrlblmsg *ifal;
575 : : struct nlattr *tb[IFAL_MAX+1];
576 : : struct in6_addr *addr;
577 : : u32 lseq;
578 : : int err = 0;
579 : : struct ip6addrlbl_entry *p;
580 : : struct sk_buff *skb;
581 : :
582 : 0 : err = ip6addrlbl_valid_get_req(in_skb, nlh, tb, extack);
583 : 0 : if (err < 0)
584 : : return err;
585 : :
586 : : ifal = nlmsg_data(nlh);
587 : :
588 : 0 : if (ifal->ifal_family != AF_INET6 ||
589 : : ifal->ifal_prefixlen != 128)
590 : : return -EINVAL;
591 : :
592 : 0 : if (ifal->ifal_index &&
593 : 0 : !addrlbl_ifindex_exists(net, ifal->ifal_index))
594 : : return -EINVAL;
595 : :
596 : 0 : if (!tb[IFAL_ADDRESS])
597 : : return -EINVAL;
598 : : addr = nla_data(tb[IFAL_ADDRESS]);
599 : :
600 : : skb = nlmsg_new(ip6addrlbl_msgsize(), GFP_KERNEL);
601 : 0 : if (!skb)
602 : : return -ENOBUFS;
603 : :
604 : : err = -ESRCH;
605 : :
606 : : rcu_read_lock();
607 : 0 : p = __ipv6_addr_label(net, addr, ipv6_addr_type(addr), ifal->ifal_index);
608 : 0 : lseq = net->ipv6.ip6addrlbl_table.seq;
609 : 0 : if (p)
610 : 0 : err = ip6addrlbl_fill(skb, p, lseq,
611 : : NETLINK_CB(in_skb).portid,
612 : : nlh->nlmsg_seq,
613 : : RTM_NEWADDRLABEL, 0);
614 : : rcu_read_unlock();
615 : :
616 : 0 : if (err < 0) {
617 : 0 : WARN_ON(err == -EMSGSIZE);
618 : 0 : kfree_skb(skb);
619 : : } else {
620 : 0 : err = rtnl_unicast(skb, net, NETLINK_CB(in_skb).portid);
621 : : }
622 : 0 : return err;
623 : : }
624 : :
625 : 3 : int __init ipv6_addr_label_rtnl_register(void)
626 : : {
627 : : int ret;
628 : :
629 : 3 : ret = rtnl_register_module(THIS_MODULE, PF_INET6, RTM_NEWADDRLABEL,
630 : : ip6addrlbl_newdel,
631 : : NULL, RTNL_FLAG_DOIT_UNLOCKED);
632 : 3 : if (ret < 0)
633 : : return ret;
634 : 3 : ret = rtnl_register_module(THIS_MODULE, PF_INET6, RTM_DELADDRLABEL,
635 : : ip6addrlbl_newdel,
636 : : NULL, RTNL_FLAG_DOIT_UNLOCKED);
637 : 3 : if (ret < 0)
638 : : return ret;
639 : 3 : ret = rtnl_register_module(THIS_MODULE, PF_INET6, RTM_GETADDRLABEL,
640 : : ip6addrlbl_get,
641 : : ip6addrlbl_dump, RTNL_FLAG_DOIT_UNLOCKED);
642 : 3 : return ret;
643 : : }
|