12a8cc6c8SYOSHIFUJI Hideaki /* 22a8cc6c8SYOSHIFUJI Hideaki * IPv6 Address Label subsystem 32a8cc6c8SYOSHIFUJI Hideaki * for the IPv6 "Default" Source Address Selection 42a8cc6c8SYOSHIFUJI Hideaki * 52a8cc6c8SYOSHIFUJI Hideaki * Copyright (C)2007 USAGI/WIDE Project 62a8cc6c8SYOSHIFUJI Hideaki */ 72a8cc6c8SYOSHIFUJI Hideaki /* 82a8cc6c8SYOSHIFUJI Hideaki * Author: 92a8cc6c8SYOSHIFUJI Hideaki * YOSHIFUJI Hideaki @ USAGI/WIDE Project <yoshfuji@linux-ipv6.org> 102a8cc6c8SYOSHIFUJI Hideaki */ 112a8cc6c8SYOSHIFUJI Hideaki 122a8cc6c8SYOSHIFUJI Hideaki #include <linux/kernel.h> 132a8cc6c8SYOSHIFUJI Hideaki #include <linux/list.h> 142a8cc6c8SYOSHIFUJI Hideaki #include <linux/rcupdate.h> 152a8cc6c8SYOSHIFUJI Hideaki #include <linux/in6.h> 162a8cc6c8SYOSHIFUJI Hideaki #include <net/addrconf.h> 172a8cc6c8SYOSHIFUJI Hideaki #include <linux/if_addrlabel.h> 182a8cc6c8SYOSHIFUJI Hideaki #include <linux/netlink.h> 192a8cc6c8SYOSHIFUJI Hideaki #include <linux/rtnetlink.h> 202a8cc6c8SYOSHIFUJI Hideaki 212a8cc6c8SYOSHIFUJI Hideaki #if 0 222a8cc6c8SYOSHIFUJI Hideaki #define ADDRLABEL(x...) printk(x) 232a8cc6c8SYOSHIFUJI Hideaki #else 242a8cc6c8SYOSHIFUJI Hideaki #define ADDRLABEL(x...) do { ; } while(0) 252a8cc6c8SYOSHIFUJI Hideaki #endif 262a8cc6c8SYOSHIFUJI Hideaki 272a8cc6c8SYOSHIFUJI Hideaki /* 282a8cc6c8SYOSHIFUJI Hideaki * Policy Table 292a8cc6c8SYOSHIFUJI Hideaki */ 302a8cc6c8SYOSHIFUJI Hideaki struct ip6addrlbl_entry 312a8cc6c8SYOSHIFUJI Hideaki { 322a8cc6c8SYOSHIFUJI Hideaki struct in6_addr prefix; 332a8cc6c8SYOSHIFUJI Hideaki int prefixlen; 342a8cc6c8SYOSHIFUJI Hideaki int ifindex; 352a8cc6c8SYOSHIFUJI Hideaki int addrtype; 362a8cc6c8SYOSHIFUJI Hideaki u32 label; 372a8cc6c8SYOSHIFUJI Hideaki struct hlist_node list; 382a8cc6c8SYOSHIFUJI Hideaki atomic_t refcnt; 392a8cc6c8SYOSHIFUJI Hideaki struct rcu_head rcu; 402a8cc6c8SYOSHIFUJI Hideaki }; 412a8cc6c8SYOSHIFUJI Hideaki 422a8cc6c8SYOSHIFUJI Hideaki static struct ip6addrlbl_table 432a8cc6c8SYOSHIFUJI Hideaki { 442a8cc6c8SYOSHIFUJI Hideaki struct hlist_head head; 452a8cc6c8SYOSHIFUJI Hideaki spinlock_t lock; 462a8cc6c8SYOSHIFUJI Hideaki u32 seq; 472a8cc6c8SYOSHIFUJI Hideaki } ip6addrlbl_table; 482a8cc6c8SYOSHIFUJI Hideaki 492a8cc6c8SYOSHIFUJI Hideaki /* 502a8cc6c8SYOSHIFUJI Hideaki * Default policy table (RFC3484 + extensions) 512a8cc6c8SYOSHIFUJI Hideaki * 522a8cc6c8SYOSHIFUJI Hideaki * prefix addr_type label 532a8cc6c8SYOSHIFUJI Hideaki * ------------------------------------------------------------------------- 542a8cc6c8SYOSHIFUJI Hideaki * ::1/128 LOOPBACK 0 552a8cc6c8SYOSHIFUJI Hideaki * ::/0 N/A 1 562a8cc6c8SYOSHIFUJI Hideaki * 2002::/16 N/A 2 572a8cc6c8SYOSHIFUJI Hideaki * ::/96 COMPATv4 3 582a8cc6c8SYOSHIFUJI Hideaki * ::ffff:0:0/96 V4MAPPED 4 592a8cc6c8SYOSHIFUJI Hideaki * fc00::/7 N/A 5 ULA (RFC 4193) 602a8cc6c8SYOSHIFUJI Hideaki * 2001::/32 N/A 6 Teredo (RFC 4380) 615fe47b8aSJuha-Matti Tapio * 2001:10::/28 N/A 7 ORCHID (RFC 4843) 622a8cc6c8SYOSHIFUJI Hideaki * 632a8cc6c8SYOSHIFUJI Hideaki * Note: 0xffffffff is used if we do not have any policies. 642a8cc6c8SYOSHIFUJI Hideaki */ 652a8cc6c8SYOSHIFUJI Hideaki 662a8cc6c8SYOSHIFUJI Hideaki #define IPV6_ADDR_LABEL_DEFAULT 0xffffffffUL 672a8cc6c8SYOSHIFUJI Hideaki 682a8cc6c8SYOSHIFUJI Hideaki static const __initdata struct ip6addrlbl_init_table 692a8cc6c8SYOSHIFUJI Hideaki { 702a8cc6c8SYOSHIFUJI Hideaki const struct in6_addr *prefix; 712a8cc6c8SYOSHIFUJI Hideaki int prefixlen; 722a8cc6c8SYOSHIFUJI Hideaki u32 label; 732a8cc6c8SYOSHIFUJI Hideaki } ip6addrlbl_init_table[] = { 742a8cc6c8SYOSHIFUJI Hideaki { /* ::/0 */ 752a8cc6c8SYOSHIFUJI Hideaki .prefix = &in6addr_any, 762a8cc6c8SYOSHIFUJI Hideaki .label = 1, 772a8cc6c8SYOSHIFUJI Hideaki },{ /* fc00::/7 */ 782a8cc6c8SYOSHIFUJI Hideaki .prefix = &(struct in6_addr){{{ 0xfc }}}, 792a8cc6c8SYOSHIFUJI Hideaki .prefixlen = 7, 802a8cc6c8SYOSHIFUJI Hideaki .label = 5, 812a8cc6c8SYOSHIFUJI Hideaki },{ /* 2002::/16 */ 822a8cc6c8SYOSHIFUJI Hideaki .prefix = &(struct in6_addr){{{ 0x20, 0x02 }}}, 832a8cc6c8SYOSHIFUJI Hideaki .prefixlen = 16, 842a8cc6c8SYOSHIFUJI Hideaki .label = 2, 852a8cc6c8SYOSHIFUJI Hideaki },{ /* 2001::/32 */ 862a8cc6c8SYOSHIFUJI Hideaki .prefix = &(struct in6_addr){{{ 0x20, 0x01 }}}, 872a8cc6c8SYOSHIFUJI Hideaki .prefixlen = 32, 882a8cc6c8SYOSHIFUJI Hideaki .label = 6, 895fe47b8aSJuha-Matti Tapio },{ /* 2001:10::/28 */ 905fe47b8aSJuha-Matti Tapio .prefix = &(struct in6_addr){{{ 0x20, 0x01, 0x00, 0x10 }}}, 915fe47b8aSJuha-Matti Tapio .prefixlen = 28, 925fe47b8aSJuha-Matti Tapio .label = 7, 932a8cc6c8SYOSHIFUJI Hideaki },{ /* ::ffff:0:0 */ 942a8cc6c8SYOSHIFUJI Hideaki .prefix = &(struct in6_addr){{{ [10] = 0xff, [11] = 0xff }}}, 952a8cc6c8SYOSHIFUJI Hideaki .prefixlen = 96, 962a8cc6c8SYOSHIFUJI Hideaki .label = 4, 972a8cc6c8SYOSHIFUJI Hideaki },{ /* ::/96 */ 982a8cc6c8SYOSHIFUJI Hideaki .prefix = &in6addr_any, 992a8cc6c8SYOSHIFUJI Hideaki .prefixlen = 96, 1002a8cc6c8SYOSHIFUJI Hideaki .label = 3, 1012a8cc6c8SYOSHIFUJI Hideaki },{ /* ::1/128 */ 1022a8cc6c8SYOSHIFUJI Hideaki .prefix = &in6addr_loopback, 1032a8cc6c8SYOSHIFUJI Hideaki .prefixlen = 128, 1042a8cc6c8SYOSHIFUJI Hideaki .label = 0, 1052a8cc6c8SYOSHIFUJI Hideaki } 1062a8cc6c8SYOSHIFUJI Hideaki }; 1072a8cc6c8SYOSHIFUJI Hideaki 1082a8cc6c8SYOSHIFUJI Hideaki /* Object management */ 1092a8cc6c8SYOSHIFUJI Hideaki static inline void ip6addrlbl_free(struct ip6addrlbl_entry *p) 1102a8cc6c8SYOSHIFUJI Hideaki { 1112a8cc6c8SYOSHIFUJI Hideaki kfree(p); 1122a8cc6c8SYOSHIFUJI Hideaki } 1132a8cc6c8SYOSHIFUJI Hideaki 11485040bcbSYOSHIFUJI Hideaki static void ip6addrlbl_free_rcu(struct rcu_head *h) 11585040bcbSYOSHIFUJI Hideaki { 11685040bcbSYOSHIFUJI Hideaki ip6addrlbl_free(container_of(h, struct ip6addrlbl_entry, rcu)); 11785040bcbSYOSHIFUJI Hideaki } 11885040bcbSYOSHIFUJI Hideaki 1192a8cc6c8SYOSHIFUJI Hideaki static inline int ip6addrlbl_hold(struct ip6addrlbl_entry *p) 1202a8cc6c8SYOSHIFUJI Hideaki { 1212a8cc6c8SYOSHIFUJI Hideaki return atomic_inc_not_zero(&p->refcnt); 1222a8cc6c8SYOSHIFUJI Hideaki } 1232a8cc6c8SYOSHIFUJI Hideaki 1242a8cc6c8SYOSHIFUJI Hideaki static inline void ip6addrlbl_put(struct ip6addrlbl_entry *p) 1252a8cc6c8SYOSHIFUJI Hideaki { 1262a8cc6c8SYOSHIFUJI Hideaki if (atomic_dec_and_test(&p->refcnt)) 12785040bcbSYOSHIFUJI Hideaki call_rcu(&p->rcu, ip6addrlbl_free_rcu); 1282a8cc6c8SYOSHIFUJI Hideaki } 1292a8cc6c8SYOSHIFUJI Hideaki 1302a8cc6c8SYOSHIFUJI Hideaki /* Find label */ 1312a8cc6c8SYOSHIFUJI Hideaki static int __ip6addrlbl_match(struct ip6addrlbl_entry *p, 1322a8cc6c8SYOSHIFUJI Hideaki const struct in6_addr *addr, 1332a8cc6c8SYOSHIFUJI Hideaki int addrtype, int ifindex) 1342a8cc6c8SYOSHIFUJI Hideaki { 1352a8cc6c8SYOSHIFUJI Hideaki if (p->ifindex && p->ifindex != ifindex) 1362a8cc6c8SYOSHIFUJI Hideaki return 0; 1372a8cc6c8SYOSHIFUJI Hideaki if (p->addrtype && p->addrtype != addrtype) 1382a8cc6c8SYOSHIFUJI Hideaki return 0; 1392a8cc6c8SYOSHIFUJI Hideaki if (!ipv6_prefix_equal(addr, &p->prefix, p->prefixlen)) 1402a8cc6c8SYOSHIFUJI Hideaki return 0; 1412a8cc6c8SYOSHIFUJI Hideaki return 1; 1422a8cc6c8SYOSHIFUJI Hideaki } 1432a8cc6c8SYOSHIFUJI Hideaki 1442a8cc6c8SYOSHIFUJI Hideaki static struct ip6addrlbl_entry *__ipv6_addr_label(const struct in6_addr *addr, 1452a8cc6c8SYOSHIFUJI Hideaki int type, int ifindex) 1462a8cc6c8SYOSHIFUJI Hideaki { 1472a8cc6c8SYOSHIFUJI Hideaki struct hlist_node *pos; 1482a8cc6c8SYOSHIFUJI Hideaki struct ip6addrlbl_entry *p; 1492a8cc6c8SYOSHIFUJI Hideaki hlist_for_each_entry_rcu(p, pos, &ip6addrlbl_table.head, list) { 1502a8cc6c8SYOSHIFUJI Hideaki if (__ip6addrlbl_match(p, addr, type, ifindex)) 1512a8cc6c8SYOSHIFUJI Hideaki return p; 1522a8cc6c8SYOSHIFUJI Hideaki } 1532a8cc6c8SYOSHIFUJI Hideaki return NULL; 1542a8cc6c8SYOSHIFUJI Hideaki } 1552a8cc6c8SYOSHIFUJI Hideaki 1562a8cc6c8SYOSHIFUJI Hideaki u32 ipv6_addr_label(const struct in6_addr *addr, int type, int ifindex) 1572a8cc6c8SYOSHIFUJI Hideaki { 1582a8cc6c8SYOSHIFUJI Hideaki u32 label; 1592a8cc6c8SYOSHIFUJI Hideaki struct ip6addrlbl_entry *p; 1602a8cc6c8SYOSHIFUJI Hideaki 1612a8cc6c8SYOSHIFUJI Hideaki type &= IPV6_ADDR_MAPPED | IPV6_ADDR_COMPATv4 | IPV6_ADDR_LOOPBACK; 1622a8cc6c8SYOSHIFUJI Hideaki 1632a8cc6c8SYOSHIFUJI Hideaki rcu_read_lock(); 1642a8cc6c8SYOSHIFUJI Hideaki p = __ipv6_addr_label(addr, type, ifindex); 1652a8cc6c8SYOSHIFUJI Hideaki label = p ? p->label : IPV6_ADDR_LABEL_DEFAULT; 1662a8cc6c8SYOSHIFUJI Hideaki rcu_read_unlock(); 1672a8cc6c8SYOSHIFUJI Hideaki 1682a8cc6c8SYOSHIFUJI Hideaki ADDRLABEL(KERN_DEBUG "%s(addr=" NIP6_FMT ", type=%d, ifindex=%d) => %08x\n", 1690dc47877SHarvey Harrison __func__, 1702a8cc6c8SYOSHIFUJI Hideaki NIP6(*addr), type, ifindex, 1712a8cc6c8SYOSHIFUJI Hideaki label); 1722a8cc6c8SYOSHIFUJI Hideaki 1732a8cc6c8SYOSHIFUJI Hideaki return label; 1742a8cc6c8SYOSHIFUJI Hideaki } 1752a8cc6c8SYOSHIFUJI Hideaki 1762a8cc6c8SYOSHIFUJI Hideaki /* allocate one entry */ 17740fee36eSYOSHIFUJI Hideaki static struct ip6addrlbl_entry *ip6addrlbl_alloc(const struct in6_addr *prefix, 1782a8cc6c8SYOSHIFUJI Hideaki int prefixlen, int ifindex, 1792a8cc6c8SYOSHIFUJI Hideaki u32 label) 1802a8cc6c8SYOSHIFUJI Hideaki { 1812a8cc6c8SYOSHIFUJI Hideaki struct ip6addrlbl_entry *newp; 1822a8cc6c8SYOSHIFUJI Hideaki int addrtype; 1832a8cc6c8SYOSHIFUJI Hideaki 1842a8cc6c8SYOSHIFUJI Hideaki ADDRLABEL(KERN_DEBUG "%s(prefix=" NIP6_FMT ", prefixlen=%d, ifindex=%d, label=%u)\n", 1850dc47877SHarvey Harrison __func__, 1862a8cc6c8SYOSHIFUJI Hideaki NIP6(*prefix), prefixlen, 1872a8cc6c8SYOSHIFUJI Hideaki ifindex, 1882a8cc6c8SYOSHIFUJI Hideaki (unsigned int)label); 1892a8cc6c8SYOSHIFUJI Hideaki 1902a8cc6c8SYOSHIFUJI Hideaki addrtype = ipv6_addr_type(prefix) & (IPV6_ADDR_MAPPED | IPV6_ADDR_COMPATv4 | IPV6_ADDR_LOOPBACK); 1912a8cc6c8SYOSHIFUJI Hideaki 1922a8cc6c8SYOSHIFUJI Hideaki switch (addrtype) { 1932a8cc6c8SYOSHIFUJI Hideaki case IPV6_ADDR_MAPPED: 1942a8cc6c8SYOSHIFUJI Hideaki if (prefixlen > 96) 1952a8cc6c8SYOSHIFUJI Hideaki return ERR_PTR(-EINVAL); 1962a8cc6c8SYOSHIFUJI Hideaki if (prefixlen < 96) 1972a8cc6c8SYOSHIFUJI Hideaki addrtype = 0; 1982a8cc6c8SYOSHIFUJI Hideaki break; 1992a8cc6c8SYOSHIFUJI Hideaki case IPV6_ADDR_COMPATv4: 2002a8cc6c8SYOSHIFUJI Hideaki if (prefixlen != 96) 2012a8cc6c8SYOSHIFUJI Hideaki addrtype = 0; 2022a8cc6c8SYOSHIFUJI Hideaki break; 2032a8cc6c8SYOSHIFUJI Hideaki case IPV6_ADDR_LOOPBACK: 2042a8cc6c8SYOSHIFUJI Hideaki if (prefixlen != 128) 2052a8cc6c8SYOSHIFUJI Hideaki addrtype = 0; 2062a8cc6c8SYOSHIFUJI Hideaki break; 2072a8cc6c8SYOSHIFUJI Hideaki } 2082a8cc6c8SYOSHIFUJI Hideaki 2092a8cc6c8SYOSHIFUJI Hideaki newp = kmalloc(sizeof(*newp), GFP_KERNEL); 2102a8cc6c8SYOSHIFUJI Hideaki if (!newp) 2112a8cc6c8SYOSHIFUJI Hideaki return ERR_PTR(-ENOMEM); 2122a8cc6c8SYOSHIFUJI Hideaki 2132a8cc6c8SYOSHIFUJI Hideaki ipv6_addr_prefix(&newp->prefix, prefix, prefixlen); 2142a8cc6c8SYOSHIFUJI Hideaki newp->prefixlen = prefixlen; 2152a8cc6c8SYOSHIFUJI Hideaki newp->ifindex = ifindex; 2162a8cc6c8SYOSHIFUJI Hideaki newp->addrtype = addrtype; 2172a8cc6c8SYOSHIFUJI Hideaki newp->label = label; 2182a8cc6c8SYOSHIFUJI Hideaki INIT_HLIST_NODE(&newp->list); 2192a8cc6c8SYOSHIFUJI Hideaki atomic_set(&newp->refcnt, 1); 2202a8cc6c8SYOSHIFUJI Hideaki return newp; 2212a8cc6c8SYOSHIFUJI Hideaki } 2222a8cc6c8SYOSHIFUJI Hideaki 2232a8cc6c8SYOSHIFUJI Hideaki /* add a label */ 22440fee36eSYOSHIFUJI Hideaki static int __ip6addrlbl_add(struct ip6addrlbl_entry *newp, int replace) 2252a8cc6c8SYOSHIFUJI Hideaki { 2262a8cc6c8SYOSHIFUJI Hideaki int ret = 0; 2272a8cc6c8SYOSHIFUJI Hideaki 2282a8cc6c8SYOSHIFUJI Hideaki ADDRLABEL(KERN_DEBUG "%s(newp=%p, replace=%d)\n", 2290dc47877SHarvey Harrison __func__, 2302a8cc6c8SYOSHIFUJI Hideaki newp, replace); 2312a8cc6c8SYOSHIFUJI Hideaki 2322a8cc6c8SYOSHIFUJI Hideaki if (hlist_empty(&ip6addrlbl_table.head)) { 2332a8cc6c8SYOSHIFUJI Hideaki hlist_add_head_rcu(&newp->list, &ip6addrlbl_table.head); 2342a8cc6c8SYOSHIFUJI Hideaki } else { 2352a8cc6c8SYOSHIFUJI Hideaki struct hlist_node *pos, *n; 2362a8cc6c8SYOSHIFUJI Hideaki struct ip6addrlbl_entry *p = NULL; 2372a8cc6c8SYOSHIFUJI Hideaki hlist_for_each_entry_safe(p, pos, n, 2382a8cc6c8SYOSHIFUJI Hideaki &ip6addrlbl_table.head, list) { 2392a8cc6c8SYOSHIFUJI Hideaki if (p->prefixlen == newp->prefixlen && 2402a8cc6c8SYOSHIFUJI Hideaki p->ifindex == newp->ifindex && 2412a8cc6c8SYOSHIFUJI Hideaki ipv6_addr_equal(&p->prefix, &newp->prefix)) { 2422a8cc6c8SYOSHIFUJI Hideaki if (!replace) { 2432a8cc6c8SYOSHIFUJI Hideaki ret = -EEXIST; 2442a8cc6c8SYOSHIFUJI Hideaki goto out; 2452a8cc6c8SYOSHIFUJI Hideaki } 2462a8cc6c8SYOSHIFUJI Hideaki hlist_replace_rcu(&p->list, &newp->list); 2472a8cc6c8SYOSHIFUJI Hideaki ip6addrlbl_put(p); 2482a8cc6c8SYOSHIFUJI Hideaki goto out; 2492a8cc6c8SYOSHIFUJI Hideaki } else if ((p->prefixlen == newp->prefixlen && !p->ifindex) || 2502a8cc6c8SYOSHIFUJI Hideaki (p->prefixlen < newp->prefixlen)) { 2512a8cc6c8SYOSHIFUJI Hideaki hlist_add_before_rcu(&newp->list, &p->list); 2522a8cc6c8SYOSHIFUJI Hideaki goto out; 2532a8cc6c8SYOSHIFUJI Hideaki } 2542a8cc6c8SYOSHIFUJI Hideaki } 2552a8cc6c8SYOSHIFUJI Hideaki hlist_add_after_rcu(&p->list, &newp->list); 2562a8cc6c8SYOSHIFUJI Hideaki } 2572a8cc6c8SYOSHIFUJI Hideaki out: 2582a8cc6c8SYOSHIFUJI Hideaki if (!ret) 2592a8cc6c8SYOSHIFUJI Hideaki ip6addrlbl_table.seq++; 2602a8cc6c8SYOSHIFUJI Hideaki return ret; 2612a8cc6c8SYOSHIFUJI Hideaki } 2622a8cc6c8SYOSHIFUJI Hideaki 2632a8cc6c8SYOSHIFUJI Hideaki /* add a label */ 26440fee36eSYOSHIFUJI Hideaki static int ip6addrlbl_add(const struct in6_addr *prefix, int prefixlen, 2652a8cc6c8SYOSHIFUJI Hideaki int ifindex, u32 label, int replace) 2662a8cc6c8SYOSHIFUJI Hideaki { 2672a8cc6c8SYOSHIFUJI Hideaki struct ip6addrlbl_entry *newp; 2682a8cc6c8SYOSHIFUJI Hideaki int ret = 0; 2692a8cc6c8SYOSHIFUJI Hideaki 2702a8cc6c8SYOSHIFUJI Hideaki ADDRLABEL(KERN_DEBUG "%s(prefix=" NIP6_FMT ", prefixlen=%d, ifindex=%d, label=%u, replace=%d)\n", 2710dc47877SHarvey Harrison __func__, 2722a8cc6c8SYOSHIFUJI Hideaki NIP6(*prefix), prefixlen, 2732a8cc6c8SYOSHIFUJI Hideaki ifindex, 2742a8cc6c8SYOSHIFUJI Hideaki (unsigned int)label, 2752a8cc6c8SYOSHIFUJI Hideaki replace); 2762a8cc6c8SYOSHIFUJI Hideaki 2772a8cc6c8SYOSHIFUJI Hideaki newp = ip6addrlbl_alloc(prefix, prefixlen, ifindex, label); 2782a8cc6c8SYOSHIFUJI Hideaki if (IS_ERR(newp)) 2792a8cc6c8SYOSHIFUJI Hideaki return PTR_ERR(newp); 2802a8cc6c8SYOSHIFUJI Hideaki spin_lock(&ip6addrlbl_table.lock); 2812a8cc6c8SYOSHIFUJI Hideaki ret = __ip6addrlbl_add(newp, replace); 2822a8cc6c8SYOSHIFUJI Hideaki spin_unlock(&ip6addrlbl_table.lock); 2832a8cc6c8SYOSHIFUJI Hideaki if (ret) 2842a8cc6c8SYOSHIFUJI Hideaki ip6addrlbl_free(newp); 2852a8cc6c8SYOSHIFUJI Hideaki return ret; 2862a8cc6c8SYOSHIFUJI Hideaki } 2872a8cc6c8SYOSHIFUJI Hideaki 2882a8cc6c8SYOSHIFUJI Hideaki /* remove a label */ 28940fee36eSYOSHIFUJI Hideaki static int __ip6addrlbl_del(const struct in6_addr *prefix, int prefixlen, 2902a8cc6c8SYOSHIFUJI Hideaki int ifindex) 2912a8cc6c8SYOSHIFUJI Hideaki { 2922a8cc6c8SYOSHIFUJI Hideaki struct ip6addrlbl_entry *p = NULL; 2932a8cc6c8SYOSHIFUJI Hideaki struct hlist_node *pos, *n; 2942a8cc6c8SYOSHIFUJI Hideaki int ret = -ESRCH; 2952a8cc6c8SYOSHIFUJI Hideaki 2962a8cc6c8SYOSHIFUJI Hideaki ADDRLABEL(KERN_DEBUG "%s(prefix=" NIP6_FMT ", prefixlen=%d, ifindex=%d)\n", 2970dc47877SHarvey Harrison __func__, 2982a8cc6c8SYOSHIFUJI Hideaki NIP6(*prefix), prefixlen, 2992a8cc6c8SYOSHIFUJI Hideaki ifindex); 3002a8cc6c8SYOSHIFUJI Hideaki 3012a8cc6c8SYOSHIFUJI Hideaki hlist_for_each_entry_safe(p, pos, n, &ip6addrlbl_table.head, list) { 3022a8cc6c8SYOSHIFUJI Hideaki if (p->prefixlen == prefixlen && 3032a8cc6c8SYOSHIFUJI Hideaki p->ifindex == ifindex && 3042a8cc6c8SYOSHIFUJI Hideaki ipv6_addr_equal(&p->prefix, prefix)) { 3052a8cc6c8SYOSHIFUJI Hideaki hlist_del_rcu(&p->list); 3062a8cc6c8SYOSHIFUJI Hideaki ip6addrlbl_put(p); 3072a8cc6c8SYOSHIFUJI Hideaki ret = 0; 3082a8cc6c8SYOSHIFUJI Hideaki break; 3092a8cc6c8SYOSHIFUJI Hideaki } 3102a8cc6c8SYOSHIFUJI Hideaki } 3112a8cc6c8SYOSHIFUJI Hideaki return ret; 3122a8cc6c8SYOSHIFUJI Hideaki } 3132a8cc6c8SYOSHIFUJI Hideaki 31440fee36eSYOSHIFUJI Hideaki static int ip6addrlbl_del(const struct in6_addr *prefix, int prefixlen, 3152a8cc6c8SYOSHIFUJI Hideaki int ifindex) 3162a8cc6c8SYOSHIFUJI Hideaki { 3172a8cc6c8SYOSHIFUJI Hideaki struct in6_addr prefix_buf; 3182a8cc6c8SYOSHIFUJI Hideaki int ret; 3192a8cc6c8SYOSHIFUJI Hideaki 3202a8cc6c8SYOSHIFUJI Hideaki ADDRLABEL(KERN_DEBUG "%s(prefix=" NIP6_FMT ", prefixlen=%d, ifindex=%d)\n", 3210dc47877SHarvey Harrison __func__, 3222a8cc6c8SYOSHIFUJI Hideaki NIP6(*prefix), prefixlen, 3232a8cc6c8SYOSHIFUJI Hideaki ifindex); 3242a8cc6c8SYOSHIFUJI Hideaki 3252a8cc6c8SYOSHIFUJI Hideaki ipv6_addr_prefix(&prefix_buf, prefix, prefixlen); 3262a8cc6c8SYOSHIFUJI Hideaki spin_lock(&ip6addrlbl_table.lock); 3272a8cc6c8SYOSHIFUJI Hideaki ret = __ip6addrlbl_del(&prefix_buf, prefixlen, ifindex); 3282a8cc6c8SYOSHIFUJI Hideaki spin_unlock(&ip6addrlbl_table.lock); 3292a8cc6c8SYOSHIFUJI Hideaki return ret; 3302a8cc6c8SYOSHIFUJI Hideaki } 3312a8cc6c8SYOSHIFUJI Hideaki 3322a8cc6c8SYOSHIFUJI Hideaki /* add default label */ 3332a8cc6c8SYOSHIFUJI Hideaki static __init int ip6addrlbl_init(void) 3342a8cc6c8SYOSHIFUJI Hideaki { 3352a8cc6c8SYOSHIFUJI Hideaki int err = 0; 3362a8cc6c8SYOSHIFUJI Hideaki int i; 3372a8cc6c8SYOSHIFUJI Hideaki 3380dc47877SHarvey Harrison ADDRLABEL(KERN_DEBUG "%s()\n", __func__); 3392a8cc6c8SYOSHIFUJI Hideaki 3402a8cc6c8SYOSHIFUJI Hideaki for (i = 0; i < ARRAY_SIZE(ip6addrlbl_init_table); i++) { 3412a8cc6c8SYOSHIFUJI Hideaki int ret = ip6addrlbl_add(ip6addrlbl_init_table[i].prefix, 3422a8cc6c8SYOSHIFUJI Hideaki ip6addrlbl_init_table[i].prefixlen, 3432a8cc6c8SYOSHIFUJI Hideaki 0, 3442a8cc6c8SYOSHIFUJI Hideaki ip6addrlbl_init_table[i].label, 0); 3452a8cc6c8SYOSHIFUJI Hideaki /* XXX: should we free all rules when we catch an error? */ 3462a8cc6c8SYOSHIFUJI Hideaki if (ret && (!err || err != -ENOMEM)) 3472a8cc6c8SYOSHIFUJI Hideaki err = ret; 3482a8cc6c8SYOSHIFUJI Hideaki } 3492a8cc6c8SYOSHIFUJI Hideaki return err; 3502a8cc6c8SYOSHIFUJI Hideaki } 3512a8cc6c8SYOSHIFUJI Hideaki 3522a8cc6c8SYOSHIFUJI Hideaki int __init ipv6_addr_label_init(void) 3532a8cc6c8SYOSHIFUJI Hideaki { 3542a8cc6c8SYOSHIFUJI Hideaki spin_lock_init(&ip6addrlbl_table.lock); 3552a8cc6c8SYOSHIFUJI Hideaki 3562a8cc6c8SYOSHIFUJI Hideaki return ip6addrlbl_init(); 3572a8cc6c8SYOSHIFUJI Hideaki } 3582a8cc6c8SYOSHIFUJI Hideaki 3592a8cc6c8SYOSHIFUJI Hideaki static const struct nla_policy ifal_policy[IFAL_MAX+1] = { 3602a8cc6c8SYOSHIFUJI Hideaki [IFAL_ADDRESS] = { .len = sizeof(struct in6_addr), }, 3612a8cc6c8SYOSHIFUJI Hideaki [IFAL_LABEL] = { .len = sizeof(u32), }, 3622a8cc6c8SYOSHIFUJI Hideaki }; 3632a8cc6c8SYOSHIFUJI Hideaki 3642a8cc6c8SYOSHIFUJI Hideaki static int ip6addrlbl_newdel(struct sk_buff *skb, struct nlmsghdr *nlh, 3652a8cc6c8SYOSHIFUJI Hideaki void *arg) 3662a8cc6c8SYOSHIFUJI Hideaki { 367b854272bSDenis V. Lunev struct net *net = skb->sk->sk_net; 3682a8cc6c8SYOSHIFUJI Hideaki struct ifaddrlblmsg *ifal; 3692a8cc6c8SYOSHIFUJI Hideaki struct nlattr *tb[IFAL_MAX+1]; 3702a8cc6c8SYOSHIFUJI Hideaki struct in6_addr *pfx; 3712a8cc6c8SYOSHIFUJI Hideaki u32 label; 3722a8cc6c8SYOSHIFUJI Hideaki int err = 0; 3732a8cc6c8SYOSHIFUJI Hideaki 374b854272bSDenis V. Lunev if (net != &init_net) 375b854272bSDenis V. Lunev return 0; 376b854272bSDenis V. Lunev 3772a8cc6c8SYOSHIFUJI Hideaki err = nlmsg_parse(nlh, sizeof(*ifal), tb, IFAL_MAX, ifal_policy); 3782a8cc6c8SYOSHIFUJI Hideaki if (err < 0) 3792a8cc6c8SYOSHIFUJI Hideaki return err; 3802a8cc6c8SYOSHIFUJI Hideaki 3812a8cc6c8SYOSHIFUJI Hideaki ifal = nlmsg_data(nlh); 3822a8cc6c8SYOSHIFUJI Hideaki 3832a8cc6c8SYOSHIFUJI Hideaki if (ifal->ifal_family != AF_INET6 || 3842a8cc6c8SYOSHIFUJI Hideaki ifal->ifal_prefixlen > 128) 3852a8cc6c8SYOSHIFUJI Hideaki return -EINVAL; 3862a8cc6c8SYOSHIFUJI Hideaki 3872a8cc6c8SYOSHIFUJI Hideaki if (ifal->ifal_index && 3882a8cc6c8SYOSHIFUJI Hideaki !__dev_get_by_index(&init_net, ifal->ifal_index)) 3892a8cc6c8SYOSHIFUJI Hideaki return -EINVAL; 3902a8cc6c8SYOSHIFUJI Hideaki 3912a8cc6c8SYOSHIFUJI Hideaki if (!tb[IFAL_ADDRESS]) 3922a8cc6c8SYOSHIFUJI Hideaki return -EINVAL; 3932a8cc6c8SYOSHIFUJI Hideaki 3942a8cc6c8SYOSHIFUJI Hideaki pfx = nla_data(tb[IFAL_ADDRESS]); 3952a8cc6c8SYOSHIFUJI Hideaki if (!pfx) 3962a8cc6c8SYOSHIFUJI Hideaki return -EINVAL; 3972a8cc6c8SYOSHIFUJI Hideaki 3982a8cc6c8SYOSHIFUJI Hideaki if (!tb[IFAL_LABEL]) 3992a8cc6c8SYOSHIFUJI Hideaki return -EINVAL; 4002a8cc6c8SYOSHIFUJI Hideaki label = nla_get_u32(tb[IFAL_LABEL]); 4012a8cc6c8SYOSHIFUJI Hideaki if (label == IPV6_ADDR_LABEL_DEFAULT) 4022a8cc6c8SYOSHIFUJI Hideaki return -EINVAL; 4032a8cc6c8SYOSHIFUJI Hideaki 4042a8cc6c8SYOSHIFUJI Hideaki switch(nlh->nlmsg_type) { 4052a8cc6c8SYOSHIFUJI Hideaki case RTM_NEWADDRLABEL: 4062a8cc6c8SYOSHIFUJI Hideaki err = ip6addrlbl_add(pfx, ifal->ifal_prefixlen, 4072a8cc6c8SYOSHIFUJI Hideaki ifal->ifal_index, label, 4082a8cc6c8SYOSHIFUJI Hideaki nlh->nlmsg_flags & NLM_F_REPLACE); 4092a8cc6c8SYOSHIFUJI Hideaki break; 4102a8cc6c8SYOSHIFUJI Hideaki case RTM_DELADDRLABEL: 4112a8cc6c8SYOSHIFUJI Hideaki err = ip6addrlbl_del(pfx, ifal->ifal_prefixlen, 4122a8cc6c8SYOSHIFUJI Hideaki ifal->ifal_index); 4132a8cc6c8SYOSHIFUJI Hideaki break; 4142a8cc6c8SYOSHIFUJI Hideaki default: 4152a8cc6c8SYOSHIFUJI Hideaki err = -EOPNOTSUPP; 4162a8cc6c8SYOSHIFUJI Hideaki } 4172a8cc6c8SYOSHIFUJI Hideaki return err; 4182a8cc6c8SYOSHIFUJI Hideaki } 4192a8cc6c8SYOSHIFUJI Hideaki 4202a8cc6c8SYOSHIFUJI Hideaki static inline void ip6addrlbl_putmsg(struct nlmsghdr *nlh, 4212a8cc6c8SYOSHIFUJI Hideaki int prefixlen, int ifindex, u32 lseq) 4222a8cc6c8SYOSHIFUJI Hideaki { 4232a8cc6c8SYOSHIFUJI Hideaki struct ifaddrlblmsg *ifal = nlmsg_data(nlh); 4242a8cc6c8SYOSHIFUJI Hideaki ifal->ifal_family = AF_INET6; 4252a8cc6c8SYOSHIFUJI Hideaki ifal->ifal_prefixlen = prefixlen; 4262a8cc6c8SYOSHIFUJI Hideaki ifal->ifal_flags = 0; 4272a8cc6c8SYOSHIFUJI Hideaki ifal->ifal_index = ifindex; 4282a8cc6c8SYOSHIFUJI Hideaki ifal->ifal_seq = lseq; 4292a8cc6c8SYOSHIFUJI Hideaki }; 4302a8cc6c8SYOSHIFUJI Hideaki 4312a8cc6c8SYOSHIFUJI Hideaki static int ip6addrlbl_fill(struct sk_buff *skb, 4322a8cc6c8SYOSHIFUJI Hideaki struct ip6addrlbl_entry *p, 4332a8cc6c8SYOSHIFUJI Hideaki u32 lseq, 4342a8cc6c8SYOSHIFUJI Hideaki u32 pid, u32 seq, int event, 4352a8cc6c8SYOSHIFUJI Hideaki unsigned int flags) 4362a8cc6c8SYOSHIFUJI Hideaki { 4372a8cc6c8SYOSHIFUJI Hideaki struct nlmsghdr *nlh = nlmsg_put(skb, pid, seq, event, 4382a8cc6c8SYOSHIFUJI Hideaki sizeof(struct ifaddrlblmsg), flags); 4392a8cc6c8SYOSHIFUJI Hideaki if (!nlh) 4402a8cc6c8SYOSHIFUJI Hideaki return -EMSGSIZE; 4412a8cc6c8SYOSHIFUJI Hideaki 4422a8cc6c8SYOSHIFUJI Hideaki ip6addrlbl_putmsg(nlh, p->prefixlen, p->ifindex, lseq); 4432a8cc6c8SYOSHIFUJI Hideaki 4442a8cc6c8SYOSHIFUJI Hideaki if (nla_put(skb, IFAL_ADDRESS, 16, &p->prefix) < 0 || 4452a8cc6c8SYOSHIFUJI Hideaki nla_put_u32(skb, IFAL_LABEL, p->label) < 0) { 4462a8cc6c8SYOSHIFUJI Hideaki nlmsg_cancel(skb, nlh); 4472a8cc6c8SYOSHIFUJI Hideaki return -EMSGSIZE; 4482a8cc6c8SYOSHIFUJI Hideaki } 4492a8cc6c8SYOSHIFUJI Hideaki 4502a8cc6c8SYOSHIFUJI Hideaki return nlmsg_end(skb, nlh); 4512a8cc6c8SYOSHIFUJI Hideaki } 4522a8cc6c8SYOSHIFUJI Hideaki 4532a8cc6c8SYOSHIFUJI Hideaki static int ip6addrlbl_dump(struct sk_buff *skb, struct netlink_callback *cb) 4542a8cc6c8SYOSHIFUJI Hideaki { 455b854272bSDenis V. Lunev struct net *net = skb->sk->sk_net; 4562a8cc6c8SYOSHIFUJI Hideaki struct ip6addrlbl_entry *p; 4572a8cc6c8SYOSHIFUJI Hideaki struct hlist_node *pos; 4582a8cc6c8SYOSHIFUJI Hideaki int idx = 0, s_idx = cb->args[0]; 4592a8cc6c8SYOSHIFUJI Hideaki int err; 4602a8cc6c8SYOSHIFUJI Hideaki 461b854272bSDenis V. Lunev if (net != &init_net) 462b854272bSDenis V. Lunev return 0; 463b854272bSDenis V. Lunev 4642a8cc6c8SYOSHIFUJI Hideaki rcu_read_lock(); 4652a8cc6c8SYOSHIFUJI Hideaki hlist_for_each_entry_rcu(p, pos, &ip6addrlbl_table.head, list) { 4662a8cc6c8SYOSHIFUJI Hideaki if (idx >= s_idx) { 4672a8cc6c8SYOSHIFUJI Hideaki if ((err = ip6addrlbl_fill(skb, p, 4682a8cc6c8SYOSHIFUJI Hideaki ip6addrlbl_table.seq, 4692a8cc6c8SYOSHIFUJI Hideaki NETLINK_CB(cb->skb).pid, 4702a8cc6c8SYOSHIFUJI Hideaki cb->nlh->nlmsg_seq, 4712a8cc6c8SYOSHIFUJI Hideaki RTM_NEWADDRLABEL, 4722a8cc6c8SYOSHIFUJI Hideaki NLM_F_MULTI)) <= 0) 4732a8cc6c8SYOSHIFUJI Hideaki break; 4742a8cc6c8SYOSHIFUJI Hideaki } 4752a8cc6c8SYOSHIFUJI Hideaki idx++; 4762a8cc6c8SYOSHIFUJI Hideaki } 4772a8cc6c8SYOSHIFUJI Hideaki rcu_read_unlock(); 4782a8cc6c8SYOSHIFUJI Hideaki cb->args[0] = idx; 4792a8cc6c8SYOSHIFUJI Hideaki return skb->len; 4802a8cc6c8SYOSHIFUJI Hideaki } 4812a8cc6c8SYOSHIFUJI Hideaki 4822a8cc6c8SYOSHIFUJI Hideaki static inline int ip6addrlbl_msgsize(void) 4832a8cc6c8SYOSHIFUJI Hideaki { 4842a8cc6c8SYOSHIFUJI Hideaki return (NLMSG_ALIGN(sizeof(struct ifaddrlblmsg)) 4852a8cc6c8SYOSHIFUJI Hideaki + nla_total_size(16) /* IFAL_ADDRESS */ 4862a8cc6c8SYOSHIFUJI Hideaki + nla_total_size(4) /* IFAL_LABEL */ 4872a8cc6c8SYOSHIFUJI Hideaki ); 4882a8cc6c8SYOSHIFUJI Hideaki } 4892a8cc6c8SYOSHIFUJI Hideaki 4902a8cc6c8SYOSHIFUJI Hideaki static int ip6addrlbl_get(struct sk_buff *in_skb, struct nlmsghdr* nlh, 4912a8cc6c8SYOSHIFUJI Hideaki void *arg) 4922a8cc6c8SYOSHIFUJI Hideaki { 493b854272bSDenis V. Lunev struct net *net = in_skb->sk->sk_net; 4942a8cc6c8SYOSHIFUJI Hideaki struct ifaddrlblmsg *ifal; 4952a8cc6c8SYOSHIFUJI Hideaki struct nlattr *tb[IFAL_MAX+1]; 4962a8cc6c8SYOSHIFUJI Hideaki struct in6_addr *addr; 4972a8cc6c8SYOSHIFUJI Hideaki u32 lseq; 4982a8cc6c8SYOSHIFUJI Hideaki int err = 0; 4992a8cc6c8SYOSHIFUJI Hideaki struct ip6addrlbl_entry *p; 5002a8cc6c8SYOSHIFUJI Hideaki struct sk_buff *skb; 5012a8cc6c8SYOSHIFUJI Hideaki 502b854272bSDenis V. Lunev if (net != &init_net) 503b854272bSDenis V. Lunev return 0; 504b854272bSDenis V. Lunev 5052a8cc6c8SYOSHIFUJI Hideaki err = nlmsg_parse(nlh, sizeof(*ifal), tb, IFAL_MAX, ifal_policy); 5062a8cc6c8SYOSHIFUJI Hideaki if (err < 0) 5072a8cc6c8SYOSHIFUJI Hideaki return err; 5082a8cc6c8SYOSHIFUJI Hideaki 5092a8cc6c8SYOSHIFUJI Hideaki ifal = nlmsg_data(nlh); 5102a8cc6c8SYOSHIFUJI Hideaki 5112a8cc6c8SYOSHIFUJI Hideaki if (ifal->ifal_family != AF_INET6 || 5122a8cc6c8SYOSHIFUJI Hideaki ifal->ifal_prefixlen != 128) 5132a8cc6c8SYOSHIFUJI Hideaki return -EINVAL; 5142a8cc6c8SYOSHIFUJI Hideaki 5152a8cc6c8SYOSHIFUJI Hideaki if (ifal->ifal_index && 5162a8cc6c8SYOSHIFUJI Hideaki !__dev_get_by_index(&init_net, ifal->ifal_index)) 5172a8cc6c8SYOSHIFUJI Hideaki return -EINVAL; 5182a8cc6c8SYOSHIFUJI Hideaki 5192a8cc6c8SYOSHIFUJI Hideaki if (!tb[IFAL_ADDRESS]) 5202a8cc6c8SYOSHIFUJI Hideaki return -EINVAL; 5212a8cc6c8SYOSHIFUJI Hideaki 5222a8cc6c8SYOSHIFUJI Hideaki addr = nla_data(tb[IFAL_ADDRESS]); 5232a8cc6c8SYOSHIFUJI Hideaki if (!addr) 5242a8cc6c8SYOSHIFUJI Hideaki return -EINVAL; 5252a8cc6c8SYOSHIFUJI Hideaki 5262a8cc6c8SYOSHIFUJI Hideaki rcu_read_lock(); 5272a8cc6c8SYOSHIFUJI Hideaki p = __ipv6_addr_label(addr, ipv6_addr_type(addr), ifal->ifal_index); 5282a8cc6c8SYOSHIFUJI Hideaki if (p && ip6addrlbl_hold(p)) 5292a8cc6c8SYOSHIFUJI Hideaki p = NULL; 5302a8cc6c8SYOSHIFUJI Hideaki lseq = ip6addrlbl_table.seq; 5312a8cc6c8SYOSHIFUJI Hideaki rcu_read_unlock(); 5322a8cc6c8SYOSHIFUJI Hideaki 5332a8cc6c8SYOSHIFUJI Hideaki if (!p) { 5342a8cc6c8SYOSHIFUJI Hideaki err = -ESRCH; 5352a8cc6c8SYOSHIFUJI Hideaki goto out; 5362a8cc6c8SYOSHIFUJI Hideaki } 5372a8cc6c8SYOSHIFUJI Hideaki 5382a8cc6c8SYOSHIFUJI Hideaki if (!(skb = nlmsg_new(ip6addrlbl_msgsize(), GFP_KERNEL))) { 5392a8cc6c8SYOSHIFUJI Hideaki ip6addrlbl_put(p); 5402a8cc6c8SYOSHIFUJI Hideaki return -ENOBUFS; 5412a8cc6c8SYOSHIFUJI Hideaki } 5422a8cc6c8SYOSHIFUJI Hideaki 5432a8cc6c8SYOSHIFUJI Hideaki err = ip6addrlbl_fill(skb, p, lseq, 5442a8cc6c8SYOSHIFUJI Hideaki NETLINK_CB(in_skb).pid, nlh->nlmsg_seq, 5452a8cc6c8SYOSHIFUJI Hideaki RTM_NEWADDRLABEL, 0); 5462a8cc6c8SYOSHIFUJI Hideaki 5472a8cc6c8SYOSHIFUJI Hideaki ip6addrlbl_put(p); 5482a8cc6c8SYOSHIFUJI Hideaki 5492a8cc6c8SYOSHIFUJI Hideaki if (err < 0) { 5502a8cc6c8SYOSHIFUJI Hideaki WARN_ON(err == -EMSGSIZE); 5512a8cc6c8SYOSHIFUJI Hideaki kfree_skb(skb); 5522a8cc6c8SYOSHIFUJI Hideaki goto out; 5532a8cc6c8SYOSHIFUJI Hideaki } 5542a8cc6c8SYOSHIFUJI Hideaki 55597c53cacSDenis V. Lunev err = rtnl_unicast(skb, &init_net, NETLINK_CB(in_skb).pid); 5562a8cc6c8SYOSHIFUJI Hideaki out: 5572a8cc6c8SYOSHIFUJI Hideaki return err; 5582a8cc6c8SYOSHIFUJI Hideaki } 5592a8cc6c8SYOSHIFUJI Hideaki 5602a8cc6c8SYOSHIFUJI Hideaki void __init ipv6_addr_label_rtnl_register(void) 5612a8cc6c8SYOSHIFUJI Hideaki { 5622a8cc6c8SYOSHIFUJI Hideaki __rtnl_register(PF_INET6, RTM_NEWADDRLABEL, ip6addrlbl_newdel, NULL); 5632a8cc6c8SYOSHIFUJI Hideaki __rtnl_register(PF_INET6, RTM_DELADDRLABEL, ip6addrlbl_newdel, NULL); 5642a8cc6c8SYOSHIFUJI Hideaki __rtnl_register(PF_INET6, RTM_GETADDRLABEL, ip6addrlbl_get, ip6addrlbl_dump); 5652a8cc6c8SYOSHIFUJI Hideaki } 5662a8cc6c8SYOSHIFUJI Hideaki 567