Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

[IPV6] ADDRCONF: Support RFC3484 configurable address selection policy table.

Policy table is implemented as an RCU linear list since we do not expect
large list nor frequent updates.

Signed-off-by: YOSHIFUJI Hideaki <yoshfuji@linux-ipv6.org>
Signed-off-by: David S. Miller <davem@davemloft.net>

authored by

YOSHIFUJI Hideaki and committed by
David S. Miller
2a8cc6c8 303065a8

+608 -31
+32
include/linux/if_addrlabel.h
··· 1 + /* 2 + * if_addrlabel.h - netlink interface for address labels 3 + * 4 + * Copyright (C)2007 USAGI/WIDE Project, All Rights Reserved. 5 + * 6 + * Authors: 7 + * YOSHIFUJI Hideaki @ USAGI/WIDE <yoshfuji@linux-ipv6.org> 8 + */ 9 + 10 + #ifndef __LINUX_IF_ADDRLABEL_H 11 + #define __LINUX_IF_ADDRLABEL_H 12 + 13 + struct ifaddrlblmsg 14 + { 15 + __u8 ifal_family; /* Address family */ 16 + __u8 __ifal_reserved; /* Reserved */ 17 + __u8 ifal_prefixlen; /* Prefix length */ 18 + __u8 ifal_flags; /* Flags */ 19 + __u32 ifal_index; /* Link index */ 20 + __u32 ifal_seq; /* sequence number */ 21 + }; 22 + 23 + enum 24 + { 25 + IFAL_ADDRESS = 1, 26 + IFAL_LABEL = 2, 27 + __IFAL_MAX 28 + }; 29 + 30 + #define IFAL_MAX (__IFAL_MAX - 1) 31 + 32 + #endif
+7
include/linux/rtnetlink.h
··· 100 100 RTM_NEWNDUSEROPT = 68, 101 101 #define RTM_NEWNDUSEROPT RTM_NEWNDUSEROPT 102 102 103 + RTM_NEWADDRLABEL = 72, 104 + #define RTM_NEWADDRLABEL RTM_NEWADDRLABEL 105 + RTM_DELADDRLABEL, 106 + #define RTM_NEWADDRLABEL RTM_NEWADDRLABEL 107 + RTM_GETADDRLABEL, 108 + #define RTM_GETADDRLABEL RTM_GETADDRLABEL 109 + 103 110 __RTM_MAX, 104 111 #define RTM_MAX (((__RTM_MAX + 3) & ~3) - 1) 105 112 };
+8
include/net/addrconf.h
··· 84 84 struct in6_addr *addr); 85 85 86 86 /* 87 + * IPv6 Address Label subsystem (addrlabel.c) 88 + */ 89 + extern int ipv6_addr_label_init(void); 90 + extern void ipv6_addr_label_rtnl_register(void); 91 + extern u32 ipv6_addr_label(const struct in6_addr *addr, 92 + int type, int ifindex); 93 + 94 + /* 87 95 * multicast prototypes (mcast.c) 88 96 */ 89 97 extern int ipv6_sock_mc_join(struct sock *sk, int ifindex,
+1
net/ipv6/Makefile
··· 5 5 obj-$(CONFIG_IPV6) += ipv6.o 6 6 7 7 ipv6-objs := af_inet6.o anycast.o ip6_output.o ip6_input.o addrconf.o \ 8 + addrlabel.o \ 8 9 route.o ip6_fib.o ipv6_sockglue.o ndisc.o udp.o udplite.o \ 9 10 raw.o protocol.o icmp.o mcast.o reassembly.o tcp_ipv6.o \ 10 11 exthdrs.o sysctl_net_ipv6.o datagram.o \
+9 -31
net/ipv6/addrconf.c
··· 874 874 return 0; 875 875 } 876 876 877 - /* static matching label */ 878 - static inline int ipv6_addr_label(const struct in6_addr *addr, int type, 879 - int ifindex) 880 - { 881 - /* 882 - * prefix (longest match) label 883 - * ----------------------------- 884 - * ::1/128 0 885 - * ::/0 1 886 - * 2002::/16 2 887 - * ::/96 3 888 - * ::ffff:0:0/96 4 889 - * fc00::/7 5 890 - * 2001::/32 6 891 - */ 892 - if (type & IPV6_ADDR_LOOPBACK) 893 - return 0; 894 - else if (type & IPV6_ADDR_COMPATv4) 895 - return 3; 896 - else if (type & IPV6_ADDR_MAPPED) 897 - return 4; 898 - else if (addr->s6_addr32[0] == htonl(0x20010000)) 899 - return 6; 900 - else if (addr->s6_addr16[0] == htons(0x2002)) 901 - return 2; 902 - else if ((addr->s6_addr[0] & 0xfe) == 0xfc) 903 - return 5; 904 - return 1; 905 - } 906 - 907 877 int ipv6_dev_get_saddr(struct net_device *daddr_dev, 908 878 struct in6_addr *daddr, struct in6_addr *saddr) 909 879 { ··· 4159 4189 4160 4190 int __init addrconf_init(void) 4161 4191 { 4162 - int err = 0; 4192 + int err; 4193 + 4194 + if ((err = ipv6_addr_label_init()) < 0) { 4195 + printk(KERN_CRIT "IPv6 Addrconf: cannot initialize default policy table: %d.\n", 4196 + err); 4197 + return err; 4198 + } 4163 4199 4164 4200 /* The addrconf netdev notifier requires that loopback_dev 4165 4201 * has it's ipv6 private information allocated and setup ··· 4215 4239 __rtnl_register(PF_INET6, RTM_GETADDR, inet6_rtm_getaddr, inet6_dump_ifaddr); 4216 4240 __rtnl_register(PF_INET6, RTM_GETMULTICAST, NULL, inet6_dump_ifmcaddr); 4217 4241 __rtnl_register(PF_INET6, RTM_GETANYCAST, NULL, inet6_dump_ifacaddr); 4242 + 4243 + ipv6_addr_label_rtnl_register(); 4218 4244 4219 4245 #ifdef CONFIG_SYSCTL 4220 4246 addrconf_sysctl.sysctl_header =
+551
net/ipv6/addrlabel.c
··· 1 + /* 2 + * IPv6 Address Label subsystem 3 + * for the IPv6 "Default" Source Address Selection 4 + * 5 + * Copyright (C)2007 USAGI/WIDE Project 6 + */ 7 + /* 8 + * Author: 9 + * YOSHIFUJI Hideaki @ USAGI/WIDE Project <yoshfuji@linux-ipv6.org> 10 + */ 11 + 12 + #include <linux/kernel.h> 13 + #include <linux/list.h> 14 + #include <linux/rcupdate.h> 15 + #include <linux/in6.h> 16 + #include <net/addrconf.h> 17 + #include <linux/if_addrlabel.h> 18 + #include <linux/netlink.h> 19 + #include <linux/rtnetlink.h> 20 + 21 + #if 0 22 + #define ADDRLABEL(x...) printk(x) 23 + #else 24 + #define ADDRLABEL(x...) do { ; } while(0) 25 + #endif 26 + 27 + /* 28 + * Policy Table 29 + */ 30 + struct ip6addrlbl_entry 31 + { 32 + struct in6_addr prefix; 33 + int prefixlen; 34 + int ifindex; 35 + int addrtype; 36 + u32 label; 37 + struct hlist_node list; 38 + atomic_t refcnt; 39 + struct rcu_head rcu; 40 + }; 41 + 42 + static struct ip6addrlbl_table 43 + { 44 + struct hlist_head head; 45 + spinlock_t lock; 46 + u32 seq; 47 + } ip6addrlbl_table; 48 + 49 + /* 50 + * Default policy table (RFC3484 + extensions) 51 + * 52 + * prefix addr_type label 53 + * ------------------------------------------------------------------------- 54 + * ::1/128 LOOPBACK 0 55 + * ::/0 N/A 1 56 + * 2002::/16 N/A 2 57 + * ::/96 COMPATv4 3 58 + * ::ffff:0:0/96 V4MAPPED 4 59 + * fc00::/7 N/A 5 ULA (RFC 4193) 60 + * 2001::/32 N/A 6 Teredo (RFC 4380) 61 + * 62 + * Note: 0xffffffff is used if we do not have any policies. 63 + */ 64 + 65 + #define IPV6_ADDR_LABEL_DEFAULT 0xffffffffUL 66 + 67 + static const __initdata struct ip6addrlbl_init_table 68 + { 69 + const struct in6_addr *prefix; 70 + int prefixlen; 71 + u32 label; 72 + } ip6addrlbl_init_table[] = { 73 + { /* ::/0 */ 74 + .prefix = &in6addr_any, 75 + .label = 1, 76 + },{ /* fc00::/7 */ 77 + .prefix = &(struct in6_addr){{{ 0xfc }}}, 78 + .prefixlen = 7, 79 + .label = 5, 80 + },{ /* 2002::/16 */ 81 + .prefix = &(struct in6_addr){{{ 0x20, 0x02 }}}, 82 + .prefixlen = 16, 83 + .label = 2, 84 + },{ /* 2001::/32 */ 85 + .prefix = &(struct in6_addr){{{ 0x20, 0x01 }}}, 86 + .prefixlen = 32, 87 + .label = 6, 88 + },{ /* ::ffff:0:0 */ 89 + .prefix = &(struct in6_addr){{{ [10] = 0xff, [11] = 0xff }}}, 90 + .prefixlen = 96, 91 + .label = 4, 92 + },{ /* ::/96 */ 93 + .prefix = &in6addr_any, 94 + .prefixlen = 96, 95 + .label = 3, 96 + },{ /* ::1/128 */ 97 + .prefix = &in6addr_loopback, 98 + .prefixlen = 128, 99 + .label = 0, 100 + } 101 + }; 102 + 103 + /* Object management */ 104 + static inline void ip6addrlbl_free(struct ip6addrlbl_entry *p) 105 + { 106 + kfree(p); 107 + } 108 + 109 + static inline int ip6addrlbl_hold(struct ip6addrlbl_entry *p) 110 + { 111 + return atomic_inc_not_zero(&p->refcnt); 112 + } 113 + 114 + static inline void ip6addrlbl_put(struct ip6addrlbl_entry *p) 115 + { 116 + if (atomic_dec_and_test(&p->refcnt)) 117 + ip6addrlbl_free(p); 118 + } 119 + 120 + static void ip6addrlbl_free_rcu(struct rcu_head *h) 121 + { 122 + ip6addrlbl_free(container_of(h, struct ip6addrlbl_entry, rcu)); 123 + } 124 + 125 + /* Find label */ 126 + static int __ip6addrlbl_match(struct ip6addrlbl_entry *p, 127 + const struct in6_addr *addr, 128 + int addrtype, int ifindex) 129 + { 130 + if (p->ifindex && p->ifindex != ifindex) 131 + return 0; 132 + if (p->addrtype && p->addrtype != addrtype) 133 + return 0; 134 + if (!ipv6_prefix_equal(addr, &p->prefix, p->prefixlen)) 135 + return 0; 136 + return 1; 137 + } 138 + 139 + static struct ip6addrlbl_entry *__ipv6_addr_label(const struct in6_addr *addr, 140 + int type, int ifindex) 141 + { 142 + struct hlist_node *pos; 143 + struct ip6addrlbl_entry *p; 144 + hlist_for_each_entry_rcu(p, pos, &ip6addrlbl_table.head, list) { 145 + if (__ip6addrlbl_match(p, addr, type, ifindex)) 146 + return p; 147 + } 148 + return NULL; 149 + } 150 + 151 + u32 ipv6_addr_label(const struct in6_addr *addr, int type, int ifindex) 152 + { 153 + u32 label; 154 + struct ip6addrlbl_entry *p; 155 + 156 + type &= IPV6_ADDR_MAPPED | IPV6_ADDR_COMPATv4 | IPV6_ADDR_LOOPBACK; 157 + 158 + rcu_read_lock(); 159 + p = __ipv6_addr_label(addr, type, ifindex); 160 + label = p ? p->label : IPV6_ADDR_LABEL_DEFAULT; 161 + rcu_read_unlock(); 162 + 163 + ADDRLABEL(KERN_DEBUG "%s(addr=" NIP6_FMT ", type=%d, ifindex=%d) => %08x\n", 164 + __FUNCTION__, 165 + NIP6(*addr), type, ifindex, 166 + label); 167 + 168 + return label; 169 + } 170 + 171 + /* allocate one entry */ 172 + struct ip6addrlbl_entry *ip6addrlbl_alloc(const struct in6_addr *prefix, 173 + int prefixlen, int ifindex, 174 + u32 label) 175 + { 176 + struct ip6addrlbl_entry *newp; 177 + int addrtype; 178 + 179 + ADDRLABEL(KERN_DEBUG "%s(prefix=" NIP6_FMT ", prefixlen=%d, ifindex=%d, label=%u)\n", 180 + __FUNCTION__, 181 + NIP6(*prefix), prefixlen, 182 + ifindex, 183 + (unsigned int)label); 184 + 185 + addrtype = ipv6_addr_type(prefix) & (IPV6_ADDR_MAPPED | IPV6_ADDR_COMPATv4 | IPV6_ADDR_LOOPBACK); 186 + 187 + switch (addrtype) { 188 + case IPV6_ADDR_MAPPED: 189 + if (prefixlen > 96) 190 + return ERR_PTR(-EINVAL); 191 + if (prefixlen < 96) 192 + addrtype = 0; 193 + break; 194 + case IPV6_ADDR_COMPATv4: 195 + if (prefixlen != 96) 196 + addrtype = 0; 197 + break; 198 + case IPV6_ADDR_LOOPBACK: 199 + if (prefixlen != 128) 200 + addrtype = 0; 201 + break; 202 + } 203 + 204 + newp = kmalloc(sizeof(*newp), GFP_KERNEL); 205 + if (!newp) 206 + return ERR_PTR(-ENOMEM); 207 + 208 + ipv6_addr_prefix(&newp->prefix, prefix, prefixlen); 209 + newp->prefixlen = prefixlen; 210 + newp->ifindex = ifindex; 211 + newp->addrtype = addrtype; 212 + newp->label = label; 213 + INIT_HLIST_NODE(&newp->list); 214 + atomic_set(&newp->refcnt, 1); 215 + return newp; 216 + } 217 + 218 + /* add a label */ 219 + int __ip6addrlbl_add(struct ip6addrlbl_entry *newp, int replace) 220 + { 221 + int ret = 0; 222 + 223 + ADDRLABEL(KERN_DEBUG "%s(newp=%p, replace=%d)\n", 224 + __FUNCTION__, 225 + newp, replace); 226 + 227 + if (hlist_empty(&ip6addrlbl_table.head)) { 228 + hlist_add_head_rcu(&newp->list, &ip6addrlbl_table.head); 229 + } else { 230 + struct hlist_node *pos, *n; 231 + struct ip6addrlbl_entry *p = NULL; 232 + hlist_for_each_entry_safe(p, pos, n, 233 + &ip6addrlbl_table.head, list) { 234 + if (p->prefixlen == newp->prefixlen && 235 + p->ifindex == newp->ifindex && 236 + ipv6_addr_equal(&p->prefix, &newp->prefix)) { 237 + if (!replace) { 238 + ret = -EEXIST; 239 + goto out; 240 + } 241 + hlist_replace_rcu(&p->list, &newp->list); 242 + ip6addrlbl_put(p); 243 + call_rcu(&p->rcu, ip6addrlbl_free_rcu); 244 + goto out; 245 + } else if ((p->prefixlen == newp->prefixlen && !p->ifindex) || 246 + (p->prefixlen < newp->prefixlen)) { 247 + hlist_add_before_rcu(&newp->list, &p->list); 248 + goto out; 249 + } 250 + } 251 + hlist_add_after_rcu(&p->list, &newp->list); 252 + } 253 + out: 254 + if (!ret) 255 + ip6addrlbl_table.seq++; 256 + return ret; 257 + } 258 + 259 + /* add a label */ 260 + int ip6addrlbl_add(const struct in6_addr *prefix, int prefixlen, 261 + int ifindex, u32 label, int replace) 262 + { 263 + struct ip6addrlbl_entry *newp; 264 + int ret = 0; 265 + 266 + ADDRLABEL(KERN_DEBUG "%s(prefix=" NIP6_FMT ", prefixlen=%d, ifindex=%d, label=%u, replace=%d)\n", 267 + __FUNCTION__, 268 + NIP6(*prefix), prefixlen, 269 + ifindex, 270 + (unsigned int)label, 271 + replace); 272 + 273 + newp = ip6addrlbl_alloc(prefix, prefixlen, ifindex, label); 274 + if (IS_ERR(newp)) 275 + return PTR_ERR(newp); 276 + spin_lock(&ip6addrlbl_table.lock); 277 + ret = __ip6addrlbl_add(newp, replace); 278 + spin_unlock(&ip6addrlbl_table.lock); 279 + if (ret) 280 + ip6addrlbl_free(newp); 281 + return ret; 282 + } 283 + 284 + /* remove a label */ 285 + int __ip6addrlbl_del(const struct in6_addr *prefix, int prefixlen, 286 + int ifindex) 287 + { 288 + struct ip6addrlbl_entry *p = NULL; 289 + struct hlist_node *pos, *n; 290 + int ret = -ESRCH; 291 + 292 + ADDRLABEL(KERN_DEBUG "%s(prefix=" NIP6_FMT ", prefixlen=%d, ifindex=%d)\n", 293 + __FUNCTION__, 294 + NIP6(*prefix), prefixlen, 295 + ifindex); 296 + 297 + hlist_for_each_entry_safe(p, pos, n, &ip6addrlbl_table.head, list) { 298 + if (p->prefixlen == prefixlen && 299 + p->ifindex == ifindex && 300 + ipv6_addr_equal(&p->prefix, prefix)) { 301 + hlist_del_rcu(&p->list); 302 + ip6addrlbl_put(p); 303 + call_rcu(&p->rcu, ip6addrlbl_free_rcu); 304 + ret = 0; 305 + break; 306 + } 307 + } 308 + return ret; 309 + } 310 + 311 + int ip6addrlbl_del(const struct in6_addr *prefix, int prefixlen, 312 + int ifindex) 313 + { 314 + struct in6_addr prefix_buf; 315 + int ret; 316 + 317 + ADDRLABEL(KERN_DEBUG "%s(prefix=" NIP6_FMT ", prefixlen=%d, ifindex=%d)\n", 318 + __FUNCTION__, 319 + NIP6(*prefix), prefixlen, 320 + ifindex); 321 + 322 + ipv6_addr_prefix(&prefix_buf, prefix, prefixlen); 323 + spin_lock(&ip6addrlbl_table.lock); 324 + ret = __ip6addrlbl_del(&prefix_buf, prefixlen, ifindex); 325 + spin_unlock(&ip6addrlbl_table.lock); 326 + return ret; 327 + } 328 + 329 + /* add default label */ 330 + static __init int ip6addrlbl_init(void) 331 + { 332 + int err = 0; 333 + int i; 334 + 335 + ADDRLABEL(KERN_DEBUG "%s()\n", __FUNCTION__); 336 + 337 + for (i = 0; i < ARRAY_SIZE(ip6addrlbl_init_table); i++) { 338 + int ret = ip6addrlbl_add(ip6addrlbl_init_table[i].prefix, 339 + ip6addrlbl_init_table[i].prefixlen, 340 + 0, 341 + ip6addrlbl_init_table[i].label, 0); 342 + /* XXX: should we free all rules when we catch an error? */ 343 + if (ret && (!err || err != -ENOMEM)) 344 + err = ret; 345 + } 346 + return err; 347 + } 348 + 349 + int __init ipv6_addr_label_init(void) 350 + { 351 + spin_lock_init(&ip6addrlbl_table.lock); 352 + 353 + return ip6addrlbl_init(); 354 + } 355 + 356 + static const struct nla_policy ifal_policy[IFAL_MAX+1] = { 357 + [IFAL_ADDRESS] = { .len = sizeof(struct in6_addr), }, 358 + [IFAL_LABEL] = { .len = sizeof(u32), }, 359 + }; 360 + 361 + static int ip6addrlbl_newdel(struct sk_buff *skb, struct nlmsghdr *nlh, 362 + void *arg) 363 + { 364 + struct ifaddrlblmsg *ifal; 365 + struct nlattr *tb[IFAL_MAX+1]; 366 + struct in6_addr *pfx; 367 + u32 label; 368 + int err = 0; 369 + 370 + err = nlmsg_parse(nlh, sizeof(*ifal), tb, IFAL_MAX, ifal_policy); 371 + if (err < 0) 372 + return err; 373 + 374 + ifal = nlmsg_data(nlh); 375 + 376 + if (ifal->ifal_family != AF_INET6 || 377 + ifal->ifal_prefixlen > 128) 378 + return -EINVAL; 379 + 380 + if (ifal->ifal_index && 381 + !__dev_get_by_index(&init_net, ifal->ifal_index)) 382 + return -EINVAL; 383 + 384 + if (!tb[IFAL_ADDRESS]) 385 + return -EINVAL; 386 + 387 + pfx = nla_data(tb[IFAL_ADDRESS]); 388 + if (!pfx) 389 + return -EINVAL; 390 + 391 + if (!tb[IFAL_LABEL]) 392 + return -EINVAL; 393 + label = nla_get_u32(tb[IFAL_LABEL]); 394 + if (label == IPV6_ADDR_LABEL_DEFAULT) 395 + return -EINVAL; 396 + 397 + switch(nlh->nlmsg_type) { 398 + case RTM_NEWADDRLABEL: 399 + err = ip6addrlbl_add(pfx, ifal->ifal_prefixlen, 400 + ifal->ifal_index, label, 401 + nlh->nlmsg_flags & NLM_F_REPLACE); 402 + break; 403 + case RTM_DELADDRLABEL: 404 + err = ip6addrlbl_del(pfx, ifal->ifal_prefixlen, 405 + ifal->ifal_index); 406 + break; 407 + default: 408 + err = -EOPNOTSUPP; 409 + } 410 + return err; 411 + } 412 + 413 + static inline void ip6addrlbl_putmsg(struct nlmsghdr *nlh, 414 + int prefixlen, int ifindex, u32 lseq) 415 + { 416 + struct ifaddrlblmsg *ifal = nlmsg_data(nlh); 417 + ifal->ifal_family = AF_INET6; 418 + ifal->ifal_prefixlen = prefixlen; 419 + ifal->ifal_flags = 0; 420 + ifal->ifal_index = ifindex; 421 + ifal->ifal_seq = lseq; 422 + }; 423 + 424 + static int ip6addrlbl_fill(struct sk_buff *skb, 425 + struct ip6addrlbl_entry *p, 426 + u32 lseq, 427 + u32 pid, u32 seq, int event, 428 + unsigned int flags) 429 + { 430 + struct nlmsghdr *nlh = nlmsg_put(skb, pid, seq, event, 431 + sizeof(struct ifaddrlblmsg), flags); 432 + if (!nlh) 433 + return -EMSGSIZE; 434 + 435 + ip6addrlbl_putmsg(nlh, p->prefixlen, p->ifindex, lseq); 436 + 437 + if (nla_put(skb, IFAL_ADDRESS, 16, &p->prefix) < 0 || 438 + nla_put_u32(skb, IFAL_LABEL, p->label) < 0) { 439 + nlmsg_cancel(skb, nlh); 440 + return -EMSGSIZE; 441 + } 442 + 443 + return nlmsg_end(skb, nlh); 444 + } 445 + 446 + static int ip6addrlbl_dump(struct sk_buff *skb, struct netlink_callback *cb) 447 + { 448 + struct ip6addrlbl_entry *p; 449 + struct hlist_node *pos; 450 + int idx = 0, s_idx = cb->args[0]; 451 + int err; 452 + 453 + rcu_read_lock(); 454 + hlist_for_each_entry_rcu(p, pos, &ip6addrlbl_table.head, list) { 455 + if (idx >= s_idx) { 456 + if ((err = ip6addrlbl_fill(skb, p, 457 + ip6addrlbl_table.seq, 458 + NETLINK_CB(cb->skb).pid, 459 + cb->nlh->nlmsg_seq, 460 + RTM_NEWADDRLABEL, 461 + NLM_F_MULTI)) <= 0) 462 + break; 463 + } 464 + idx++; 465 + } 466 + rcu_read_unlock(); 467 + cb->args[0] = idx; 468 + return skb->len; 469 + } 470 + 471 + static inline int ip6addrlbl_msgsize(void) 472 + { 473 + return (NLMSG_ALIGN(sizeof(struct ifaddrlblmsg)) 474 + + nla_total_size(16) /* IFAL_ADDRESS */ 475 + + nla_total_size(4) /* IFAL_LABEL */ 476 + ); 477 + } 478 + 479 + static int ip6addrlbl_get(struct sk_buff *in_skb, struct nlmsghdr* nlh, 480 + void *arg) 481 + { 482 + struct ifaddrlblmsg *ifal; 483 + struct nlattr *tb[IFAL_MAX+1]; 484 + struct in6_addr *addr; 485 + u32 lseq; 486 + int err = 0; 487 + struct ip6addrlbl_entry *p; 488 + struct sk_buff *skb; 489 + 490 + err = nlmsg_parse(nlh, sizeof(*ifal), tb, IFAL_MAX, ifal_policy); 491 + if (err < 0) 492 + return err; 493 + 494 + ifal = nlmsg_data(nlh); 495 + 496 + if (ifal->ifal_family != AF_INET6 || 497 + ifal->ifal_prefixlen != 128) 498 + return -EINVAL; 499 + 500 + if (ifal->ifal_index && 501 + !__dev_get_by_index(&init_net, ifal->ifal_index)) 502 + return -EINVAL; 503 + 504 + if (!tb[IFAL_ADDRESS]) 505 + return -EINVAL; 506 + 507 + addr = nla_data(tb[IFAL_ADDRESS]); 508 + if (!addr) 509 + return -EINVAL; 510 + 511 + rcu_read_lock(); 512 + p = __ipv6_addr_label(addr, ipv6_addr_type(addr), ifal->ifal_index); 513 + if (p && ip6addrlbl_hold(p)) 514 + p = NULL; 515 + lseq = ip6addrlbl_table.seq; 516 + rcu_read_unlock(); 517 + 518 + if (!p) { 519 + err = -ESRCH; 520 + goto out; 521 + } 522 + 523 + if (!(skb = nlmsg_new(ip6addrlbl_msgsize(), GFP_KERNEL))) { 524 + ip6addrlbl_put(p); 525 + return -ENOBUFS; 526 + } 527 + 528 + err = ip6addrlbl_fill(skb, p, lseq, 529 + NETLINK_CB(in_skb).pid, nlh->nlmsg_seq, 530 + RTM_NEWADDRLABEL, 0); 531 + 532 + ip6addrlbl_put(p); 533 + 534 + if (err < 0) { 535 + WARN_ON(err == -EMSGSIZE); 536 + kfree_skb(skb); 537 + goto out; 538 + } 539 + 540 + err = rtnl_unicast(skb, NETLINK_CB(in_skb).pid); 541 + out: 542 + return err; 543 + } 544 + 545 + void __init ipv6_addr_label_rtnl_register(void) 546 + { 547 + __rtnl_register(PF_INET6, RTM_NEWADDRLABEL, ip6addrlbl_newdel, NULL); 548 + __rtnl_register(PF_INET6, RTM_DELADDRLABEL, ip6addrlbl_newdel, NULL); 549 + __rtnl_register(PF_INET6, RTM_GETADDRLABEL, ip6addrlbl_get, ip6addrlbl_dump); 550 + } 551 +