1b2441318SGreg Kroah-Hartman // SPDX-License-Identifier: GPL-2.0 22a8cc6c8SYOSHIFUJI Hideaki /* 32a8cc6c8SYOSHIFUJI Hideaki * IPv6 Address Label subsystem 42a8cc6c8SYOSHIFUJI Hideaki * for the IPv6 "Default" Source Address Selection 52a8cc6c8SYOSHIFUJI Hideaki * 62a8cc6c8SYOSHIFUJI Hideaki * Copyright (C)2007 USAGI/WIDE Project 72a8cc6c8SYOSHIFUJI Hideaki */ 82a8cc6c8SYOSHIFUJI Hideaki /* 92a8cc6c8SYOSHIFUJI Hideaki * Author: 102a8cc6c8SYOSHIFUJI Hideaki * YOSHIFUJI Hideaki @ USAGI/WIDE Project <yoshfuji@linux-ipv6.org> 112a8cc6c8SYOSHIFUJI Hideaki */ 122a8cc6c8SYOSHIFUJI Hideaki 132a8cc6c8SYOSHIFUJI Hideaki #include <linux/kernel.h> 142a8cc6c8SYOSHIFUJI Hideaki #include <linux/list.h> 152a8cc6c8SYOSHIFUJI Hideaki #include <linux/rcupdate.h> 162a8cc6c8SYOSHIFUJI Hideaki #include <linux/in6.h> 175a0e3ad6STejun Heo #include <linux/slab.h> 182a8cc6c8SYOSHIFUJI Hideaki #include <net/addrconf.h> 192a8cc6c8SYOSHIFUJI Hideaki #include <linux/if_addrlabel.h> 202a8cc6c8SYOSHIFUJI Hideaki #include <linux/netlink.h> 212a8cc6c8SYOSHIFUJI Hideaki #include <linux/rtnetlink.h> 222a8cc6c8SYOSHIFUJI Hideaki 232a8cc6c8SYOSHIFUJI Hideaki #if 0 242a8cc6c8SYOSHIFUJI Hideaki #define ADDRLABEL(x...) printk(x) 252a8cc6c8SYOSHIFUJI Hideaki #else 262a8cc6c8SYOSHIFUJI Hideaki #define ADDRLABEL(x...) do { ; } while (0) 272a8cc6c8SYOSHIFUJI Hideaki #endif 282a8cc6c8SYOSHIFUJI Hideaki 292a8cc6c8SYOSHIFUJI Hideaki /* 302a8cc6c8SYOSHIFUJI Hideaki * Policy Table 312a8cc6c8SYOSHIFUJI Hideaki */ 3222b285d6SWang Yufen struct ip6addrlbl_entry { 332a8cc6c8SYOSHIFUJI Hideaki struct in6_addr prefix; 342a8cc6c8SYOSHIFUJI Hideaki int prefixlen; 352a8cc6c8SYOSHIFUJI Hideaki int ifindex; 362a8cc6c8SYOSHIFUJI Hideaki int addrtype; 372a8cc6c8SYOSHIFUJI Hideaki u32 label; 382a8cc6c8SYOSHIFUJI Hideaki struct hlist_node list; 392a8cc6c8SYOSHIFUJI Hideaki struct rcu_head rcu; 402a8cc6c8SYOSHIFUJI Hideaki }; 412a8cc6c8SYOSHIFUJI Hideaki 422a8cc6c8SYOSHIFUJI Hideaki /* 43417962a0SYOSHIFUJI Hideaki / 吉藤英明 * Default policy table (RFC6724 + extensions) 442a8cc6c8SYOSHIFUJI Hideaki * 452a8cc6c8SYOSHIFUJI Hideaki * prefix addr_type label 462a8cc6c8SYOSHIFUJI Hideaki * ------------------------------------------------------------------------- 472a8cc6c8SYOSHIFUJI Hideaki * ::1/128 LOOPBACK 0 482a8cc6c8SYOSHIFUJI Hideaki * ::/0 N/A 1 492a8cc6c8SYOSHIFUJI Hideaki * 2002::/16 N/A 2 502a8cc6c8SYOSHIFUJI Hideaki * ::/96 COMPATv4 3 512a8cc6c8SYOSHIFUJI Hideaki * ::ffff:0:0/96 V4MAPPED 4 522a8cc6c8SYOSHIFUJI Hideaki * fc00::/7 N/A 5 ULA (RFC 4193) 532a8cc6c8SYOSHIFUJI Hideaki * 2001::/32 N/A 6 Teredo (RFC 4380) 545fe47b8aSJuha-Matti Tapio * 2001:10::/28 N/A 7 ORCHID (RFC 4843) 55417962a0SYOSHIFUJI Hideaki / 吉藤英明 * fec0::/10 N/A 11 Site-local 56417962a0SYOSHIFUJI Hideaki / 吉藤英明 * (deprecated by RFC3879) 57417962a0SYOSHIFUJI Hideaki / 吉藤英明 * 3ffe::/16 N/A 12 6bone 582a8cc6c8SYOSHIFUJI Hideaki * 592a8cc6c8SYOSHIFUJI Hideaki * Note: 0xffffffff is used if we do not have any policies. 60417962a0SYOSHIFUJI Hideaki / 吉藤英明 * Note: Labels for ULA and 6to4 are different from labels listed in RFC6724. 612a8cc6c8SYOSHIFUJI Hideaki */ 622a8cc6c8SYOSHIFUJI Hideaki 632a8cc6c8SYOSHIFUJI Hideaki #define IPV6_ADDR_LABEL_DEFAULT 0xffffffffUL 642a8cc6c8SYOSHIFUJI Hideaki 6504a6f82cSAndi Kleen static const __net_initconst struct ip6addrlbl_init_table 662a8cc6c8SYOSHIFUJI Hideaki { 672a8cc6c8SYOSHIFUJI Hideaki const struct in6_addr *prefix; 682a8cc6c8SYOSHIFUJI Hideaki int prefixlen; 692a8cc6c8SYOSHIFUJI Hideaki u32 label; 702a8cc6c8SYOSHIFUJI Hideaki } ip6addrlbl_init_table[] = { 712a8cc6c8SYOSHIFUJI Hideaki { /* ::/0 */ 722a8cc6c8SYOSHIFUJI Hideaki .prefix = &in6addr_any, 732a8cc6c8SYOSHIFUJI Hideaki .label = 1, 742a8cc6c8SYOSHIFUJI Hideaki }, { /* fc00::/7 */ 752a8cc6c8SYOSHIFUJI Hideaki .prefix = &(struct in6_addr){ { { 0xfc } } } , 762a8cc6c8SYOSHIFUJI Hideaki .prefixlen = 7, 772a8cc6c8SYOSHIFUJI Hideaki .label = 5, 78417962a0SYOSHIFUJI Hideaki / 吉藤英明 }, { /* fec0::/10 */ 79417962a0SYOSHIFUJI Hideaki / 吉藤英明 .prefix = &(struct in6_addr){ { { 0xfe, 0xc0 } } }, 80417962a0SYOSHIFUJI Hideaki / 吉藤英明 .prefixlen = 10, 81417962a0SYOSHIFUJI Hideaki / 吉藤英明 .label = 11, 822a8cc6c8SYOSHIFUJI Hideaki }, { /* 2002::/16 */ 832a8cc6c8SYOSHIFUJI Hideaki .prefix = &(struct in6_addr){ { { 0x20, 0x02 } } }, 842a8cc6c8SYOSHIFUJI Hideaki .prefixlen = 16, 852a8cc6c8SYOSHIFUJI Hideaki .label = 2, 86417962a0SYOSHIFUJI Hideaki / 吉藤英明 }, { /* 3ffe::/16 */ 87417962a0SYOSHIFUJI Hideaki / 吉藤英明 .prefix = &(struct in6_addr){ { { 0x3f, 0xfe } } }, 88417962a0SYOSHIFUJI Hideaki / 吉藤英明 .prefixlen = 16, 89417962a0SYOSHIFUJI Hideaki / 吉藤英明 .label = 12, 902a8cc6c8SYOSHIFUJI Hideaki }, { /* 2001::/32 */ 912a8cc6c8SYOSHIFUJI Hideaki .prefix = &(struct in6_addr){ { { 0x20, 0x01 } } }, 922a8cc6c8SYOSHIFUJI Hideaki .prefixlen = 32, 932a8cc6c8SYOSHIFUJI Hideaki .label = 6, 945fe47b8aSJuha-Matti Tapio }, { /* 2001:10::/28 */ 955fe47b8aSJuha-Matti Tapio .prefix = &(struct in6_addr){ { { 0x20, 0x01, 0x00, 0x10 } } }, 965fe47b8aSJuha-Matti Tapio .prefixlen = 28, 975fe47b8aSJuha-Matti Tapio .label = 7, 982a8cc6c8SYOSHIFUJI Hideaki }, { /* ::ffff:0:0 */ 992a8cc6c8SYOSHIFUJI Hideaki .prefix = &(struct in6_addr){ { { [10] = 0xff, [11] = 0xff } } }, 1002a8cc6c8SYOSHIFUJI Hideaki .prefixlen = 96, 1012a8cc6c8SYOSHIFUJI Hideaki .label = 4, 1022a8cc6c8SYOSHIFUJI Hideaki }, { /* ::/96 */ 1032a8cc6c8SYOSHIFUJI Hideaki .prefix = &in6addr_any, 1042a8cc6c8SYOSHIFUJI Hideaki .prefixlen = 96, 1052a8cc6c8SYOSHIFUJI Hideaki .label = 3, 1062a8cc6c8SYOSHIFUJI Hideaki }, { /* ::1/128 */ 1072a8cc6c8SYOSHIFUJI Hideaki .prefix = &in6addr_loopback, 1082a8cc6c8SYOSHIFUJI Hideaki .prefixlen = 128, 1092a8cc6c8SYOSHIFUJI Hideaki .label = 0, 1102a8cc6c8SYOSHIFUJI Hideaki } 1112a8cc6c8SYOSHIFUJI Hideaki }; 1122a8cc6c8SYOSHIFUJI Hideaki 1132a8cc6c8SYOSHIFUJI Hideaki /* Find label */ 114a90c9347SEric Dumazet static bool __ip6addrlbl_match(const struct ip6addrlbl_entry *p, 1152a8cc6c8SYOSHIFUJI Hideaki const struct in6_addr *addr, 1162a8cc6c8SYOSHIFUJI Hideaki int addrtype, int ifindex) 1172a8cc6c8SYOSHIFUJI Hideaki { 1182a8cc6c8SYOSHIFUJI Hideaki if (p->ifindex && p->ifindex != ifindex) 119a50feda5SEric Dumazet return false; 1202a8cc6c8SYOSHIFUJI Hideaki if (p->addrtype && p->addrtype != addrtype) 121a50feda5SEric Dumazet return false; 1222a8cc6c8SYOSHIFUJI Hideaki if (!ipv6_prefix_equal(addr, &p->prefix, p->prefixlen)) 123a50feda5SEric Dumazet return false; 124a50feda5SEric Dumazet return true; 1252a8cc6c8SYOSHIFUJI Hideaki } 1262a8cc6c8SYOSHIFUJI Hideaki 1273de23255SBenjamin Thery static struct ip6addrlbl_entry *__ipv6_addr_label(struct net *net, 1283de23255SBenjamin Thery const struct in6_addr *addr, 1292a8cc6c8SYOSHIFUJI Hideaki int type, int ifindex) 1302a8cc6c8SYOSHIFUJI Hideaki { 1312a8cc6c8SYOSHIFUJI Hideaki struct ip6addrlbl_entry *p; 132a90c9347SEric Dumazet 133a90c9347SEric Dumazet hlist_for_each_entry_rcu(p, &net->ipv6.ip6addrlbl_table.head, list) { 134a90c9347SEric Dumazet if (__ip6addrlbl_match(p, addr, type, ifindex)) 1352a8cc6c8SYOSHIFUJI Hideaki return p; 1362a8cc6c8SYOSHIFUJI Hideaki } 1372a8cc6c8SYOSHIFUJI Hideaki return NULL; 1382a8cc6c8SYOSHIFUJI Hideaki } 1392a8cc6c8SYOSHIFUJI Hideaki 1403de23255SBenjamin Thery u32 ipv6_addr_label(struct net *net, 1413de23255SBenjamin Thery const struct in6_addr *addr, int type, int ifindex) 1422a8cc6c8SYOSHIFUJI Hideaki { 1432a8cc6c8SYOSHIFUJI Hideaki u32 label; 1442a8cc6c8SYOSHIFUJI Hideaki struct ip6addrlbl_entry *p; 1452a8cc6c8SYOSHIFUJI Hideaki 1462a8cc6c8SYOSHIFUJI Hideaki type &= IPV6_ADDR_MAPPED | IPV6_ADDR_COMPATv4 | IPV6_ADDR_LOOPBACK; 1472a8cc6c8SYOSHIFUJI Hideaki 1482a8cc6c8SYOSHIFUJI Hideaki rcu_read_lock(); 1493de23255SBenjamin Thery p = __ipv6_addr_label(net, addr, type, ifindex); 1502a8cc6c8SYOSHIFUJI Hideaki label = p ? p->label : IPV6_ADDR_LABEL_DEFAULT; 1512a8cc6c8SYOSHIFUJI Hideaki rcu_read_unlock(); 1522a8cc6c8SYOSHIFUJI Hideaki 1535b095d98SHarvey Harrison ADDRLABEL(KERN_DEBUG "%s(addr=%pI6, type=%d, ifindex=%d) => %08x\n", 1540c6ce78aSHarvey Harrison __func__, addr, type, ifindex, label); 1552a8cc6c8SYOSHIFUJI Hideaki 1562a8cc6c8SYOSHIFUJI Hideaki return label; 1572a8cc6c8SYOSHIFUJI Hideaki } 1582a8cc6c8SYOSHIFUJI Hideaki 1592a8cc6c8SYOSHIFUJI Hideaki /* allocate one entry */ 160a90c9347SEric Dumazet static struct ip6addrlbl_entry *ip6addrlbl_alloc(const struct in6_addr *prefix, 1612a8cc6c8SYOSHIFUJI Hideaki int prefixlen, int ifindex, 1622a8cc6c8SYOSHIFUJI Hideaki u32 label) 1632a8cc6c8SYOSHIFUJI Hideaki { 1642a8cc6c8SYOSHIFUJI Hideaki struct ip6addrlbl_entry *newp; 1652a8cc6c8SYOSHIFUJI Hideaki int addrtype; 1662a8cc6c8SYOSHIFUJI Hideaki 1675b095d98SHarvey Harrison ADDRLABEL(KERN_DEBUG "%s(prefix=%pI6, prefixlen=%d, ifindex=%d, label=%u)\n", 1680c6ce78aSHarvey Harrison __func__, prefix, prefixlen, ifindex, (unsigned int)label); 1692a8cc6c8SYOSHIFUJI Hideaki 1702a8cc6c8SYOSHIFUJI Hideaki addrtype = ipv6_addr_type(prefix) & (IPV6_ADDR_MAPPED | IPV6_ADDR_COMPATv4 | IPV6_ADDR_LOOPBACK); 1712a8cc6c8SYOSHIFUJI Hideaki 1722a8cc6c8SYOSHIFUJI Hideaki switch (addrtype) { 1732a8cc6c8SYOSHIFUJI Hideaki case IPV6_ADDR_MAPPED: 1742a8cc6c8SYOSHIFUJI Hideaki if (prefixlen > 96) 1752a8cc6c8SYOSHIFUJI Hideaki return ERR_PTR(-EINVAL); 1762a8cc6c8SYOSHIFUJI Hideaki if (prefixlen < 96) 1772a8cc6c8SYOSHIFUJI Hideaki addrtype = 0; 1782a8cc6c8SYOSHIFUJI Hideaki break; 1792a8cc6c8SYOSHIFUJI Hideaki case IPV6_ADDR_COMPATv4: 1802a8cc6c8SYOSHIFUJI Hideaki if (prefixlen != 96) 1812a8cc6c8SYOSHIFUJI Hideaki addrtype = 0; 1822a8cc6c8SYOSHIFUJI Hideaki break; 1832a8cc6c8SYOSHIFUJI Hideaki case IPV6_ADDR_LOOPBACK: 1842a8cc6c8SYOSHIFUJI Hideaki if (prefixlen != 128) 1852a8cc6c8SYOSHIFUJI Hideaki addrtype = 0; 1862a8cc6c8SYOSHIFUJI Hideaki break; 1872a8cc6c8SYOSHIFUJI Hideaki } 1882a8cc6c8SYOSHIFUJI Hideaki 1892a8cc6c8SYOSHIFUJI Hideaki newp = kmalloc(sizeof(*newp), GFP_KERNEL); 1902a8cc6c8SYOSHIFUJI Hideaki if (!newp) 1912a8cc6c8SYOSHIFUJI Hideaki return ERR_PTR(-ENOMEM); 1922a8cc6c8SYOSHIFUJI Hideaki 1932a8cc6c8SYOSHIFUJI Hideaki ipv6_addr_prefix(&newp->prefix, prefix, prefixlen); 1942a8cc6c8SYOSHIFUJI Hideaki newp->prefixlen = prefixlen; 1952a8cc6c8SYOSHIFUJI Hideaki newp->ifindex = ifindex; 1962a8cc6c8SYOSHIFUJI Hideaki newp->addrtype = addrtype; 1972a8cc6c8SYOSHIFUJI Hideaki newp->label = label; 1982a8cc6c8SYOSHIFUJI Hideaki INIT_HLIST_NODE(&newp->list); 1992a8cc6c8SYOSHIFUJI Hideaki return newp; 2002a8cc6c8SYOSHIFUJI Hideaki } 2012a8cc6c8SYOSHIFUJI Hideaki 2022a8cc6c8SYOSHIFUJI Hideaki /* add a label */ 203a90c9347SEric Dumazet static int __ip6addrlbl_add(struct net *net, struct ip6addrlbl_entry *newp, 204a90c9347SEric Dumazet int replace) 2052a8cc6c8SYOSHIFUJI Hideaki { 206639739b5SHannes Frederic Sowa struct ip6addrlbl_entry *last = NULL, *p = NULL; 207a90c9347SEric Dumazet struct hlist_node *n; 2082a8cc6c8SYOSHIFUJI Hideaki int ret = 0; 2092a8cc6c8SYOSHIFUJI Hideaki 210639739b5SHannes Frederic Sowa ADDRLABEL(KERN_DEBUG "%s(newp=%p, replace=%d)\n", __func__, newp, 211639739b5SHannes Frederic Sowa replace); 2122a8cc6c8SYOSHIFUJI Hideaki 213a90c9347SEric Dumazet hlist_for_each_entry_safe(p, n, &net->ipv6.ip6addrlbl_table.head, list) { 2142a8cc6c8SYOSHIFUJI Hideaki if (p->prefixlen == newp->prefixlen && 2152a8cc6c8SYOSHIFUJI Hideaki p->ifindex == newp->ifindex && 2162a8cc6c8SYOSHIFUJI Hideaki ipv6_addr_equal(&p->prefix, &newp->prefix)) { 2172a8cc6c8SYOSHIFUJI Hideaki if (!replace) { 2182a8cc6c8SYOSHIFUJI Hideaki ret = -EEXIST; 2192a8cc6c8SYOSHIFUJI Hideaki goto out; 2202a8cc6c8SYOSHIFUJI Hideaki } 2212a8cc6c8SYOSHIFUJI Hideaki hlist_replace_rcu(&p->list, &newp->list); 2222809c095SEric Dumazet kfree_rcu(p, rcu); 2232a8cc6c8SYOSHIFUJI Hideaki goto out; 2242a8cc6c8SYOSHIFUJI Hideaki } else if ((p->prefixlen == newp->prefixlen && !p->ifindex) || 2252a8cc6c8SYOSHIFUJI Hideaki (p->prefixlen < newp->prefixlen)) { 2262a8cc6c8SYOSHIFUJI Hideaki hlist_add_before_rcu(&newp->list, &p->list); 2272a8cc6c8SYOSHIFUJI Hideaki goto out; 2282a8cc6c8SYOSHIFUJI Hideaki } 229639739b5SHannes Frederic Sowa last = p; 2302a8cc6c8SYOSHIFUJI Hideaki } 231639739b5SHannes Frederic Sowa if (last) 2321d023284SKen Helias hlist_add_behind_rcu(&newp->list, &last->list); 233639739b5SHannes Frederic Sowa else 234a90c9347SEric Dumazet hlist_add_head_rcu(&newp->list, &net->ipv6.ip6addrlbl_table.head); 2352a8cc6c8SYOSHIFUJI Hideaki out: 2362a8cc6c8SYOSHIFUJI Hideaki if (!ret) 237a90c9347SEric Dumazet net->ipv6.ip6addrlbl_table.seq++; 2382a8cc6c8SYOSHIFUJI Hideaki return ret; 2392a8cc6c8SYOSHIFUJI Hideaki } 2402a8cc6c8SYOSHIFUJI Hideaki 2412a8cc6c8SYOSHIFUJI Hideaki /* add a label */ 2423de23255SBenjamin Thery static int ip6addrlbl_add(struct net *net, 2433de23255SBenjamin Thery const struct in6_addr *prefix, int prefixlen, 2442a8cc6c8SYOSHIFUJI Hideaki int ifindex, u32 label, int replace) 2452a8cc6c8SYOSHIFUJI Hideaki { 2462a8cc6c8SYOSHIFUJI Hideaki struct ip6addrlbl_entry *newp; 2472a8cc6c8SYOSHIFUJI Hideaki int ret = 0; 2482a8cc6c8SYOSHIFUJI Hideaki 2495b095d98SHarvey Harrison ADDRLABEL(KERN_DEBUG "%s(prefix=%pI6, prefixlen=%d, ifindex=%d, label=%u, replace=%d)\n", 2500c6ce78aSHarvey Harrison __func__, prefix, prefixlen, ifindex, (unsigned int)label, 2512a8cc6c8SYOSHIFUJI Hideaki replace); 2522a8cc6c8SYOSHIFUJI Hideaki 253a90c9347SEric Dumazet newp = ip6addrlbl_alloc(prefix, prefixlen, ifindex, label); 2542a8cc6c8SYOSHIFUJI Hideaki if (IS_ERR(newp)) 2552a8cc6c8SYOSHIFUJI Hideaki return PTR_ERR(newp); 256a90c9347SEric Dumazet spin_lock(&net->ipv6.ip6addrlbl_table.lock); 257a90c9347SEric Dumazet ret = __ip6addrlbl_add(net, newp, replace); 258a90c9347SEric Dumazet spin_unlock(&net->ipv6.ip6addrlbl_table.lock); 2592a8cc6c8SYOSHIFUJI Hideaki if (ret) 2602809c095SEric Dumazet kfree(newp); 2612a8cc6c8SYOSHIFUJI Hideaki return ret; 2622a8cc6c8SYOSHIFUJI Hideaki } 2632a8cc6c8SYOSHIFUJI Hideaki 2642a8cc6c8SYOSHIFUJI Hideaki /* remove a label */ 2653de23255SBenjamin Thery static int __ip6addrlbl_del(struct net *net, 2663de23255SBenjamin Thery const struct in6_addr *prefix, int prefixlen, 2672a8cc6c8SYOSHIFUJI Hideaki int ifindex) 2682a8cc6c8SYOSHIFUJI Hideaki { 2692a8cc6c8SYOSHIFUJI Hideaki struct ip6addrlbl_entry *p = NULL; 270b67bfe0dSSasha Levin struct hlist_node *n; 2712a8cc6c8SYOSHIFUJI Hideaki int ret = -ESRCH; 2722a8cc6c8SYOSHIFUJI Hideaki 2735b095d98SHarvey Harrison ADDRLABEL(KERN_DEBUG "%s(prefix=%pI6, prefixlen=%d, ifindex=%d)\n", 2740c6ce78aSHarvey Harrison __func__, prefix, prefixlen, ifindex); 2752a8cc6c8SYOSHIFUJI Hideaki 276a90c9347SEric Dumazet hlist_for_each_entry_safe(p, n, &net->ipv6.ip6addrlbl_table.head, list) { 2772a8cc6c8SYOSHIFUJI Hideaki if (p->prefixlen == prefixlen && 2782a8cc6c8SYOSHIFUJI Hideaki p->ifindex == ifindex && 2792a8cc6c8SYOSHIFUJI Hideaki ipv6_addr_equal(&p->prefix, prefix)) { 2802a8cc6c8SYOSHIFUJI Hideaki hlist_del_rcu(&p->list); 2812809c095SEric Dumazet kfree_rcu(p, rcu); 2822a8cc6c8SYOSHIFUJI Hideaki ret = 0; 2832a8cc6c8SYOSHIFUJI Hideaki break; 2842a8cc6c8SYOSHIFUJI Hideaki } 2852a8cc6c8SYOSHIFUJI Hideaki } 2862a8cc6c8SYOSHIFUJI Hideaki return ret; 2872a8cc6c8SYOSHIFUJI Hideaki } 2882a8cc6c8SYOSHIFUJI Hideaki 2893de23255SBenjamin Thery static int ip6addrlbl_del(struct net *net, 2903de23255SBenjamin Thery const struct in6_addr *prefix, int prefixlen, 2912a8cc6c8SYOSHIFUJI Hideaki int ifindex) 2922a8cc6c8SYOSHIFUJI Hideaki { 2932a8cc6c8SYOSHIFUJI Hideaki struct in6_addr prefix_buf; 2942a8cc6c8SYOSHIFUJI Hideaki int ret; 2952a8cc6c8SYOSHIFUJI Hideaki 2965b095d98SHarvey Harrison ADDRLABEL(KERN_DEBUG "%s(prefix=%pI6, prefixlen=%d, ifindex=%d)\n", 2970c6ce78aSHarvey Harrison __func__, prefix, prefixlen, ifindex); 2982a8cc6c8SYOSHIFUJI Hideaki 2992a8cc6c8SYOSHIFUJI Hideaki ipv6_addr_prefix(&prefix_buf, prefix, prefixlen); 300a90c9347SEric Dumazet spin_lock(&net->ipv6.ip6addrlbl_table.lock); 3013de23255SBenjamin Thery ret = __ip6addrlbl_del(net, &prefix_buf, prefixlen, ifindex); 302a90c9347SEric Dumazet spin_unlock(&net->ipv6.ip6addrlbl_table.lock); 3032a8cc6c8SYOSHIFUJI Hideaki return ret; 3042a8cc6c8SYOSHIFUJI Hideaki } 3052a8cc6c8SYOSHIFUJI Hideaki 3062a8cc6c8SYOSHIFUJI Hideaki /* add default label */ 3073de23255SBenjamin Thery static int __net_init ip6addrlbl_net_init(struct net *net) 3082a8cc6c8SYOSHIFUJI Hideaki { 3092a8cc6c8SYOSHIFUJI Hideaki int err = 0; 3102a8cc6c8SYOSHIFUJI Hideaki int i; 3112a8cc6c8SYOSHIFUJI Hideaki 312f3213831SJoe Perches ADDRLABEL(KERN_DEBUG "%s\n", __func__); 3132a8cc6c8SYOSHIFUJI Hideaki 314a90c9347SEric Dumazet spin_lock_init(&net->ipv6.ip6addrlbl_table.lock); 315a90c9347SEric Dumazet INIT_HLIST_HEAD(&net->ipv6.ip6addrlbl_table.head); 316a90c9347SEric Dumazet 3172a8cc6c8SYOSHIFUJI Hideaki for (i = 0; i < ARRAY_SIZE(ip6addrlbl_init_table); i++) { 3183de23255SBenjamin Thery int ret = ip6addrlbl_add(net, 3193de23255SBenjamin Thery ip6addrlbl_init_table[i].prefix, 3202a8cc6c8SYOSHIFUJI Hideaki ip6addrlbl_init_table[i].prefixlen, 3212a8cc6c8SYOSHIFUJI Hideaki 0, 3222a8cc6c8SYOSHIFUJI Hideaki ip6addrlbl_init_table[i].label, 0); 3232a8cc6c8SYOSHIFUJI Hideaki /* XXX: should we free all rules when we catch an error? */ 3242a8cc6c8SYOSHIFUJI Hideaki if (ret && (!err || err != -ENOMEM)) 3252a8cc6c8SYOSHIFUJI Hideaki err = ret; 3262a8cc6c8SYOSHIFUJI Hideaki } 3272a8cc6c8SYOSHIFUJI Hideaki return err; 3282a8cc6c8SYOSHIFUJI Hideaki } 3292a8cc6c8SYOSHIFUJI Hideaki 3303de23255SBenjamin Thery static void __net_exit ip6addrlbl_net_exit(struct net *net) 3313de23255SBenjamin Thery { 3323de23255SBenjamin Thery struct ip6addrlbl_entry *p = NULL; 333b67bfe0dSSasha Levin struct hlist_node *n; 3343de23255SBenjamin Thery 3353de23255SBenjamin Thery /* Remove all labels belonging to the exiting net */ 336a90c9347SEric Dumazet spin_lock(&net->ipv6.ip6addrlbl_table.lock); 337a90c9347SEric Dumazet hlist_for_each_entry_safe(p, n, &net->ipv6.ip6addrlbl_table.head, list) { 3383de23255SBenjamin Thery hlist_del_rcu(&p->list); 3392809c095SEric Dumazet kfree_rcu(p, rcu); 3403de23255SBenjamin Thery } 341a90c9347SEric Dumazet spin_unlock(&net->ipv6.ip6addrlbl_table.lock); 3423de23255SBenjamin Thery } 3433de23255SBenjamin Thery 3443de23255SBenjamin Thery static struct pernet_operations ipv6_addr_label_ops = { 3453de23255SBenjamin Thery .init = ip6addrlbl_net_init, 3463de23255SBenjamin Thery .exit = ip6addrlbl_net_exit, 3473de23255SBenjamin Thery }; 3483de23255SBenjamin Thery 3492a8cc6c8SYOSHIFUJI Hideaki int __init ipv6_addr_label_init(void) 3502a8cc6c8SYOSHIFUJI Hideaki { 3513de23255SBenjamin Thery return register_pernet_subsys(&ipv6_addr_label_ops); 3522a8cc6c8SYOSHIFUJI Hideaki } 3532a8cc6c8SYOSHIFUJI Hideaki 3542cc6d2bfSNeil Horman void ipv6_addr_label_cleanup(void) 3552cc6d2bfSNeil Horman { 3562cc6d2bfSNeil Horman unregister_pernet_subsys(&ipv6_addr_label_ops); 3572cc6d2bfSNeil Horman } 3582cc6d2bfSNeil Horman 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 364a6f57028SFlorian Westphal static bool addrlbl_ifindex_exists(struct net *net, int ifindex) 365a6f57028SFlorian Westphal { 366a6f57028SFlorian Westphal 367a6f57028SFlorian Westphal struct net_device *dev; 368a6f57028SFlorian Westphal 369a6f57028SFlorian Westphal rcu_read_lock(); 370a6f57028SFlorian Westphal dev = dev_get_by_index_rcu(net, ifindex); 371a6f57028SFlorian Westphal rcu_read_unlock(); 372a6f57028SFlorian Westphal 373a6f57028SFlorian Westphal return dev != NULL; 374a6f57028SFlorian Westphal } 375a6f57028SFlorian Westphal 376c21ef3e3SDavid Ahern static int ip6addrlbl_newdel(struct sk_buff *skb, struct nlmsghdr *nlh, 377c21ef3e3SDavid Ahern struct netlink_ext_ack *extack) 3782a8cc6c8SYOSHIFUJI Hideaki { 3793b1e0a65SYOSHIFUJI Hideaki struct net *net = sock_net(skb->sk); 3802a8cc6c8SYOSHIFUJI Hideaki struct ifaddrlblmsg *ifal; 3812a8cc6c8SYOSHIFUJI Hideaki struct nlattr *tb[IFAL_MAX+1]; 3822a8cc6c8SYOSHIFUJI Hideaki struct in6_addr *pfx; 3832a8cc6c8SYOSHIFUJI Hideaki u32 label; 3842a8cc6c8SYOSHIFUJI Hideaki int err = 0; 3852a8cc6c8SYOSHIFUJI Hideaki 386c21ef3e3SDavid Ahern err = nlmsg_parse(nlh, sizeof(*ifal), tb, IFAL_MAX, ifal_policy, 387c21ef3e3SDavid Ahern extack); 3882a8cc6c8SYOSHIFUJI Hideaki if (err < 0) 3892a8cc6c8SYOSHIFUJI Hideaki return err; 3902a8cc6c8SYOSHIFUJI Hideaki 3912a8cc6c8SYOSHIFUJI Hideaki ifal = nlmsg_data(nlh); 3922a8cc6c8SYOSHIFUJI Hideaki 3932a8cc6c8SYOSHIFUJI Hideaki if (ifal->ifal_family != AF_INET6 || 3942a8cc6c8SYOSHIFUJI Hideaki ifal->ifal_prefixlen > 128) 3952a8cc6c8SYOSHIFUJI Hideaki return -EINVAL; 3962a8cc6c8SYOSHIFUJI Hideaki 3972a8cc6c8SYOSHIFUJI Hideaki if (!tb[IFAL_ADDRESS]) 3982a8cc6c8SYOSHIFUJI Hideaki return -EINVAL; 3992a8cc6c8SYOSHIFUJI Hideaki pfx = nla_data(tb[IFAL_ADDRESS]); 4002a8cc6c8SYOSHIFUJI Hideaki 4012a8cc6c8SYOSHIFUJI Hideaki if (!tb[IFAL_LABEL]) 4022a8cc6c8SYOSHIFUJI Hideaki return -EINVAL; 4032a8cc6c8SYOSHIFUJI Hideaki label = nla_get_u32(tb[IFAL_LABEL]); 4042a8cc6c8SYOSHIFUJI Hideaki if (label == IPV6_ADDR_LABEL_DEFAULT) 4052a8cc6c8SYOSHIFUJI Hideaki return -EINVAL; 4062a8cc6c8SYOSHIFUJI Hideaki 4072a8cc6c8SYOSHIFUJI Hideaki switch (nlh->nlmsg_type) { 4082a8cc6c8SYOSHIFUJI Hideaki case RTM_NEWADDRLABEL: 4090771275bSFlorian Westphal if (ifal->ifal_index && 410a6f57028SFlorian Westphal !addrlbl_ifindex_exists(net, ifal->ifal_index)) 4110771275bSFlorian Westphal return -EINVAL; 4120771275bSFlorian Westphal 4133de23255SBenjamin Thery err = ip6addrlbl_add(net, pfx, ifal->ifal_prefixlen, 4142a8cc6c8SYOSHIFUJI Hideaki ifal->ifal_index, label, 4152a8cc6c8SYOSHIFUJI Hideaki nlh->nlmsg_flags & NLM_F_REPLACE); 4162a8cc6c8SYOSHIFUJI Hideaki break; 4172a8cc6c8SYOSHIFUJI Hideaki case RTM_DELADDRLABEL: 4183de23255SBenjamin Thery err = ip6addrlbl_del(net, pfx, ifal->ifal_prefixlen, 4192a8cc6c8SYOSHIFUJI Hideaki ifal->ifal_index); 4202a8cc6c8SYOSHIFUJI Hideaki break; 4212a8cc6c8SYOSHIFUJI Hideaki default: 4222a8cc6c8SYOSHIFUJI Hideaki err = -EOPNOTSUPP; 4232a8cc6c8SYOSHIFUJI Hideaki } 4242a8cc6c8SYOSHIFUJI Hideaki return err; 4252a8cc6c8SYOSHIFUJI Hideaki } 4262a8cc6c8SYOSHIFUJI Hideaki 427a50feda5SEric Dumazet static void ip6addrlbl_putmsg(struct nlmsghdr *nlh, 4282a8cc6c8SYOSHIFUJI Hideaki int prefixlen, int ifindex, u32 lseq) 4292a8cc6c8SYOSHIFUJI Hideaki { 4302a8cc6c8SYOSHIFUJI Hideaki struct ifaddrlblmsg *ifal = nlmsg_data(nlh); 4312a8cc6c8SYOSHIFUJI Hideaki ifal->ifal_family = AF_INET6; 4322a8cc6c8SYOSHIFUJI Hideaki ifal->ifal_prefixlen = prefixlen; 4332a8cc6c8SYOSHIFUJI Hideaki ifal->ifal_flags = 0; 4342a8cc6c8SYOSHIFUJI Hideaki ifal->ifal_index = ifindex; 4352a8cc6c8SYOSHIFUJI Hideaki ifal->ifal_seq = lseq; 4362a8cc6c8SYOSHIFUJI Hideaki }; 4372a8cc6c8SYOSHIFUJI Hideaki 4382a8cc6c8SYOSHIFUJI Hideaki static int ip6addrlbl_fill(struct sk_buff *skb, 4392a8cc6c8SYOSHIFUJI Hideaki struct ip6addrlbl_entry *p, 4402a8cc6c8SYOSHIFUJI Hideaki u32 lseq, 44115e47304SEric W. Biederman u32 portid, u32 seq, int event, 4422a8cc6c8SYOSHIFUJI Hideaki unsigned int flags) 4432a8cc6c8SYOSHIFUJI Hideaki { 44415e47304SEric W. Biederman struct nlmsghdr *nlh = nlmsg_put(skb, portid, seq, event, 4452a8cc6c8SYOSHIFUJI Hideaki sizeof(struct ifaddrlblmsg), flags); 4462a8cc6c8SYOSHIFUJI Hideaki if (!nlh) 4472a8cc6c8SYOSHIFUJI Hideaki return -EMSGSIZE; 4482a8cc6c8SYOSHIFUJI Hideaki 4492a8cc6c8SYOSHIFUJI Hideaki ip6addrlbl_putmsg(nlh, p->prefixlen, p->ifindex, lseq); 4502a8cc6c8SYOSHIFUJI Hideaki 451930345eaSJiri Benc if (nla_put_in6_addr(skb, IFAL_ADDRESS, &p->prefix) < 0 || 4522a8cc6c8SYOSHIFUJI Hideaki nla_put_u32(skb, IFAL_LABEL, p->label) < 0) { 4532a8cc6c8SYOSHIFUJI Hideaki nlmsg_cancel(skb, nlh); 4542a8cc6c8SYOSHIFUJI Hideaki return -EMSGSIZE; 4552a8cc6c8SYOSHIFUJI Hideaki } 4562a8cc6c8SYOSHIFUJI Hideaki 457053c095aSJohannes Berg nlmsg_end(skb, nlh); 458053c095aSJohannes Berg return 0; 4592a8cc6c8SYOSHIFUJI Hideaki } 4602a8cc6c8SYOSHIFUJI Hideaki 461f2ae64bbSDavid Ahern static int ip6addrlbl_valid_dump_req(const struct nlmsghdr *nlh, 462f2ae64bbSDavid Ahern struct netlink_ext_ack *extack) 463f2ae64bbSDavid Ahern { 464f2ae64bbSDavid Ahern struct ifaddrlblmsg *ifal; 465f2ae64bbSDavid Ahern 466f2ae64bbSDavid Ahern if (nlh->nlmsg_len < nlmsg_msg_size(sizeof(*ifal))) { 467f2ae64bbSDavid Ahern NL_SET_ERR_MSG_MOD(extack, "Invalid header for address label dump request"); 468f2ae64bbSDavid Ahern return -EINVAL; 469f2ae64bbSDavid Ahern } 470f2ae64bbSDavid Ahern 471f2ae64bbSDavid Ahern ifal = nlmsg_data(nlh); 472f2ae64bbSDavid Ahern if (ifal->__ifal_reserved || ifal->ifal_prefixlen || 473f2ae64bbSDavid Ahern ifal->ifal_flags || ifal->ifal_index || ifal->ifal_seq) { 474f2ae64bbSDavid Ahern NL_SET_ERR_MSG_MOD(extack, "Invalid values in header for address label dump request"); 475f2ae64bbSDavid Ahern return -EINVAL; 476f2ae64bbSDavid Ahern } 477f2ae64bbSDavid Ahern 478f2ae64bbSDavid Ahern if (nlmsg_attrlen(nlh, sizeof(*ifal))) { 479f2ae64bbSDavid Ahern NL_SET_ERR_MSG_MOD(extack, "Invalid data after header for address label dump requewst"); 480f2ae64bbSDavid Ahern return -EINVAL; 481f2ae64bbSDavid Ahern } 482f2ae64bbSDavid Ahern 483f2ae64bbSDavid Ahern return 0; 484f2ae64bbSDavid Ahern } 485f2ae64bbSDavid Ahern 4862a8cc6c8SYOSHIFUJI Hideaki static int ip6addrlbl_dump(struct sk_buff *skb, struct netlink_callback *cb) 4872a8cc6c8SYOSHIFUJI Hideaki { 488f2ae64bbSDavid Ahern const struct nlmsghdr *nlh = cb->nlh; 4893b1e0a65SYOSHIFUJI Hideaki struct net *net = sock_net(skb->sk); 4902a8cc6c8SYOSHIFUJI Hideaki struct ip6addrlbl_entry *p; 4912a8cc6c8SYOSHIFUJI Hideaki int idx = 0, s_idx = cb->args[0]; 4922a8cc6c8SYOSHIFUJI Hideaki int err; 4932a8cc6c8SYOSHIFUJI Hideaki 494f2ae64bbSDavid Ahern if (cb->strict_check) { 495f2ae64bbSDavid Ahern err = ip6addrlbl_valid_dump_req(nlh, cb->extack); 496f2ae64bbSDavid Ahern if (err < 0) 497f2ae64bbSDavid Ahern return err; 498f2ae64bbSDavid Ahern } 499f2ae64bbSDavid Ahern 5002a8cc6c8SYOSHIFUJI Hideaki rcu_read_lock(); 501a90c9347SEric Dumazet hlist_for_each_entry_rcu(p, &net->ipv6.ip6addrlbl_table.head, list) { 502a90c9347SEric Dumazet if (idx >= s_idx) { 503cb6e926eSWang Yufen err = ip6addrlbl_fill(skb, p, 504a90c9347SEric Dumazet net->ipv6.ip6addrlbl_table.seq, 50515e47304SEric W. Biederman NETLINK_CB(cb->skb).portid, 506f2ae64bbSDavid Ahern nlh->nlmsg_seq, 5072a8cc6c8SYOSHIFUJI Hideaki RTM_NEWADDRLABEL, 508cb6e926eSWang Yufen NLM_F_MULTI); 509053c095aSJohannes Berg if (err < 0) 5102a8cc6c8SYOSHIFUJI Hideaki break; 5112a8cc6c8SYOSHIFUJI Hideaki } 5122a8cc6c8SYOSHIFUJI Hideaki idx++; 5132a8cc6c8SYOSHIFUJI Hideaki } 5142a8cc6c8SYOSHIFUJI Hideaki rcu_read_unlock(); 5152a8cc6c8SYOSHIFUJI Hideaki cb->args[0] = idx; 5162a8cc6c8SYOSHIFUJI Hideaki return skb->len; 5172a8cc6c8SYOSHIFUJI Hideaki } 5182a8cc6c8SYOSHIFUJI Hideaki 5192a8cc6c8SYOSHIFUJI Hideaki static inline int ip6addrlbl_msgsize(void) 5202a8cc6c8SYOSHIFUJI Hideaki { 521a02cec21SEric Dumazet return NLMSG_ALIGN(sizeof(struct ifaddrlblmsg)) 5222a8cc6c8SYOSHIFUJI Hideaki + nla_total_size(16) /* IFAL_ADDRESS */ 523a02cec21SEric Dumazet + nla_total_size(4); /* IFAL_LABEL */ 5242a8cc6c8SYOSHIFUJI Hideaki } 5252a8cc6c8SYOSHIFUJI Hideaki 526c21ef3e3SDavid Ahern static int ip6addrlbl_get(struct sk_buff *in_skb, struct nlmsghdr *nlh, 527c21ef3e3SDavid Ahern struct netlink_ext_ack *extack) 5282a8cc6c8SYOSHIFUJI Hideaki { 5293b1e0a65SYOSHIFUJI Hideaki struct net *net = sock_net(in_skb->sk); 5302a8cc6c8SYOSHIFUJI Hideaki struct ifaddrlblmsg *ifal; 5312a8cc6c8SYOSHIFUJI Hideaki struct nlattr *tb[IFAL_MAX+1]; 5322a8cc6c8SYOSHIFUJI Hideaki struct in6_addr *addr; 5332a8cc6c8SYOSHIFUJI Hideaki u32 lseq; 5342a8cc6c8SYOSHIFUJI Hideaki int err = 0; 5352a8cc6c8SYOSHIFUJI Hideaki struct ip6addrlbl_entry *p; 5362a8cc6c8SYOSHIFUJI Hideaki struct sk_buff *skb; 5372a8cc6c8SYOSHIFUJI Hideaki 538c21ef3e3SDavid Ahern err = nlmsg_parse(nlh, sizeof(*ifal), tb, IFAL_MAX, ifal_policy, 539c21ef3e3SDavid Ahern extack); 5402a8cc6c8SYOSHIFUJI Hideaki if (err < 0) 5412a8cc6c8SYOSHIFUJI Hideaki return err; 5422a8cc6c8SYOSHIFUJI Hideaki 5432a8cc6c8SYOSHIFUJI Hideaki ifal = nlmsg_data(nlh); 5442a8cc6c8SYOSHIFUJI Hideaki 5452a8cc6c8SYOSHIFUJI Hideaki if (ifal->ifal_family != AF_INET6 || 5462a8cc6c8SYOSHIFUJI Hideaki ifal->ifal_prefixlen != 128) 5472a8cc6c8SYOSHIFUJI Hideaki return -EINVAL; 5482a8cc6c8SYOSHIFUJI Hideaki 5492a8cc6c8SYOSHIFUJI Hideaki if (ifal->ifal_index && 550a6f57028SFlorian Westphal !addrlbl_ifindex_exists(net, ifal->ifal_index)) 5512a8cc6c8SYOSHIFUJI Hideaki return -EINVAL; 5522a8cc6c8SYOSHIFUJI Hideaki 5532a8cc6c8SYOSHIFUJI Hideaki if (!tb[IFAL_ADDRESS]) 5542a8cc6c8SYOSHIFUJI Hideaki return -EINVAL; 5552a8cc6c8SYOSHIFUJI Hideaki addr = nla_data(tb[IFAL_ADDRESS]); 5562a8cc6c8SYOSHIFUJI Hideaki 55766c77ff3SEric Dumazet skb = nlmsg_new(ip6addrlbl_msgsize(), GFP_KERNEL); 55866c77ff3SEric Dumazet if (!skb) 55966c77ff3SEric Dumazet return -ENOBUFS; 56066c77ff3SEric Dumazet 56166c77ff3SEric Dumazet err = -ESRCH; 56266c77ff3SEric Dumazet 5632a8cc6c8SYOSHIFUJI Hideaki rcu_read_lock(); 5643de23255SBenjamin Thery p = __ipv6_addr_label(net, addr, ipv6_addr_type(addr), ifal->ifal_index); 565a90c9347SEric Dumazet lseq = net->ipv6.ip6addrlbl_table.seq; 56666c77ff3SEric Dumazet if (p) 5672a8cc6c8SYOSHIFUJI Hideaki err = ip6addrlbl_fill(skb, p, lseq, 56866c77ff3SEric Dumazet NETLINK_CB(in_skb).portid, 56966c77ff3SEric Dumazet nlh->nlmsg_seq, 5702a8cc6c8SYOSHIFUJI Hideaki RTM_NEWADDRLABEL, 0); 57166c77ff3SEric Dumazet rcu_read_unlock(); 5722a8cc6c8SYOSHIFUJI Hideaki 5732a8cc6c8SYOSHIFUJI Hideaki if (err < 0) { 5742a8cc6c8SYOSHIFUJI Hideaki WARN_ON(err == -EMSGSIZE); 5752a8cc6c8SYOSHIFUJI Hideaki kfree_skb(skb); 57666c77ff3SEric Dumazet } else { 57715e47304SEric W. Biederman err = rtnl_unicast(skb, net, NETLINK_CB(in_skb).portid); 57866c77ff3SEric Dumazet } 5792a8cc6c8SYOSHIFUJI Hideaki return err; 5802a8cc6c8SYOSHIFUJI Hideaki } 5812a8cc6c8SYOSHIFUJI Hideaki 582a3fde2adSFlorian Westphal int __init ipv6_addr_label_rtnl_register(void) 5832a8cc6c8SYOSHIFUJI Hideaki { 584a3fde2adSFlorian Westphal int ret; 585a3fde2adSFlorian Westphal 586a3fde2adSFlorian Westphal ret = rtnl_register_module(THIS_MODULE, PF_INET6, RTM_NEWADDRLABEL, 587a3fde2adSFlorian Westphal ip6addrlbl_newdel, 588a6f57028SFlorian Westphal NULL, RTNL_FLAG_DOIT_UNLOCKED); 589a3fde2adSFlorian Westphal if (ret < 0) 590a3fde2adSFlorian Westphal return ret; 591a3fde2adSFlorian Westphal ret = rtnl_register_module(THIS_MODULE, PF_INET6, RTM_DELADDRLABEL, 592a3fde2adSFlorian Westphal ip6addrlbl_newdel, 593a6f57028SFlorian Westphal NULL, RTNL_FLAG_DOIT_UNLOCKED); 594a3fde2adSFlorian Westphal if (ret < 0) 595a3fde2adSFlorian Westphal return ret; 596a3fde2adSFlorian Westphal ret = rtnl_register_module(THIS_MODULE, PF_INET6, RTM_GETADDRLABEL, 597a3fde2adSFlorian Westphal ip6addrlbl_get, 598a6f57028SFlorian Westphal ip6addrlbl_dump, RTNL_FLAG_DOIT_UNLOCKED); 599a3fde2adSFlorian Westphal return ret; 6002a8cc6c8SYOSHIFUJI Hideaki } 601