18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci * IPv6 Address Label subsystem
48c2ecf20Sopenharmony_ci * for the IPv6 "Default" Source Address Selection
58c2ecf20Sopenharmony_ci *
68c2ecf20Sopenharmony_ci * Copyright (C)2007 USAGI/WIDE Project
78c2ecf20Sopenharmony_ci */
88c2ecf20Sopenharmony_ci/*
98c2ecf20Sopenharmony_ci * Author:
108c2ecf20Sopenharmony_ci *	YOSHIFUJI Hideaki @ USAGI/WIDE Project <yoshfuji@linux-ipv6.org>
118c2ecf20Sopenharmony_ci */
128c2ecf20Sopenharmony_ci
138c2ecf20Sopenharmony_ci#include <linux/kernel.h>
148c2ecf20Sopenharmony_ci#include <linux/list.h>
158c2ecf20Sopenharmony_ci#include <linux/rcupdate.h>
168c2ecf20Sopenharmony_ci#include <linux/in6.h>
178c2ecf20Sopenharmony_ci#include <linux/slab.h>
188c2ecf20Sopenharmony_ci#include <net/addrconf.h>
198c2ecf20Sopenharmony_ci#include <linux/if_addrlabel.h>
208c2ecf20Sopenharmony_ci#include <linux/netlink.h>
218c2ecf20Sopenharmony_ci#include <linux/rtnetlink.h>
228c2ecf20Sopenharmony_ci
238c2ecf20Sopenharmony_ci#if 0
248c2ecf20Sopenharmony_ci#define ADDRLABEL(x...) printk(x)
258c2ecf20Sopenharmony_ci#else
268c2ecf20Sopenharmony_ci#define ADDRLABEL(x...) do { ; } while (0)
278c2ecf20Sopenharmony_ci#endif
288c2ecf20Sopenharmony_ci
298c2ecf20Sopenharmony_ci/*
308c2ecf20Sopenharmony_ci * Policy Table
318c2ecf20Sopenharmony_ci */
328c2ecf20Sopenharmony_cistruct ip6addrlbl_entry {
338c2ecf20Sopenharmony_ci	struct in6_addr prefix;
348c2ecf20Sopenharmony_ci	int prefixlen;
358c2ecf20Sopenharmony_ci	int ifindex;
368c2ecf20Sopenharmony_ci	int addrtype;
378c2ecf20Sopenharmony_ci	u32 label;
388c2ecf20Sopenharmony_ci	struct hlist_node list;
398c2ecf20Sopenharmony_ci	struct rcu_head rcu;
408c2ecf20Sopenharmony_ci};
418c2ecf20Sopenharmony_ci
428c2ecf20Sopenharmony_ci/*
438c2ecf20Sopenharmony_ci * Default policy table (RFC6724 + extensions)
448c2ecf20Sopenharmony_ci *
458c2ecf20Sopenharmony_ci * prefix		addr_type	label
468c2ecf20Sopenharmony_ci * -------------------------------------------------------------------------
478c2ecf20Sopenharmony_ci * ::1/128		LOOPBACK	0
488c2ecf20Sopenharmony_ci * ::/0			N/A		1
498c2ecf20Sopenharmony_ci * 2002::/16		N/A		2
508c2ecf20Sopenharmony_ci * ::/96		COMPATv4	3
518c2ecf20Sopenharmony_ci * ::ffff:0:0/96	V4MAPPED	4
528c2ecf20Sopenharmony_ci * fc00::/7		N/A		5		ULA (RFC 4193)
538c2ecf20Sopenharmony_ci * 2001::/32		N/A		6		Teredo (RFC 4380)
548c2ecf20Sopenharmony_ci * 2001:10::/28		N/A		7		ORCHID (RFC 4843)
558c2ecf20Sopenharmony_ci * fec0::/10		N/A		11		Site-local
568c2ecf20Sopenharmony_ci *							(deprecated by RFC3879)
578c2ecf20Sopenharmony_ci * 3ffe::/16		N/A		12		6bone
588c2ecf20Sopenharmony_ci *
598c2ecf20Sopenharmony_ci * Note: 0xffffffff is used if we do not have any policies.
608c2ecf20Sopenharmony_ci * Note: Labels for ULA and 6to4 are different from labels listed in RFC6724.
618c2ecf20Sopenharmony_ci */
628c2ecf20Sopenharmony_ci
638c2ecf20Sopenharmony_ci#define IPV6_ADDR_LABEL_DEFAULT	0xffffffffUL
648c2ecf20Sopenharmony_ci
658c2ecf20Sopenharmony_cistatic const __net_initconst struct ip6addrlbl_init_table
668c2ecf20Sopenharmony_ci{
678c2ecf20Sopenharmony_ci	const struct in6_addr *prefix;
688c2ecf20Sopenharmony_ci	int prefixlen;
698c2ecf20Sopenharmony_ci	u32 label;
708c2ecf20Sopenharmony_ci} ip6addrlbl_init_table[] = {
718c2ecf20Sopenharmony_ci	{	/* ::/0 */
728c2ecf20Sopenharmony_ci		.prefix = &in6addr_any,
738c2ecf20Sopenharmony_ci		.label = 1,
748c2ecf20Sopenharmony_ci	}, {	/* fc00::/7 */
758c2ecf20Sopenharmony_ci		.prefix = &(struct in6_addr){ { { 0xfc } } } ,
768c2ecf20Sopenharmony_ci		.prefixlen = 7,
778c2ecf20Sopenharmony_ci		.label = 5,
788c2ecf20Sopenharmony_ci	}, {	/* fec0::/10 */
798c2ecf20Sopenharmony_ci		.prefix = &(struct in6_addr){ { { 0xfe, 0xc0 } } },
808c2ecf20Sopenharmony_ci		.prefixlen = 10,
818c2ecf20Sopenharmony_ci		.label = 11,
828c2ecf20Sopenharmony_ci	}, {	/* 2002::/16 */
838c2ecf20Sopenharmony_ci		.prefix = &(struct in6_addr){ { { 0x20, 0x02 } } },
848c2ecf20Sopenharmony_ci		.prefixlen = 16,
858c2ecf20Sopenharmony_ci		.label = 2,
868c2ecf20Sopenharmony_ci	}, {	/* 3ffe::/16 */
878c2ecf20Sopenharmony_ci		.prefix = &(struct in6_addr){ { { 0x3f, 0xfe } } },
888c2ecf20Sopenharmony_ci		.prefixlen = 16,
898c2ecf20Sopenharmony_ci		.label = 12,
908c2ecf20Sopenharmony_ci	}, {	/* 2001::/32 */
918c2ecf20Sopenharmony_ci		.prefix = &(struct in6_addr){ { { 0x20, 0x01 } } },
928c2ecf20Sopenharmony_ci		.prefixlen = 32,
938c2ecf20Sopenharmony_ci		.label = 6,
948c2ecf20Sopenharmony_ci	}, {	/* 2001:10::/28 */
958c2ecf20Sopenharmony_ci		.prefix = &(struct in6_addr){ { { 0x20, 0x01, 0x00, 0x10 } } },
968c2ecf20Sopenharmony_ci		.prefixlen = 28,
978c2ecf20Sopenharmony_ci		.label = 7,
988c2ecf20Sopenharmony_ci	}, {	/* ::ffff:0:0 */
998c2ecf20Sopenharmony_ci		.prefix = &(struct in6_addr){ { { [10] = 0xff, [11] = 0xff } } },
1008c2ecf20Sopenharmony_ci		.prefixlen = 96,
1018c2ecf20Sopenharmony_ci		.label = 4,
1028c2ecf20Sopenharmony_ci	}, {	/* ::/96 */
1038c2ecf20Sopenharmony_ci		.prefix = &in6addr_any,
1048c2ecf20Sopenharmony_ci		.prefixlen = 96,
1058c2ecf20Sopenharmony_ci		.label = 3,
1068c2ecf20Sopenharmony_ci	}, {	/* ::1/128 */
1078c2ecf20Sopenharmony_ci		.prefix = &in6addr_loopback,
1088c2ecf20Sopenharmony_ci		.prefixlen = 128,
1098c2ecf20Sopenharmony_ci		.label = 0,
1108c2ecf20Sopenharmony_ci	}
1118c2ecf20Sopenharmony_ci};
1128c2ecf20Sopenharmony_ci
1138c2ecf20Sopenharmony_ci/* Find label */
1148c2ecf20Sopenharmony_cistatic bool __ip6addrlbl_match(const struct ip6addrlbl_entry *p,
1158c2ecf20Sopenharmony_ci			       const struct in6_addr *addr,
1168c2ecf20Sopenharmony_ci			       int addrtype, int ifindex)
1178c2ecf20Sopenharmony_ci{
1188c2ecf20Sopenharmony_ci	if (p->ifindex && p->ifindex != ifindex)
1198c2ecf20Sopenharmony_ci		return false;
1208c2ecf20Sopenharmony_ci	if (p->addrtype && p->addrtype != addrtype)
1218c2ecf20Sopenharmony_ci		return false;
1228c2ecf20Sopenharmony_ci	if (!ipv6_prefix_equal(addr, &p->prefix, p->prefixlen))
1238c2ecf20Sopenharmony_ci		return false;
1248c2ecf20Sopenharmony_ci	return true;
1258c2ecf20Sopenharmony_ci}
1268c2ecf20Sopenharmony_ci
1278c2ecf20Sopenharmony_cistatic struct ip6addrlbl_entry *__ipv6_addr_label(struct net *net,
1288c2ecf20Sopenharmony_ci						  const struct in6_addr *addr,
1298c2ecf20Sopenharmony_ci						  int type, int ifindex)
1308c2ecf20Sopenharmony_ci{
1318c2ecf20Sopenharmony_ci	struct ip6addrlbl_entry *p;
1328c2ecf20Sopenharmony_ci
1338c2ecf20Sopenharmony_ci	hlist_for_each_entry_rcu(p, &net->ipv6.ip6addrlbl_table.head, list) {
1348c2ecf20Sopenharmony_ci		if (__ip6addrlbl_match(p, addr, type, ifindex))
1358c2ecf20Sopenharmony_ci			return p;
1368c2ecf20Sopenharmony_ci	}
1378c2ecf20Sopenharmony_ci	return NULL;
1388c2ecf20Sopenharmony_ci}
1398c2ecf20Sopenharmony_ci
1408c2ecf20Sopenharmony_ciu32 ipv6_addr_label(struct net *net,
1418c2ecf20Sopenharmony_ci		    const struct in6_addr *addr, int type, int ifindex)
1428c2ecf20Sopenharmony_ci{
1438c2ecf20Sopenharmony_ci	u32 label;
1448c2ecf20Sopenharmony_ci	struct ip6addrlbl_entry *p;
1458c2ecf20Sopenharmony_ci
1468c2ecf20Sopenharmony_ci	type &= IPV6_ADDR_MAPPED | IPV6_ADDR_COMPATv4 | IPV6_ADDR_LOOPBACK;
1478c2ecf20Sopenharmony_ci
1488c2ecf20Sopenharmony_ci	rcu_read_lock();
1498c2ecf20Sopenharmony_ci	p = __ipv6_addr_label(net, addr, type, ifindex);
1508c2ecf20Sopenharmony_ci	label = p ? p->label : IPV6_ADDR_LABEL_DEFAULT;
1518c2ecf20Sopenharmony_ci	rcu_read_unlock();
1528c2ecf20Sopenharmony_ci
1538c2ecf20Sopenharmony_ci	ADDRLABEL(KERN_DEBUG "%s(addr=%pI6, type=%d, ifindex=%d) => %08x\n",
1548c2ecf20Sopenharmony_ci		  __func__, addr, type, ifindex, label);
1558c2ecf20Sopenharmony_ci
1568c2ecf20Sopenharmony_ci	return label;
1578c2ecf20Sopenharmony_ci}
1588c2ecf20Sopenharmony_ci
1598c2ecf20Sopenharmony_ci/* allocate one entry */
1608c2ecf20Sopenharmony_cistatic struct ip6addrlbl_entry *ip6addrlbl_alloc(const struct in6_addr *prefix,
1618c2ecf20Sopenharmony_ci						 int prefixlen, int ifindex,
1628c2ecf20Sopenharmony_ci						 u32 label)
1638c2ecf20Sopenharmony_ci{
1648c2ecf20Sopenharmony_ci	struct ip6addrlbl_entry *newp;
1658c2ecf20Sopenharmony_ci	int addrtype;
1668c2ecf20Sopenharmony_ci
1678c2ecf20Sopenharmony_ci	ADDRLABEL(KERN_DEBUG "%s(prefix=%pI6, prefixlen=%d, ifindex=%d, label=%u)\n",
1688c2ecf20Sopenharmony_ci		  __func__, prefix, prefixlen, ifindex, (unsigned int)label);
1698c2ecf20Sopenharmony_ci
1708c2ecf20Sopenharmony_ci	addrtype = ipv6_addr_type(prefix) & (IPV6_ADDR_MAPPED | IPV6_ADDR_COMPATv4 | IPV6_ADDR_LOOPBACK);
1718c2ecf20Sopenharmony_ci
1728c2ecf20Sopenharmony_ci	switch (addrtype) {
1738c2ecf20Sopenharmony_ci	case IPV6_ADDR_MAPPED:
1748c2ecf20Sopenharmony_ci		if (prefixlen > 96)
1758c2ecf20Sopenharmony_ci			return ERR_PTR(-EINVAL);
1768c2ecf20Sopenharmony_ci		if (prefixlen < 96)
1778c2ecf20Sopenharmony_ci			addrtype = 0;
1788c2ecf20Sopenharmony_ci		break;
1798c2ecf20Sopenharmony_ci	case IPV6_ADDR_COMPATv4:
1808c2ecf20Sopenharmony_ci		if (prefixlen != 96)
1818c2ecf20Sopenharmony_ci			addrtype = 0;
1828c2ecf20Sopenharmony_ci		break;
1838c2ecf20Sopenharmony_ci	case IPV6_ADDR_LOOPBACK:
1848c2ecf20Sopenharmony_ci		if (prefixlen != 128)
1858c2ecf20Sopenharmony_ci			addrtype = 0;
1868c2ecf20Sopenharmony_ci		break;
1878c2ecf20Sopenharmony_ci	}
1888c2ecf20Sopenharmony_ci
1898c2ecf20Sopenharmony_ci	newp = kmalloc(sizeof(*newp), GFP_KERNEL);
1908c2ecf20Sopenharmony_ci	if (!newp)
1918c2ecf20Sopenharmony_ci		return ERR_PTR(-ENOMEM);
1928c2ecf20Sopenharmony_ci
1938c2ecf20Sopenharmony_ci	ipv6_addr_prefix(&newp->prefix, prefix, prefixlen);
1948c2ecf20Sopenharmony_ci	newp->prefixlen = prefixlen;
1958c2ecf20Sopenharmony_ci	newp->ifindex = ifindex;
1968c2ecf20Sopenharmony_ci	newp->addrtype = addrtype;
1978c2ecf20Sopenharmony_ci	newp->label = label;
1988c2ecf20Sopenharmony_ci	INIT_HLIST_NODE(&newp->list);
1998c2ecf20Sopenharmony_ci	return newp;
2008c2ecf20Sopenharmony_ci}
2018c2ecf20Sopenharmony_ci
2028c2ecf20Sopenharmony_ci/* add a label */
2038c2ecf20Sopenharmony_cistatic int __ip6addrlbl_add(struct net *net, struct ip6addrlbl_entry *newp,
2048c2ecf20Sopenharmony_ci			    int replace)
2058c2ecf20Sopenharmony_ci{
2068c2ecf20Sopenharmony_ci	struct ip6addrlbl_entry *last = NULL, *p = NULL;
2078c2ecf20Sopenharmony_ci	struct hlist_node *n;
2088c2ecf20Sopenharmony_ci	int ret = 0;
2098c2ecf20Sopenharmony_ci
2108c2ecf20Sopenharmony_ci	ADDRLABEL(KERN_DEBUG "%s(newp=%p, replace=%d)\n", __func__, newp,
2118c2ecf20Sopenharmony_ci		  replace);
2128c2ecf20Sopenharmony_ci
2138c2ecf20Sopenharmony_ci	hlist_for_each_entry_safe(p, n,	&net->ipv6.ip6addrlbl_table.head, list) {
2148c2ecf20Sopenharmony_ci		if (p->prefixlen == newp->prefixlen &&
2158c2ecf20Sopenharmony_ci		    p->ifindex == newp->ifindex &&
2168c2ecf20Sopenharmony_ci		    ipv6_addr_equal(&p->prefix, &newp->prefix)) {
2178c2ecf20Sopenharmony_ci			if (!replace) {
2188c2ecf20Sopenharmony_ci				ret = -EEXIST;
2198c2ecf20Sopenharmony_ci				goto out;
2208c2ecf20Sopenharmony_ci			}
2218c2ecf20Sopenharmony_ci			hlist_replace_rcu(&p->list, &newp->list);
2228c2ecf20Sopenharmony_ci			kfree_rcu(p, rcu);
2238c2ecf20Sopenharmony_ci			goto out;
2248c2ecf20Sopenharmony_ci		} else if ((p->prefixlen == newp->prefixlen && !p->ifindex) ||
2258c2ecf20Sopenharmony_ci			   (p->prefixlen < newp->prefixlen)) {
2268c2ecf20Sopenharmony_ci			hlist_add_before_rcu(&newp->list, &p->list);
2278c2ecf20Sopenharmony_ci			goto out;
2288c2ecf20Sopenharmony_ci		}
2298c2ecf20Sopenharmony_ci		last = p;
2308c2ecf20Sopenharmony_ci	}
2318c2ecf20Sopenharmony_ci	if (last)
2328c2ecf20Sopenharmony_ci		hlist_add_behind_rcu(&newp->list, &last->list);
2338c2ecf20Sopenharmony_ci	else
2348c2ecf20Sopenharmony_ci		hlist_add_head_rcu(&newp->list, &net->ipv6.ip6addrlbl_table.head);
2358c2ecf20Sopenharmony_ciout:
2368c2ecf20Sopenharmony_ci	if (!ret)
2378c2ecf20Sopenharmony_ci		net->ipv6.ip6addrlbl_table.seq++;
2388c2ecf20Sopenharmony_ci	return ret;
2398c2ecf20Sopenharmony_ci}
2408c2ecf20Sopenharmony_ci
2418c2ecf20Sopenharmony_ci/* add a label */
2428c2ecf20Sopenharmony_cistatic int ip6addrlbl_add(struct net *net,
2438c2ecf20Sopenharmony_ci			  const struct in6_addr *prefix, int prefixlen,
2448c2ecf20Sopenharmony_ci			  int ifindex, u32 label, int replace)
2458c2ecf20Sopenharmony_ci{
2468c2ecf20Sopenharmony_ci	struct ip6addrlbl_entry *newp;
2478c2ecf20Sopenharmony_ci	int ret = 0;
2488c2ecf20Sopenharmony_ci
2498c2ecf20Sopenharmony_ci	ADDRLABEL(KERN_DEBUG "%s(prefix=%pI6, prefixlen=%d, ifindex=%d, label=%u, replace=%d)\n",
2508c2ecf20Sopenharmony_ci		  __func__, prefix, prefixlen, ifindex, (unsigned int)label,
2518c2ecf20Sopenharmony_ci		  replace);
2528c2ecf20Sopenharmony_ci
2538c2ecf20Sopenharmony_ci	newp = ip6addrlbl_alloc(prefix, prefixlen, ifindex, label);
2548c2ecf20Sopenharmony_ci	if (IS_ERR(newp))
2558c2ecf20Sopenharmony_ci		return PTR_ERR(newp);
2568c2ecf20Sopenharmony_ci	spin_lock(&net->ipv6.ip6addrlbl_table.lock);
2578c2ecf20Sopenharmony_ci	ret = __ip6addrlbl_add(net, newp, replace);
2588c2ecf20Sopenharmony_ci	spin_unlock(&net->ipv6.ip6addrlbl_table.lock);
2598c2ecf20Sopenharmony_ci	if (ret)
2608c2ecf20Sopenharmony_ci		kfree(newp);
2618c2ecf20Sopenharmony_ci	return ret;
2628c2ecf20Sopenharmony_ci}
2638c2ecf20Sopenharmony_ci
2648c2ecf20Sopenharmony_ci/* remove a label */
2658c2ecf20Sopenharmony_cistatic int __ip6addrlbl_del(struct net *net,
2668c2ecf20Sopenharmony_ci			    const struct in6_addr *prefix, int prefixlen,
2678c2ecf20Sopenharmony_ci			    int ifindex)
2688c2ecf20Sopenharmony_ci{
2698c2ecf20Sopenharmony_ci	struct ip6addrlbl_entry *p = NULL;
2708c2ecf20Sopenharmony_ci	struct hlist_node *n;
2718c2ecf20Sopenharmony_ci	int ret = -ESRCH;
2728c2ecf20Sopenharmony_ci
2738c2ecf20Sopenharmony_ci	ADDRLABEL(KERN_DEBUG "%s(prefix=%pI6, prefixlen=%d, ifindex=%d)\n",
2748c2ecf20Sopenharmony_ci		  __func__, prefix, prefixlen, ifindex);
2758c2ecf20Sopenharmony_ci
2768c2ecf20Sopenharmony_ci	hlist_for_each_entry_safe(p, n, &net->ipv6.ip6addrlbl_table.head, list) {
2778c2ecf20Sopenharmony_ci		if (p->prefixlen == prefixlen &&
2788c2ecf20Sopenharmony_ci		    p->ifindex == ifindex &&
2798c2ecf20Sopenharmony_ci		    ipv6_addr_equal(&p->prefix, prefix)) {
2808c2ecf20Sopenharmony_ci			hlist_del_rcu(&p->list);
2818c2ecf20Sopenharmony_ci			kfree_rcu(p, rcu);
2828c2ecf20Sopenharmony_ci			ret = 0;
2838c2ecf20Sopenharmony_ci			break;
2848c2ecf20Sopenharmony_ci		}
2858c2ecf20Sopenharmony_ci	}
2868c2ecf20Sopenharmony_ci	return ret;
2878c2ecf20Sopenharmony_ci}
2888c2ecf20Sopenharmony_ci
2898c2ecf20Sopenharmony_cistatic int ip6addrlbl_del(struct net *net,
2908c2ecf20Sopenharmony_ci			  const struct in6_addr *prefix, int prefixlen,
2918c2ecf20Sopenharmony_ci			  int ifindex)
2928c2ecf20Sopenharmony_ci{
2938c2ecf20Sopenharmony_ci	struct in6_addr prefix_buf;
2948c2ecf20Sopenharmony_ci	int ret;
2958c2ecf20Sopenharmony_ci
2968c2ecf20Sopenharmony_ci	ADDRLABEL(KERN_DEBUG "%s(prefix=%pI6, prefixlen=%d, ifindex=%d)\n",
2978c2ecf20Sopenharmony_ci		  __func__, prefix, prefixlen, ifindex);
2988c2ecf20Sopenharmony_ci
2998c2ecf20Sopenharmony_ci	ipv6_addr_prefix(&prefix_buf, prefix, prefixlen);
3008c2ecf20Sopenharmony_ci	spin_lock(&net->ipv6.ip6addrlbl_table.lock);
3018c2ecf20Sopenharmony_ci	ret = __ip6addrlbl_del(net, &prefix_buf, prefixlen, ifindex);
3028c2ecf20Sopenharmony_ci	spin_unlock(&net->ipv6.ip6addrlbl_table.lock);
3038c2ecf20Sopenharmony_ci	return ret;
3048c2ecf20Sopenharmony_ci}
3058c2ecf20Sopenharmony_ci
3068c2ecf20Sopenharmony_ci/* add default label */
3078c2ecf20Sopenharmony_cistatic int __net_init ip6addrlbl_net_init(struct net *net)
3088c2ecf20Sopenharmony_ci{
3098c2ecf20Sopenharmony_ci	struct ip6addrlbl_entry *p = NULL;
3108c2ecf20Sopenharmony_ci	struct hlist_node *n;
3118c2ecf20Sopenharmony_ci	int err;
3128c2ecf20Sopenharmony_ci	int i;
3138c2ecf20Sopenharmony_ci
3148c2ecf20Sopenharmony_ci	ADDRLABEL(KERN_DEBUG "%s\n", __func__);
3158c2ecf20Sopenharmony_ci
3168c2ecf20Sopenharmony_ci	spin_lock_init(&net->ipv6.ip6addrlbl_table.lock);
3178c2ecf20Sopenharmony_ci	INIT_HLIST_HEAD(&net->ipv6.ip6addrlbl_table.head);
3188c2ecf20Sopenharmony_ci
3198c2ecf20Sopenharmony_ci	for (i = 0; i < ARRAY_SIZE(ip6addrlbl_init_table); i++) {
3208c2ecf20Sopenharmony_ci		err = ip6addrlbl_add(net,
3218c2ecf20Sopenharmony_ci				     ip6addrlbl_init_table[i].prefix,
3228c2ecf20Sopenharmony_ci				     ip6addrlbl_init_table[i].prefixlen,
3238c2ecf20Sopenharmony_ci				     0,
3248c2ecf20Sopenharmony_ci				     ip6addrlbl_init_table[i].label, 0);
3258c2ecf20Sopenharmony_ci		if (err)
3268c2ecf20Sopenharmony_ci			goto err_ip6addrlbl_add;
3278c2ecf20Sopenharmony_ci	}
3288c2ecf20Sopenharmony_ci	return 0;
3298c2ecf20Sopenharmony_ci
3308c2ecf20Sopenharmony_cierr_ip6addrlbl_add:
3318c2ecf20Sopenharmony_ci	hlist_for_each_entry_safe(p, n, &net->ipv6.ip6addrlbl_table.head, list) {
3328c2ecf20Sopenharmony_ci		hlist_del_rcu(&p->list);
3338c2ecf20Sopenharmony_ci		kfree_rcu(p, rcu);
3348c2ecf20Sopenharmony_ci	}
3358c2ecf20Sopenharmony_ci	return err;
3368c2ecf20Sopenharmony_ci}
3378c2ecf20Sopenharmony_ci
3388c2ecf20Sopenharmony_cistatic void __net_exit ip6addrlbl_net_exit(struct net *net)
3398c2ecf20Sopenharmony_ci{
3408c2ecf20Sopenharmony_ci	struct ip6addrlbl_entry *p = NULL;
3418c2ecf20Sopenharmony_ci	struct hlist_node *n;
3428c2ecf20Sopenharmony_ci
3438c2ecf20Sopenharmony_ci	/* Remove all labels belonging to the exiting net */
3448c2ecf20Sopenharmony_ci	spin_lock(&net->ipv6.ip6addrlbl_table.lock);
3458c2ecf20Sopenharmony_ci	hlist_for_each_entry_safe(p, n, &net->ipv6.ip6addrlbl_table.head, list) {
3468c2ecf20Sopenharmony_ci		hlist_del_rcu(&p->list);
3478c2ecf20Sopenharmony_ci		kfree_rcu(p, rcu);
3488c2ecf20Sopenharmony_ci	}
3498c2ecf20Sopenharmony_ci	spin_unlock(&net->ipv6.ip6addrlbl_table.lock);
3508c2ecf20Sopenharmony_ci}
3518c2ecf20Sopenharmony_ci
3528c2ecf20Sopenharmony_cistatic struct pernet_operations ipv6_addr_label_ops = {
3538c2ecf20Sopenharmony_ci	.init = ip6addrlbl_net_init,
3548c2ecf20Sopenharmony_ci	.exit = ip6addrlbl_net_exit,
3558c2ecf20Sopenharmony_ci};
3568c2ecf20Sopenharmony_ci
3578c2ecf20Sopenharmony_ciint __init ipv6_addr_label_init(void)
3588c2ecf20Sopenharmony_ci{
3598c2ecf20Sopenharmony_ci	return register_pernet_subsys(&ipv6_addr_label_ops);
3608c2ecf20Sopenharmony_ci}
3618c2ecf20Sopenharmony_ci
3628c2ecf20Sopenharmony_civoid ipv6_addr_label_cleanup(void)
3638c2ecf20Sopenharmony_ci{
3648c2ecf20Sopenharmony_ci	unregister_pernet_subsys(&ipv6_addr_label_ops);
3658c2ecf20Sopenharmony_ci}
3668c2ecf20Sopenharmony_ci
3678c2ecf20Sopenharmony_cistatic const struct nla_policy ifal_policy[IFAL_MAX+1] = {
3688c2ecf20Sopenharmony_ci	[IFAL_ADDRESS]		= { .len = sizeof(struct in6_addr), },
3698c2ecf20Sopenharmony_ci	[IFAL_LABEL]		= { .len = sizeof(u32), },
3708c2ecf20Sopenharmony_ci};
3718c2ecf20Sopenharmony_ci
3728c2ecf20Sopenharmony_cistatic bool addrlbl_ifindex_exists(struct net *net, int ifindex)
3738c2ecf20Sopenharmony_ci{
3748c2ecf20Sopenharmony_ci
3758c2ecf20Sopenharmony_ci	struct net_device *dev;
3768c2ecf20Sopenharmony_ci
3778c2ecf20Sopenharmony_ci	rcu_read_lock();
3788c2ecf20Sopenharmony_ci	dev = dev_get_by_index_rcu(net, ifindex);
3798c2ecf20Sopenharmony_ci	rcu_read_unlock();
3808c2ecf20Sopenharmony_ci
3818c2ecf20Sopenharmony_ci	return dev != NULL;
3828c2ecf20Sopenharmony_ci}
3838c2ecf20Sopenharmony_ci
3848c2ecf20Sopenharmony_cistatic int ip6addrlbl_newdel(struct sk_buff *skb, struct nlmsghdr *nlh,
3858c2ecf20Sopenharmony_ci			     struct netlink_ext_ack *extack)
3868c2ecf20Sopenharmony_ci{
3878c2ecf20Sopenharmony_ci	struct net *net = sock_net(skb->sk);
3888c2ecf20Sopenharmony_ci	struct ifaddrlblmsg *ifal;
3898c2ecf20Sopenharmony_ci	struct nlattr *tb[IFAL_MAX+1];
3908c2ecf20Sopenharmony_ci	struct in6_addr *pfx;
3918c2ecf20Sopenharmony_ci	u32 label;
3928c2ecf20Sopenharmony_ci	int err = 0;
3938c2ecf20Sopenharmony_ci
3948c2ecf20Sopenharmony_ci	err = nlmsg_parse_deprecated(nlh, sizeof(*ifal), tb, IFAL_MAX,
3958c2ecf20Sopenharmony_ci				     ifal_policy, extack);
3968c2ecf20Sopenharmony_ci	if (err < 0)
3978c2ecf20Sopenharmony_ci		return err;
3988c2ecf20Sopenharmony_ci
3998c2ecf20Sopenharmony_ci	ifal = nlmsg_data(nlh);
4008c2ecf20Sopenharmony_ci
4018c2ecf20Sopenharmony_ci	if (ifal->ifal_family != AF_INET6 ||
4028c2ecf20Sopenharmony_ci	    ifal->ifal_prefixlen > 128)
4038c2ecf20Sopenharmony_ci		return -EINVAL;
4048c2ecf20Sopenharmony_ci
4058c2ecf20Sopenharmony_ci	if (!tb[IFAL_ADDRESS])
4068c2ecf20Sopenharmony_ci		return -EINVAL;
4078c2ecf20Sopenharmony_ci	pfx = nla_data(tb[IFAL_ADDRESS]);
4088c2ecf20Sopenharmony_ci
4098c2ecf20Sopenharmony_ci	if (!tb[IFAL_LABEL])
4108c2ecf20Sopenharmony_ci		return -EINVAL;
4118c2ecf20Sopenharmony_ci	label = nla_get_u32(tb[IFAL_LABEL]);
4128c2ecf20Sopenharmony_ci	if (label == IPV6_ADDR_LABEL_DEFAULT)
4138c2ecf20Sopenharmony_ci		return -EINVAL;
4148c2ecf20Sopenharmony_ci
4158c2ecf20Sopenharmony_ci	switch (nlh->nlmsg_type) {
4168c2ecf20Sopenharmony_ci	case RTM_NEWADDRLABEL:
4178c2ecf20Sopenharmony_ci		if (ifal->ifal_index &&
4188c2ecf20Sopenharmony_ci		    !addrlbl_ifindex_exists(net, ifal->ifal_index))
4198c2ecf20Sopenharmony_ci			return -EINVAL;
4208c2ecf20Sopenharmony_ci
4218c2ecf20Sopenharmony_ci		err = ip6addrlbl_add(net, pfx, ifal->ifal_prefixlen,
4228c2ecf20Sopenharmony_ci				     ifal->ifal_index, label,
4238c2ecf20Sopenharmony_ci				     nlh->nlmsg_flags & NLM_F_REPLACE);
4248c2ecf20Sopenharmony_ci		break;
4258c2ecf20Sopenharmony_ci	case RTM_DELADDRLABEL:
4268c2ecf20Sopenharmony_ci		err = ip6addrlbl_del(net, pfx, ifal->ifal_prefixlen,
4278c2ecf20Sopenharmony_ci				     ifal->ifal_index);
4288c2ecf20Sopenharmony_ci		break;
4298c2ecf20Sopenharmony_ci	default:
4308c2ecf20Sopenharmony_ci		err = -EOPNOTSUPP;
4318c2ecf20Sopenharmony_ci	}
4328c2ecf20Sopenharmony_ci	return err;
4338c2ecf20Sopenharmony_ci}
4348c2ecf20Sopenharmony_ci
4358c2ecf20Sopenharmony_cistatic void ip6addrlbl_putmsg(struct nlmsghdr *nlh,
4368c2ecf20Sopenharmony_ci			      int prefixlen, int ifindex, u32 lseq)
4378c2ecf20Sopenharmony_ci{
4388c2ecf20Sopenharmony_ci	struct ifaddrlblmsg *ifal = nlmsg_data(nlh);
4398c2ecf20Sopenharmony_ci	ifal->ifal_family = AF_INET6;
4408c2ecf20Sopenharmony_ci	ifal->__ifal_reserved = 0;
4418c2ecf20Sopenharmony_ci	ifal->ifal_prefixlen = prefixlen;
4428c2ecf20Sopenharmony_ci	ifal->ifal_flags = 0;
4438c2ecf20Sopenharmony_ci	ifal->ifal_index = ifindex;
4448c2ecf20Sopenharmony_ci	ifal->ifal_seq = lseq;
4458c2ecf20Sopenharmony_ci};
4468c2ecf20Sopenharmony_ci
4478c2ecf20Sopenharmony_cistatic int ip6addrlbl_fill(struct sk_buff *skb,
4488c2ecf20Sopenharmony_ci			   struct ip6addrlbl_entry *p,
4498c2ecf20Sopenharmony_ci			   u32 lseq,
4508c2ecf20Sopenharmony_ci			   u32 portid, u32 seq, int event,
4518c2ecf20Sopenharmony_ci			   unsigned int flags)
4528c2ecf20Sopenharmony_ci{
4538c2ecf20Sopenharmony_ci	struct nlmsghdr *nlh = nlmsg_put(skb, portid, seq, event,
4548c2ecf20Sopenharmony_ci					 sizeof(struct ifaddrlblmsg), flags);
4558c2ecf20Sopenharmony_ci	if (!nlh)
4568c2ecf20Sopenharmony_ci		return -EMSGSIZE;
4578c2ecf20Sopenharmony_ci
4588c2ecf20Sopenharmony_ci	ip6addrlbl_putmsg(nlh, p->prefixlen, p->ifindex, lseq);
4598c2ecf20Sopenharmony_ci
4608c2ecf20Sopenharmony_ci	if (nla_put_in6_addr(skb, IFAL_ADDRESS, &p->prefix) < 0 ||
4618c2ecf20Sopenharmony_ci	    nla_put_u32(skb, IFAL_LABEL, p->label) < 0) {
4628c2ecf20Sopenharmony_ci		nlmsg_cancel(skb, nlh);
4638c2ecf20Sopenharmony_ci		return -EMSGSIZE;
4648c2ecf20Sopenharmony_ci	}
4658c2ecf20Sopenharmony_ci
4668c2ecf20Sopenharmony_ci	nlmsg_end(skb, nlh);
4678c2ecf20Sopenharmony_ci	return 0;
4688c2ecf20Sopenharmony_ci}
4698c2ecf20Sopenharmony_ci
4708c2ecf20Sopenharmony_cistatic int ip6addrlbl_valid_dump_req(const struct nlmsghdr *nlh,
4718c2ecf20Sopenharmony_ci				     struct netlink_ext_ack *extack)
4728c2ecf20Sopenharmony_ci{
4738c2ecf20Sopenharmony_ci	struct ifaddrlblmsg *ifal;
4748c2ecf20Sopenharmony_ci
4758c2ecf20Sopenharmony_ci	if (nlh->nlmsg_len < nlmsg_msg_size(sizeof(*ifal))) {
4768c2ecf20Sopenharmony_ci		NL_SET_ERR_MSG_MOD(extack, "Invalid header for address label dump request");
4778c2ecf20Sopenharmony_ci		return -EINVAL;
4788c2ecf20Sopenharmony_ci	}
4798c2ecf20Sopenharmony_ci
4808c2ecf20Sopenharmony_ci	ifal = nlmsg_data(nlh);
4818c2ecf20Sopenharmony_ci	if (ifal->__ifal_reserved || ifal->ifal_prefixlen ||
4828c2ecf20Sopenharmony_ci	    ifal->ifal_flags || ifal->ifal_index || ifal->ifal_seq) {
4838c2ecf20Sopenharmony_ci		NL_SET_ERR_MSG_MOD(extack, "Invalid values in header for address label dump request");
4848c2ecf20Sopenharmony_ci		return -EINVAL;
4858c2ecf20Sopenharmony_ci	}
4868c2ecf20Sopenharmony_ci
4878c2ecf20Sopenharmony_ci	if (nlmsg_attrlen(nlh, sizeof(*ifal))) {
4888c2ecf20Sopenharmony_ci		NL_SET_ERR_MSG_MOD(extack, "Invalid data after header for address label dump request");
4898c2ecf20Sopenharmony_ci		return -EINVAL;
4908c2ecf20Sopenharmony_ci	}
4918c2ecf20Sopenharmony_ci
4928c2ecf20Sopenharmony_ci	return 0;
4938c2ecf20Sopenharmony_ci}
4948c2ecf20Sopenharmony_ci
4958c2ecf20Sopenharmony_cistatic int ip6addrlbl_dump(struct sk_buff *skb, struct netlink_callback *cb)
4968c2ecf20Sopenharmony_ci{
4978c2ecf20Sopenharmony_ci	const struct nlmsghdr *nlh = cb->nlh;
4988c2ecf20Sopenharmony_ci	struct net *net = sock_net(skb->sk);
4998c2ecf20Sopenharmony_ci	struct ip6addrlbl_entry *p;
5008c2ecf20Sopenharmony_ci	int idx = 0, s_idx = cb->args[0];
5018c2ecf20Sopenharmony_ci	int err;
5028c2ecf20Sopenharmony_ci
5038c2ecf20Sopenharmony_ci	if (cb->strict_check) {
5048c2ecf20Sopenharmony_ci		err = ip6addrlbl_valid_dump_req(nlh, cb->extack);
5058c2ecf20Sopenharmony_ci		if (err < 0)
5068c2ecf20Sopenharmony_ci			return err;
5078c2ecf20Sopenharmony_ci	}
5088c2ecf20Sopenharmony_ci
5098c2ecf20Sopenharmony_ci	rcu_read_lock();
5108c2ecf20Sopenharmony_ci	hlist_for_each_entry_rcu(p, &net->ipv6.ip6addrlbl_table.head, list) {
5118c2ecf20Sopenharmony_ci		if (idx >= s_idx) {
5128c2ecf20Sopenharmony_ci			err = ip6addrlbl_fill(skb, p,
5138c2ecf20Sopenharmony_ci					      net->ipv6.ip6addrlbl_table.seq,
5148c2ecf20Sopenharmony_ci					      NETLINK_CB(cb->skb).portid,
5158c2ecf20Sopenharmony_ci					      nlh->nlmsg_seq,
5168c2ecf20Sopenharmony_ci					      RTM_NEWADDRLABEL,
5178c2ecf20Sopenharmony_ci					      NLM_F_MULTI);
5188c2ecf20Sopenharmony_ci			if (err < 0)
5198c2ecf20Sopenharmony_ci				break;
5208c2ecf20Sopenharmony_ci		}
5218c2ecf20Sopenharmony_ci		idx++;
5228c2ecf20Sopenharmony_ci	}
5238c2ecf20Sopenharmony_ci	rcu_read_unlock();
5248c2ecf20Sopenharmony_ci	cb->args[0] = idx;
5258c2ecf20Sopenharmony_ci	return skb->len;
5268c2ecf20Sopenharmony_ci}
5278c2ecf20Sopenharmony_ci
5288c2ecf20Sopenharmony_cistatic inline int ip6addrlbl_msgsize(void)
5298c2ecf20Sopenharmony_ci{
5308c2ecf20Sopenharmony_ci	return NLMSG_ALIGN(sizeof(struct ifaddrlblmsg))
5318c2ecf20Sopenharmony_ci		+ nla_total_size(16)	/* IFAL_ADDRESS */
5328c2ecf20Sopenharmony_ci		+ nla_total_size(4);	/* IFAL_LABEL */
5338c2ecf20Sopenharmony_ci}
5348c2ecf20Sopenharmony_ci
5358c2ecf20Sopenharmony_cistatic int ip6addrlbl_valid_get_req(struct sk_buff *skb,
5368c2ecf20Sopenharmony_ci				    const struct nlmsghdr *nlh,
5378c2ecf20Sopenharmony_ci				    struct nlattr **tb,
5388c2ecf20Sopenharmony_ci				    struct netlink_ext_ack *extack)
5398c2ecf20Sopenharmony_ci{
5408c2ecf20Sopenharmony_ci	struct ifaddrlblmsg *ifal;
5418c2ecf20Sopenharmony_ci	int i, err;
5428c2ecf20Sopenharmony_ci
5438c2ecf20Sopenharmony_ci	if (nlh->nlmsg_len < nlmsg_msg_size(sizeof(*ifal))) {
5448c2ecf20Sopenharmony_ci		NL_SET_ERR_MSG_MOD(extack, "Invalid header for addrlabel get request");
5458c2ecf20Sopenharmony_ci		return -EINVAL;
5468c2ecf20Sopenharmony_ci	}
5478c2ecf20Sopenharmony_ci
5488c2ecf20Sopenharmony_ci	if (!netlink_strict_get_check(skb))
5498c2ecf20Sopenharmony_ci		return nlmsg_parse_deprecated(nlh, sizeof(*ifal), tb,
5508c2ecf20Sopenharmony_ci					      IFAL_MAX, ifal_policy, extack);
5518c2ecf20Sopenharmony_ci
5528c2ecf20Sopenharmony_ci	ifal = nlmsg_data(nlh);
5538c2ecf20Sopenharmony_ci	if (ifal->__ifal_reserved || ifal->ifal_flags || ifal->ifal_seq) {
5548c2ecf20Sopenharmony_ci		NL_SET_ERR_MSG_MOD(extack, "Invalid values in header for addrlabel get request");
5558c2ecf20Sopenharmony_ci		return -EINVAL;
5568c2ecf20Sopenharmony_ci	}
5578c2ecf20Sopenharmony_ci
5588c2ecf20Sopenharmony_ci	err = nlmsg_parse_deprecated_strict(nlh, sizeof(*ifal), tb, IFAL_MAX,
5598c2ecf20Sopenharmony_ci					    ifal_policy, extack);
5608c2ecf20Sopenharmony_ci	if (err)
5618c2ecf20Sopenharmony_ci		return err;
5628c2ecf20Sopenharmony_ci
5638c2ecf20Sopenharmony_ci	for (i = 0; i <= IFAL_MAX; i++) {
5648c2ecf20Sopenharmony_ci		if (!tb[i])
5658c2ecf20Sopenharmony_ci			continue;
5668c2ecf20Sopenharmony_ci
5678c2ecf20Sopenharmony_ci		switch (i) {
5688c2ecf20Sopenharmony_ci		case IFAL_ADDRESS:
5698c2ecf20Sopenharmony_ci			break;
5708c2ecf20Sopenharmony_ci		default:
5718c2ecf20Sopenharmony_ci			NL_SET_ERR_MSG_MOD(extack, "Unsupported attribute in addrlabel get request");
5728c2ecf20Sopenharmony_ci			return -EINVAL;
5738c2ecf20Sopenharmony_ci		}
5748c2ecf20Sopenharmony_ci	}
5758c2ecf20Sopenharmony_ci
5768c2ecf20Sopenharmony_ci	return 0;
5778c2ecf20Sopenharmony_ci}
5788c2ecf20Sopenharmony_ci
5798c2ecf20Sopenharmony_cistatic int ip6addrlbl_get(struct sk_buff *in_skb, struct nlmsghdr *nlh,
5808c2ecf20Sopenharmony_ci			  struct netlink_ext_ack *extack)
5818c2ecf20Sopenharmony_ci{
5828c2ecf20Sopenharmony_ci	struct net *net = sock_net(in_skb->sk);
5838c2ecf20Sopenharmony_ci	struct ifaddrlblmsg *ifal;
5848c2ecf20Sopenharmony_ci	struct nlattr *tb[IFAL_MAX+1];
5858c2ecf20Sopenharmony_ci	struct in6_addr *addr;
5868c2ecf20Sopenharmony_ci	u32 lseq;
5878c2ecf20Sopenharmony_ci	int err = 0;
5888c2ecf20Sopenharmony_ci	struct ip6addrlbl_entry *p;
5898c2ecf20Sopenharmony_ci	struct sk_buff *skb;
5908c2ecf20Sopenharmony_ci
5918c2ecf20Sopenharmony_ci	err = ip6addrlbl_valid_get_req(in_skb, nlh, tb, extack);
5928c2ecf20Sopenharmony_ci	if (err < 0)
5938c2ecf20Sopenharmony_ci		return err;
5948c2ecf20Sopenharmony_ci
5958c2ecf20Sopenharmony_ci	ifal = nlmsg_data(nlh);
5968c2ecf20Sopenharmony_ci
5978c2ecf20Sopenharmony_ci	if (ifal->ifal_family != AF_INET6 ||
5988c2ecf20Sopenharmony_ci	    ifal->ifal_prefixlen != 128)
5998c2ecf20Sopenharmony_ci		return -EINVAL;
6008c2ecf20Sopenharmony_ci
6018c2ecf20Sopenharmony_ci	if (ifal->ifal_index &&
6028c2ecf20Sopenharmony_ci	    !addrlbl_ifindex_exists(net, ifal->ifal_index))
6038c2ecf20Sopenharmony_ci		return -EINVAL;
6048c2ecf20Sopenharmony_ci
6058c2ecf20Sopenharmony_ci	if (!tb[IFAL_ADDRESS])
6068c2ecf20Sopenharmony_ci		return -EINVAL;
6078c2ecf20Sopenharmony_ci	addr = nla_data(tb[IFAL_ADDRESS]);
6088c2ecf20Sopenharmony_ci
6098c2ecf20Sopenharmony_ci	skb = nlmsg_new(ip6addrlbl_msgsize(), GFP_KERNEL);
6108c2ecf20Sopenharmony_ci	if (!skb)
6118c2ecf20Sopenharmony_ci		return -ENOBUFS;
6128c2ecf20Sopenharmony_ci
6138c2ecf20Sopenharmony_ci	err = -ESRCH;
6148c2ecf20Sopenharmony_ci
6158c2ecf20Sopenharmony_ci	rcu_read_lock();
6168c2ecf20Sopenharmony_ci	p = __ipv6_addr_label(net, addr, ipv6_addr_type(addr), ifal->ifal_index);
6178c2ecf20Sopenharmony_ci	lseq = net->ipv6.ip6addrlbl_table.seq;
6188c2ecf20Sopenharmony_ci	if (p)
6198c2ecf20Sopenharmony_ci		err = ip6addrlbl_fill(skb, p, lseq,
6208c2ecf20Sopenharmony_ci				      NETLINK_CB(in_skb).portid,
6218c2ecf20Sopenharmony_ci				      nlh->nlmsg_seq,
6228c2ecf20Sopenharmony_ci				      RTM_NEWADDRLABEL, 0);
6238c2ecf20Sopenharmony_ci	rcu_read_unlock();
6248c2ecf20Sopenharmony_ci
6258c2ecf20Sopenharmony_ci	if (err < 0) {
6268c2ecf20Sopenharmony_ci		WARN_ON(err == -EMSGSIZE);
6278c2ecf20Sopenharmony_ci		kfree_skb(skb);
6288c2ecf20Sopenharmony_ci	} else {
6298c2ecf20Sopenharmony_ci		err = rtnl_unicast(skb, net, NETLINK_CB(in_skb).portid);
6308c2ecf20Sopenharmony_ci	}
6318c2ecf20Sopenharmony_ci	return err;
6328c2ecf20Sopenharmony_ci}
6338c2ecf20Sopenharmony_ci
6348c2ecf20Sopenharmony_ciint __init ipv6_addr_label_rtnl_register(void)
6358c2ecf20Sopenharmony_ci{
6368c2ecf20Sopenharmony_ci	int ret;
6378c2ecf20Sopenharmony_ci
6388c2ecf20Sopenharmony_ci	ret = rtnl_register_module(THIS_MODULE, PF_INET6, RTM_NEWADDRLABEL,
6398c2ecf20Sopenharmony_ci				   ip6addrlbl_newdel,
6408c2ecf20Sopenharmony_ci				   NULL, RTNL_FLAG_DOIT_UNLOCKED);
6418c2ecf20Sopenharmony_ci	if (ret < 0)
6428c2ecf20Sopenharmony_ci		return ret;
6438c2ecf20Sopenharmony_ci	ret = rtnl_register_module(THIS_MODULE, PF_INET6, RTM_DELADDRLABEL,
6448c2ecf20Sopenharmony_ci				   ip6addrlbl_newdel,
6458c2ecf20Sopenharmony_ci				   NULL, RTNL_FLAG_DOIT_UNLOCKED);
6468c2ecf20Sopenharmony_ci	if (ret < 0)
6478c2ecf20Sopenharmony_ci		return ret;
6488c2ecf20Sopenharmony_ci	ret = rtnl_register_module(THIS_MODULE, PF_INET6, RTM_GETADDRLABEL,
6498c2ecf20Sopenharmony_ci				   ip6addrlbl_get,
6508c2ecf20Sopenharmony_ci				   ip6addrlbl_dump, RTNL_FLAG_DOIT_UNLOCKED);
6518c2ecf20Sopenharmony_ci	return ret;
6528c2ecf20Sopenharmony_ci}
653