xref: /openbmc/linux/net/ipv6/addrlabel.c (revision 2acb2779)
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 */
__ip6addrlbl_match(const struct ip6addrlbl_entry * p,const struct in6_addr * addr,int addrtype,int ifindex)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 
__ipv6_addr_label(struct net * net,const struct in6_addr * addr,int type,int ifindex)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 
ipv6_addr_label(struct net * net,const struct in6_addr * addr,int type,int ifindex)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 */
ip6addrlbl_alloc(const struct in6_addr * prefix,int prefixlen,int ifindex,u32 label)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 */
__ip6addrlbl_add(struct net * net,struct ip6addrlbl_entry * newp,int replace)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 */
ip6addrlbl_add(struct net * net,const struct in6_addr * prefix,int prefixlen,int ifindex,u32 label,int replace)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 */
__ip6addrlbl_del(struct net * net,const struct in6_addr * prefix,int prefixlen,int ifindex)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 
ip6addrlbl_del(struct net * net,const struct in6_addr * prefix,int prefixlen,int ifindex)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 */
ip6addrlbl_net_init(struct net * net)3073de23255SBenjamin Thery static int __net_init ip6addrlbl_net_init(struct net *net)
3082a8cc6c8SYOSHIFUJI Hideaki {
309e255e11eSWang Hai 	struct ip6addrlbl_entry *p = NULL;
310e255e11eSWang Hai 	struct hlist_node *n;
311e255e11eSWang Hai 	int err;
3122a8cc6c8SYOSHIFUJI Hideaki 	int i;
3132a8cc6c8SYOSHIFUJI Hideaki 
314f3213831SJoe Perches 	ADDRLABEL(KERN_DEBUG "%s\n", __func__);
3152a8cc6c8SYOSHIFUJI Hideaki 
316a90c9347SEric Dumazet 	spin_lock_init(&net->ipv6.ip6addrlbl_table.lock);
317a90c9347SEric Dumazet 	INIT_HLIST_HEAD(&net->ipv6.ip6addrlbl_table.head);
318a90c9347SEric Dumazet 
3192a8cc6c8SYOSHIFUJI Hideaki 	for (i = 0; i < ARRAY_SIZE(ip6addrlbl_init_table); i++) {
320e255e11eSWang Hai 		err = ip6addrlbl_add(net,
3213de23255SBenjamin Thery 				     ip6addrlbl_init_table[i].prefix,
3222a8cc6c8SYOSHIFUJI Hideaki 				     ip6addrlbl_init_table[i].prefixlen,
3232a8cc6c8SYOSHIFUJI Hideaki 				     0,
3242a8cc6c8SYOSHIFUJI Hideaki 				     ip6addrlbl_init_table[i].label, 0);
325e255e11eSWang Hai 		if (err)
326e255e11eSWang Hai 			goto err_ip6addrlbl_add;
327e255e11eSWang Hai 	}
328e255e11eSWang Hai 	return 0;
329e255e11eSWang Hai 
330e255e11eSWang Hai err_ip6addrlbl_add:
331e255e11eSWang Hai 	hlist_for_each_entry_safe(p, n, &net->ipv6.ip6addrlbl_table.head, list) {
332e255e11eSWang Hai 		hlist_del_rcu(&p->list);
333e255e11eSWang Hai 		kfree_rcu(p, rcu);
3342a8cc6c8SYOSHIFUJI Hideaki 	}
3352a8cc6c8SYOSHIFUJI Hideaki 	return err;
3362a8cc6c8SYOSHIFUJI Hideaki }
3372a8cc6c8SYOSHIFUJI Hideaki 
ip6addrlbl_net_exit(struct net * net)3383de23255SBenjamin Thery static void __net_exit ip6addrlbl_net_exit(struct net *net)
3393de23255SBenjamin Thery {
3403de23255SBenjamin Thery 	struct ip6addrlbl_entry *p = NULL;
341b67bfe0dSSasha Levin 	struct hlist_node *n;
3423de23255SBenjamin Thery 
3433de23255SBenjamin Thery 	/* Remove all labels belonging to the exiting net */
344a90c9347SEric Dumazet 	spin_lock(&net->ipv6.ip6addrlbl_table.lock);
345a90c9347SEric Dumazet 	hlist_for_each_entry_safe(p, n, &net->ipv6.ip6addrlbl_table.head, list) {
3463de23255SBenjamin Thery 		hlist_del_rcu(&p->list);
3472809c095SEric Dumazet 		kfree_rcu(p, rcu);
3483de23255SBenjamin Thery 	}
349a90c9347SEric Dumazet 	spin_unlock(&net->ipv6.ip6addrlbl_table.lock);
3503de23255SBenjamin Thery }
3513de23255SBenjamin Thery 
3523de23255SBenjamin Thery static struct pernet_operations ipv6_addr_label_ops = {
3533de23255SBenjamin Thery 	.init = ip6addrlbl_net_init,
3543de23255SBenjamin Thery 	.exit = ip6addrlbl_net_exit,
3553de23255SBenjamin Thery };
3563de23255SBenjamin Thery 
ipv6_addr_label_init(void)3572a8cc6c8SYOSHIFUJI Hideaki int __init ipv6_addr_label_init(void)
3582a8cc6c8SYOSHIFUJI Hideaki {
3593de23255SBenjamin Thery 	return register_pernet_subsys(&ipv6_addr_label_ops);
3602a8cc6c8SYOSHIFUJI Hideaki }
3612a8cc6c8SYOSHIFUJI Hideaki 
ipv6_addr_label_cleanup(void)3622cc6d2bfSNeil Horman void ipv6_addr_label_cleanup(void)
3632cc6d2bfSNeil Horman {
3642cc6d2bfSNeil Horman 	unregister_pernet_subsys(&ipv6_addr_label_ops);
3652cc6d2bfSNeil Horman }
3662cc6d2bfSNeil Horman 
3672a8cc6c8SYOSHIFUJI Hideaki static const struct nla_policy ifal_policy[IFAL_MAX+1] = {
3682a8cc6c8SYOSHIFUJI Hideaki 	[IFAL_ADDRESS]		= { .len = sizeof(struct in6_addr), },
3692a8cc6c8SYOSHIFUJI Hideaki 	[IFAL_LABEL]		= { .len = sizeof(u32), },
3702a8cc6c8SYOSHIFUJI Hideaki };
3712a8cc6c8SYOSHIFUJI Hideaki 
addrlbl_ifindex_exists(struct net * net,int ifindex)372a6f57028SFlorian Westphal static bool addrlbl_ifindex_exists(struct net *net, int ifindex)
373a6f57028SFlorian Westphal {
374a6f57028SFlorian Westphal 
375a6f57028SFlorian Westphal 	struct net_device *dev;
376a6f57028SFlorian Westphal 
377a6f57028SFlorian Westphal 	rcu_read_lock();
378a6f57028SFlorian Westphal 	dev = dev_get_by_index_rcu(net, ifindex);
379a6f57028SFlorian Westphal 	rcu_read_unlock();
380a6f57028SFlorian Westphal 
381a6f57028SFlorian Westphal 	return dev != NULL;
382a6f57028SFlorian Westphal }
383a6f57028SFlorian Westphal 
ip6addrlbl_newdel(struct sk_buff * skb,struct nlmsghdr * nlh,struct netlink_ext_ack * extack)384c21ef3e3SDavid Ahern static int ip6addrlbl_newdel(struct sk_buff *skb, struct nlmsghdr *nlh,
385c21ef3e3SDavid Ahern 			     struct netlink_ext_ack *extack)
3862a8cc6c8SYOSHIFUJI Hideaki {
3873b1e0a65SYOSHIFUJI Hideaki 	struct net *net = sock_net(skb->sk);
3882a8cc6c8SYOSHIFUJI Hideaki 	struct ifaddrlblmsg *ifal;
3892a8cc6c8SYOSHIFUJI Hideaki 	struct nlattr *tb[IFAL_MAX+1];
3902a8cc6c8SYOSHIFUJI Hideaki 	struct in6_addr *pfx;
3912a8cc6c8SYOSHIFUJI Hideaki 	u32 label;
3922a8cc6c8SYOSHIFUJI Hideaki 	int err = 0;
3932a8cc6c8SYOSHIFUJI Hideaki 
3948cb08174SJohannes Berg 	err = nlmsg_parse_deprecated(nlh, sizeof(*ifal), tb, IFAL_MAX,
3958cb08174SJohannes Berg 				     ifal_policy, extack);
3962a8cc6c8SYOSHIFUJI Hideaki 	if (err < 0)
3972a8cc6c8SYOSHIFUJI Hideaki 		return err;
3982a8cc6c8SYOSHIFUJI Hideaki 
3992a8cc6c8SYOSHIFUJI Hideaki 	ifal = nlmsg_data(nlh);
4002a8cc6c8SYOSHIFUJI Hideaki 
4012a8cc6c8SYOSHIFUJI Hideaki 	if (ifal->ifal_family != AF_INET6 ||
4022a8cc6c8SYOSHIFUJI Hideaki 	    ifal->ifal_prefixlen > 128)
4032a8cc6c8SYOSHIFUJI Hideaki 		return -EINVAL;
4042a8cc6c8SYOSHIFUJI Hideaki 
4052a8cc6c8SYOSHIFUJI Hideaki 	if (!tb[IFAL_ADDRESS])
4062a8cc6c8SYOSHIFUJI Hideaki 		return -EINVAL;
4072a8cc6c8SYOSHIFUJI Hideaki 	pfx = nla_data(tb[IFAL_ADDRESS]);
4082a8cc6c8SYOSHIFUJI Hideaki 
4092a8cc6c8SYOSHIFUJI Hideaki 	if (!tb[IFAL_LABEL])
4102a8cc6c8SYOSHIFUJI Hideaki 		return -EINVAL;
4112a8cc6c8SYOSHIFUJI Hideaki 	label = nla_get_u32(tb[IFAL_LABEL]);
4122a8cc6c8SYOSHIFUJI Hideaki 	if (label == IPV6_ADDR_LABEL_DEFAULT)
4132a8cc6c8SYOSHIFUJI Hideaki 		return -EINVAL;
4142a8cc6c8SYOSHIFUJI Hideaki 
4152a8cc6c8SYOSHIFUJI Hideaki 	switch (nlh->nlmsg_type) {
4162a8cc6c8SYOSHIFUJI Hideaki 	case RTM_NEWADDRLABEL:
4170771275bSFlorian Westphal 		if (ifal->ifal_index &&
418a6f57028SFlorian Westphal 		    !addrlbl_ifindex_exists(net, ifal->ifal_index))
4190771275bSFlorian Westphal 			return -EINVAL;
4200771275bSFlorian Westphal 
4213de23255SBenjamin Thery 		err = ip6addrlbl_add(net, pfx, ifal->ifal_prefixlen,
4222a8cc6c8SYOSHIFUJI Hideaki 				     ifal->ifal_index, label,
4232a8cc6c8SYOSHIFUJI Hideaki 				     nlh->nlmsg_flags & NLM_F_REPLACE);
4242a8cc6c8SYOSHIFUJI Hideaki 		break;
4252a8cc6c8SYOSHIFUJI Hideaki 	case RTM_DELADDRLABEL:
4263de23255SBenjamin Thery 		err = ip6addrlbl_del(net, pfx, ifal->ifal_prefixlen,
4272a8cc6c8SYOSHIFUJI Hideaki 				     ifal->ifal_index);
4282a8cc6c8SYOSHIFUJI Hideaki 		break;
4292a8cc6c8SYOSHIFUJI Hideaki 	default:
4302a8cc6c8SYOSHIFUJI Hideaki 		err = -EOPNOTSUPP;
4312a8cc6c8SYOSHIFUJI Hideaki 	}
4322a8cc6c8SYOSHIFUJI Hideaki 	return err;
4332a8cc6c8SYOSHIFUJI Hideaki }
4342a8cc6c8SYOSHIFUJI Hideaki 
ip6addrlbl_putmsg(struct nlmsghdr * nlh,int prefixlen,int ifindex,u32 lseq)435a50feda5SEric Dumazet static void ip6addrlbl_putmsg(struct nlmsghdr *nlh,
4362a8cc6c8SYOSHIFUJI Hideaki 			      int prefixlen, int ifindex, u32 lseq)
4372a8cc6c8SYOSHIFUJI Hideaki {
4382a8cc6c8SYOSHIFUJI Hideaki 	struct ifaddrlblmsg *ifal = nlmsg_data(nlh);
4392a8cc6c8SYOSHIFUJI Hideaki 	ifal->ifal_family = AF_INET6;
4402acb2779SAlexander Potapenko 	ifal->__ifal_reserved = 0;
4412a8cc6c8SYOSHIFUJI Hideaki 	ifal->ifal_prefixlen = prefixlen;
4422a8cc6c8SYOSHIFUJI Hideaki 	ifal->ifal_flags = 0;
4432a8cc6c8SYOSHIFUJI Hideaki 	ifal->ifal_index = ifindex;
4442a8cc6c8SYOSHIFUJI Hideaki 	ifal->ifal_seq = lseq;
4452a8cc6c8SYOSHIFUJI Hideaki };
4462a8cc6c8SYOSHIFUJI Hideaki 
ip6addrlbl_fill(struct sk_buff * skb,struct ip6addrlbl_entry * p,u32 lseq,u32 portid,u32 seq,int event,unsigned int flags)4472a8cc6c8SYOSHIFUJI Hideaki static int ip6addrlbl_fill(struct sk_buff *skb,
4482a8cc6c8SYOSHIFUJI Hideaki 			   struct ip6addrlbl_entry *p,
4492a8cc6c8SYOSHIFUJI Hideaki 			   u32 lseq,
45015e47304SEric W. Biederman 			   u32 portid, u32 seq, int event,
4512a8cc6c8SYOSHIFUJI Hideaki 			   unsigned int flags)
4522a8cc6c8SYOSHIFUJI Hideaki {
45315e47304SEric W. Biederman 	struct nlmsghdr *nlh = nlmsg_put(skb, portid, seq, event,
4542a8cc6c8SYOSHIFUJI Hideaki 					 sizeof(struct ifaddrlblmsg), flags);
4552a8cc6c8SYOSHIFUJI Hideaki 	if (!nlh)
4562a8cc6c8SYOSHIFUJI Hideaki 		return -EMSGSIZE;
4572a8cc6c8SYOSHIFUJI Hideaki 
4582a8cc6c8SYOSHIFUJI Hideaki 	ip6addrlbl_putmsg(nlh, p->prefixlen, p->ifindex, lseq);
4592a8cc6c8SYOSHIFUJI Hideaki 
460930345eaSJiri Benc 	if (nla_put_in6_addr(skb, IFAL_ADDRESS, &p->prefix) < 0 ||
4612a8cc6c8SYOSHIFUJI Hideaki 	    nla_put_u32(skb, IFAL_LABEL, p->label) < 0) {
4622a8cc6c8SYOSHIFUJI Hideaki 		nlmsg_cancel(skb, nlh);
4632a8cc6c8SYOSHIFUJI Hideaki 		return -EMSGSIZE;
4642a8cc6c8SYOSHIFUJI Hideaki 	}
4652a8cc6c8SYOSHIFUJI Hideaki 
466053c095aSJohannes Berg 	nlmsg_end(skb, nlh);
467053c095aSJohannes Berg 	return 0;
4682a8cc6c8SYOSHIFUJI Hideaki }
4692a8cc6c8SYOSHIFUJI Hideaki 
ip6addrlbl_valid_dump_req(const struct nlmsghdr * nlh,struct netlink_ext_ack * extack)470f2ae64bbSDavid Ahern static int ip6addrlbl_valid_dump_req(const struct nlmsghdr *nlh,
471f2ae64bbSDavid Ahern 				     struct netlink_ext_ack *extack)
472f2ae64bbSDavid Ahern {
473f2ae64bbSDavid Ahern 	struct ifaddrlblmsg *ifal;
474f2ae64bbSDavid Ahern 
475f2ae64bbSDavid Ahern 	if (nlh->nlmsg_len < nlmsg_msg_size(sizeof(*ifal))) {
476f2ae64bbSDavid Ahern 		NL_SET_ERR_MSG_MOD(extack, "Invalid header for address label dump request");
477f2ae64bbSDavid Ahern 		return -EINVAL;
478f2ae64bbSDavid Ahern 	}
479f2ae64bbSDavid Ahern 
480f2ae64bbSDavid Ahern 	ifal = nlmsg_data(nlh);
481f2ae64bbSDavid Ahern 	if (ifal->__ifal_reserved || ifal->ifal_prefixlen ||
482f2ae64bbSDavid Ahern 	    ifal->ifal_flags || ifal->ifal_index || ifal->ifal_seq) {
483f2ae64bbSDavid Ahern 		NL_SET_ERR_MSG_MOD(extack, "Invalid values in header for address label dump request");
484f2ae64bbSDavid Ahern 		return -EINVAL;
485f2ae64bbSDavid Ahern 	}
486f2ae64bbSDavid Ahern 
487f2ae64bbSDavid Ahern 	if (nlmsg_attrlen(nlh, sizeof(*ifal))) {
488d5f6db35SColin Ian King 		NL_SET_ERR_MSG_MOD(extack, "Invalid data after header for address label dump request");
489f2ae64bbSDavid Ahern 		return -EINVAL;
490f2ae64bbSDavid Ahern 	}
491f2ae64bbSDavid Ahern 
492f2ae64bbSDavid Ahern 	return 0;
493f2ae64bbSDavid Ahern }
494f2ae64bbSDavid Ahern 
ip6addrlbl_dump(struct sk_buff * skb,struct netlink_callback * cb)4952a8cc6c8SYOSHIFUJI Hideaki static int ip6addrlbl_dump(struct sk_buff *skb, struct netlink_callback *cb)
4962a8cc6c8SYOSHIFUJI Hideaki {
497f2ae64bbSDavid Ahern 	const struct nlmsghdr *nlh = cb->nlh;
4983b1e0a65SYOSHIFUJI Hideaki 	struct net *net = sock_net(skb->sk);
4992a8cc6c8SYOSHIFUJI Hideaki 	struct ip6addrlbl_entry *p;
5002a8cc6c8SYOSHIFUJI Hideaki 	int idx = 0, s_idx = cb->args[0];
5012a8cc6c8SYOSHIFUJI Hideaki 	int err;
5022a8cc6c8SYOSHIFUJI Hideaki 
503f2ae64bbSDavid Ahern 	if (cb->strict_check) {
504f2ae64bbSDavid Ahern 		err = ip6addrlbl_valid_dump_req(nlh, cb->extack);
505f2ae64bbSDavid Ahern 		if (err < 0)
506f2ae64bbSDavid Ahern 			return err;
507f2ae64bbSDavid Ahern 	}
508f2ae64bbSDavid Ahern 
5092a8cc6c8SYOSHIFUJI Hideaki 	rcu_read_lock();
510a90c9347SEric Dumazet 	hlist_for_each_entry_rcu(p, &net->ipv6.ip6addrlbl_table.head, list) {
511a90c9347SEric Dumazet 		if (idx >= s_idx) {
512cb6e926eSWang Yufen 			err = ip6addrlbl_fill(skb, p,
513a90c9347SEric Dumazet 					      net->ipv6.ip6addrlbl_table.seq,
51415e47304SEric W. Biederman 					      NETLINK_CB(cb->skb).portid,
515f2ae64bbSDavid Ahern 					      nlh->nlmsg_seq,
5162a8cc6c8SYOSHIFUJI Hideaki 					      RTM_NEWADDRLABEL,
517cb6e926eSWang Yufen 					      NLM_F_MULTI);
518053c095aSJohannes Berg 			if (err < 0)
5192a8cc6c8SYOSHIFUJI Hideaki 				break;
5202a8cc6c8SYOSHIFUJI Hideaki 		}
5212a8cc6c8SYOSHIFUJI Hideaki 		idx++;
5222a8cc6c8SYOSHIFUJI Hideaki 	}
5232a8cc6c8SYOSHIFUJI Hideaki 	rcu_read_unlock();
5242a8cc6c8SYOSHIFUJI Hideaki 	cb->args[0] = idx;
5252a8cc6c8SYOSHIFUJI Hideaki 	return skb->len;
5262a8cc6c8SYOSHIFUJI Hideaki }
5272a8cc6c8SYOSHIFUJI Hideaki 
ip6addrlbl_msgsize(void)5282a8cc6c8SYOSHIFUJI Hideaki static inline int ip6addrlbl_msgsize(void)
5292a8cc6c8SYOSHIFUJI Hideaki {
530a02cec21SEric Dumazet 	return NLMSG_ALIGN(sizeof(struct ifaddrlblmsg))
5312a8cc6c8SYOSHIFUJI Hideaki 		+ nla_total_size(16)	/* IFAL_ADDRESS */
532a02cec21SEric Dumazet 		+ nla_total_size(4);	/* IFAL_LABEL */
5332a8cc6c8SYOSHIFUJI Hideaki }
5342a8cc6c8SYOSHIFUJI Hideaki 
ip6addrlbl_valid_get_req(struct sk_buff * skb,const struct nlmsghdr * nlh,struct nlattr ** tb,struct netlink_ext_ack * extack)5355912a775SJakub Kicinski static int ip6addrlbl_valid_get_req(struct sk_buff *skb,
5365912a775SJakub Kicinski 				    const struct nlmsghdr *nlh,
5375912a775SJakub Kicinski 				    struct nlattr **tb,
5385912a775SJakub Kicinski 				    struct netlink_ext_ack *extack)
5395912a775SJakub Kicinski {
5405912a775SJakub Kicinski 	struct ifaddrlblmsg *ifal;
5415912a775SJakub Kicinski 	int i, err;
5425912a775SJakub Kicinski 
5435912a775SJakub Kicinski 	if (nlh->nlmsg_len < nlmsg_msg_size(sizeof(*ifal))) {
5445912a775SJakub Kicinski 		NL_SET_ERR_MSG_MOD(extack, "Invalid header for addrlabel get request");
5455912a775SJakub Kicinski 		return -EINVAL;
5465912a775SJakub Kicinski 	}
5475912a775SJakub Kicinski 
5485912a775SJakub Kicinski 	if (!netlink_strict_get_check(skb))
5498cb08174SJohannes Berg 		return nlmsg_parse_deprecated(nlh, sizeof(*ifal), tb,
5508cb08174SJohannes Berg 					      IFAL_MAX, ifal_policy, extack);
5515912a775SJakub Kicinski 
5525912a775SJakub Kicinski 	ifal = nlmsg_data(nlh);
5535912a775SJakub Kicinski 	if (ifal->__ifal_reserved || ifal->ifal_flags || ifal->ifal_seq) {
5545912a775SJakub Kicinski 		NL_SET_ERR_MSG_MOD(extack, "Invalid values in header for addrlabel get request");
5555912a775SJakub Kicinski 		return -EINVAL;
5565912a775SJakub Kicinski 	}
5575912a775SJakub Kicinski 
5588cb08174SJohannes Berg 	err = nlmsg_parse_deprecated_strict(nlh, sizeof(*ifal), tb, IFAL_MAX,
5595912a775SJakub Kicinski 					    ifal_policy, extack);
5605912a775SJakub Kicinski 	if (err)
5615912a775SJakub Kicinski 		return err;
5625912a775SJakub Kicinski 
5635912a775SJakub Kicinski 	for (i = 0; i <= IFAL_MAX; i++) {
5645912a775SJakub Kicinski 		if (!tb[i])
5655912a775SJakub Kicinski 			continue;
5665912a775SJakub Kicinski 
5675912a775SJakub Kicinski 		switch (i) {
5685912a775SJakub Kicinski 		case IFAL_ADDRESS:
5695912a775SJakub Kicinski 			break;
5705912a775SJakub Kicinski 		default:
5715912a775SJakub Kicinski 			NL_SET_ERR_MSG_MOD(extack, "Unsupported attribute in addrlabel get request");
5725912a775SJakub Kicinski 			return -EINVAL;
5735912a775SJakub Kicinski 		}
5745912a775SJakub Kicinski 	}
5755912a775SJakub Kicinski 
5765912a775SJakub Kicinski 	return 0;
5775912a775SJakub Kicinski }
5785912a775SJakub Kicinski 
ip6addrlbl_get(struct sk_buff * in_skb,struct nlmsghdr * nlh,struct netlink_ext_ack * extack)579c21ef3e3SDavid Ahern static int ip6addrlbl_get(struct sk_buff *in_skb, struct nlmsghdr *nlh,
580c21ef3e3SDavid Ahern 			  struct netlink_ext_ack *extack)
5812a8cc6c8SYOSHIFUJI Hideaki {
5823b1e0a65SYOSHIFUJI Hideaki 	struct net *net = sock_net(in_skb->sk);
5832a8cc6c8SYOSHIFUJI Hideaki 	struct ifaddrlblmsg *ifal;
5842a8cc6c8SYOSHIFUJI Hideaki 	struct nlattr *tb[IFAL_MAX+1];
5852a8cc6c8SYOSHIFUJI Hideaki 	struct in6_addr *addr;
5862a8cc6c8SYOSHIFUJI Hideaki 	u32 lseq;
5872a8cc6c8SYOSHIFUJI Hideaki 	int err = 0;
5882a8cc6c8SYOSHIFUJI Hideaki 	struct ip6addrlbl_entry *p;
5892a8cc6c8SYOSHIFUJI Hideaki 	struct sk_buff *skb;
5902a8cc6c8SYOSHIFUJI Hideaki 
5915912a775SJakub Kicinski 	err = ip6addrlbl_valid_get_req(in_skb, nlh, tb, extack);
5922a8cc6c8SYOSHIFUJI Hideaki 	if (err < 0)
5932a8cc6c8SYOSHIFUJI Hideaki 		return err;
5942a8cc6c8SYOSHIFUJI Hideaki 
5952a8cc6c8SYOSHIFUJI Hideaki 	ifal = nlmsg_data(nlh);
5962a8cc6c8SYOSHIFUJI Hideaki 
5972a8cc6c8SYOSHIFUJI Hideaki 	if (ifal->ifal_family != AF_INET6 ||
5982a8cc6c8SYOSHIFUJI Hideaki 	    ifal->ifal_prefixlen != 128)
5992a8cc6c8SYOSHIFUJI Hideaki 		return -EINVAL;
6002a8cc6c8SYOSHIFUJI Hideaki 
6012a8cc6c8SYOSHIFUJI Hideaki 	if (ifal->ifal_index &&
602a6f57028SFlorian Westphal 	    !addrlbl_ifindex_exists(net, ifal->ifal_index))
6032a8cc6c8SYOSHIFUJI Hideaki 		return -EINVAL;
6042a8cc6c8SYOSHIFUJI Hideaki 
6052a8cc6c8SYOSHIFUJI Hideaki 	if (!tb[IFAL_ADDRESS])
6062a8cc6c8SYOSHIFUJI Hideaki 		return -EINVAL;
6072a8cc6c8SYOSHIFUJI Hideaki 	addr = nla_data(tb[IFAL_ADDRESS]);
6082a8cc6c8SYOSHIFUJI Hideaki 
60966c77ff3SEric Dumazet 	skb = nlmsg_new(ip6addrlbl_msgsize(), GFP_KERNEL);
61066c77ff3SEric Dumazet 	if (!skb)
61166c77ff3SEric Dumazet 		return -ENOBUFS;
61266c77ff3SEric Dumazet 
61366c77ff3SEric Dumazet 	err = -ESRCH;
61466c77ff3SEric Dumazet 
6152a8cc6c8SYOSHIFUJI Hideaki 	rcu_read_lock();
6163de23255SBenjamin Thery 	p = __ipv6_addr_label(net, addr, ipv6_addr_type(addr), ifal->ifal_index);
617a90c9347SEric Dumazet 	lseq = net->ipv6.ip6addrlbl_table.seq;
61866c77ff3SEric Dumazet 	if (p)
6192a8cc6c8SYOSHIFUJI Hideaki 		err = ip6addrlbl_fill(skb, p, lseq,
62066c77ff3SEric Dumazet 				      NETLINK_CB(in_skb).portid,
62166c77ff3SEric Dumazet 				      nlh->nlmsg_seq,
6222a8cc6c8SYOSHIFUJI Hideaki 				      RTM_NEWADDRLABEL, 0);
62366c77ff3SEric Dumazet 	rcu_read_unlock();
6242a8cc6c8SYOSHIFUJI Hideaki 
6252a8cc6c8SYOSHIFUJI Hideaki 	if (err < 0) {
6262a8cc6c8SYOSHIFUJI Hideaki 		WARN_ON(err == -EMSGSIZE);
6272a8cc6c8SYOSHIFUJI Hideaki 		kfree_skb(skb);
62866c77ff3SEric Dumazet 	} else {
62915e47304SEric W. Biederman 		err = rtnl_unicast(skb, net, NETLINK_CB(in_skb).portid);
63066c77ff3SEric Dumazet 	}
6312a8cc6c8SYOSHIFUJI Hideaki 	return err;
6322a8cc6c8SYOSHIFUJI Hideaki }
6332a8cc6c8SYOSHIFUJI Hideaki 
ipv6_addr_label_rtnl_register(void)634a3fde2adSFlorian Westphal int __init ipv6_addr_label_rtnl_register(void)
6352a8cc6c8SYOSHIFUJI Hideaki {
636a3fde2adSFlorian Westphal 	int ret;
637a3fde2adSFlorian Westphal 
638a3fde2adSFlorian Westphal 	ret = rtnl_register_module(THIS_MODULE, PF_INET6, RTM_NEWADDRLABEL,
639a3fde2adSFlorian Westphal 				   ip6addrlbl_newdel,
640a6f57028SFlorian Westphal 				   NULL, RTNL_FLAG_DOIT_UNLOCKED);
641a3fde2adSFlorian Westphal 	if (ret < 0)
642a3fde2adSFlorian Westphal 		return ret;
643a3fde2adSFlorian Westphal 	ret = rtnl_register_module(THIS_MODULE, PF_INET6, RTM_DELADDRLABEL,
644a3fde2adSFlorian Westphal 				   ip6addrlbl_newdel,
645a6f57028SFlorian Westphal 				   NULL, RTNL_FLAG_DOIT_UNLOCKED);
646a3fde2adSFlorian Westphal 	if (ret < 0)
647a3fde2adSFlorian Westphal 		return ret;
648a3fde2adSFlorian Westphal 	ret = rtnl_register_module(THIS_MODULE, PF_INET6, RTM_GETADDRLABEL,
649a3fde2adSFlorian Westphal 				   ip6addrlbl_get,
650a6f57028SFlorian Westphal 				   ip6addrlbl_dump, RTNL_FLAG_DOIT_UNLOCKED);
651a3fde2adSFlorian Westphal 	return ret;
6522a8cc6c8SYOSHIFUJI Hideaki }
653