162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * IPv6 Address Label subsystem
462306a36Sopenharmony_ci * for the IPv6 "Default" Source Address Selection
562306a36Sopenharmony_ci *
662306a36Sopenharmony_ci * Copyright (C)2007 USAGI/WIDE Project
762306a36Sopenharmony_ci */
862306a36Sopenharmony_ci/*
962306a36Sopenharmony_ci * Author:
1062306a36Sopenharmony_ci *	YOSHIFUJI Hideaki @ USAGI/WIDE Project <yoshfuji@linux-ipv6.org>
1162306a36Sopenharmony_ci */
1262306a36Sopenharmony_ci
1362306a36Sopenharmony_ci#include <linux/kernel.h>
1462306a36Sopenharmony_ci#include <linux/list.h>
1562306a36Sopenharmony_ci#include <linux/rcupdate.h>
1662306a36Sopenharmony_ci#include <linux/in6.h>
1762306a36Sopenharmony_ci#include <linux/slab.h>
1862306a36Sopenharmony_ci#include <net/addrconf.h>
1962306a36Sopenharmony_ci#include <linux/if_addrlabel.h>
2062306a36Sopenharmony_ci#include <linux/netlink.h>
2162306a36Sopenharmony_ci#include <linux/rtnetlink.h>
2262306a36Sopenharmony_ci
2362306a36Sopenharmony_ci#if 0
2462306a36Sopenharmony_ci#define ADDRLABEL(x...) printk(x)
2562306a36Sopenharmony_ci#else
2662306a36Sopenharmony_ci#define ADDRLABEL(x...) do { ; } while (0)
2762306a36Sopenharmony_ci#endif
2862306a36Sopenharmony_ci
2962306a36Sopenharmony_ci/*
3062306a36Sopenharmony_ci * Policy Table
3162306a36Sopenharmony_ci */
3262306a36Sopenharmony_cistruct ip6addrlbl_entry {
3362306a36Sopenharmony_ci	struct in6_addr prefix;
3462306a36Sopenharmony_ci	int prefixlen;
3562306a36Sopenharmony_ci	int ifindex;
3662306a36Sopenharmony_ci	int addrtype;
3762306a36Sopenharmony_ci	u32 label;
3862306a36Sopenharmony_ci	struct hlist_node list;
3962306a36Sopenharmony_ci	struct rcu_head rcu;
4062306a36Sopenharmony_ci};
4162306a36Sopenharmony_ci
4262306a36Sopenharmony_ci/*
4362306a36Sopenharmony_ci * Default policy table (RFC6724 + extensions)
4462306a36Sopenharmony_ci *
4562306a36Sopenharmony_ci * prefix		addr_type	label
4662306a36Sopenharmony_ci * -------------------------------------------------------------------------
4762306a36Sopenharmony_ci * ::1/128		LOOPBACK	0
4862306a36Sopenharmony_ci * ::/0			N/A		1
4962306a36Sopenharmony_ci * 2002::/16		N/A		2
5062306a36Sopenharmony_ci * ::/96		COMPATv4	3
5162306a36Sopenharmony_ci * ::ffff:0:0/96	V4MAPPED	4
5262306a36Sopenharmony_ci * fc00::/7		N/A		5		ULA (RFC 4193)
5362306a36Sopenharmony_ci * 2001::/32		N/A		6		Teredo (RFC 4380)
5462306a36Sopenharmony_ci * 2001:10::/28		N/A		7		ORCHID (RFC 4843)
5562306a36Sopenharmony_ci * fec0::/10		N/A		11		Site-local
5662306a36Sopenharmony_ci *							(deprecated by RFC3879)
5762306a36Sopenharmony_ci * 3ffe::/16		N/A		12		6bone
5862306a36Sopenharmony_ci *
5962306a36Sopenharmony_ci * Note: 0xffffffff is used if we do not have any policies.
6062306a36Sopenharmony_ci * Note: Labels for ULA and 6to4 are different from labels listed in RFC6724.
6162306a36Sopenharmony_ci */
6262306a36Sopenharmony_ci
6362306a36Sopenharmony_ci#define IPV6_ADDR_LABEL_DEFAULT	0xffffffffUL
6462306a36Sopenharmony_ci
6562306a36Sopenharmony_cistatic const __net_initconst struct ip6addrlbl_init_table
6662306a36Sopenharmony_ci{
6762306a36Sopenharmony_ci	const struct in6_addr *prefix;
6862306a36Sopenharmony_ci	int prefixlen;
6962306a36Sopenharmony_ci	u32 label;
7062306a36Sopenharmony_ci} ip6addrlbl_init_table[] = {
7162306a36Sopenharmony_ci	{	/* ::/0 */
7262306a36Sopenharmony_ci		.prefix = &in6addr_any,
7362306a36Sopenharmony_ci		.label = 1,
7462306a36Sopenharmony_ci	}, {	/* fc00::/7 */
7562306a36Sopenharmony_ci		.prefix = &(struct in6_addr){ { { 0xfc } } } ,
7662306a36Sopenharmony_ci		.prefixlen = 7,
7762306a36Sopenharmony_ci		.label = 5,
7862306a36Sopenharmony_ci	}, {	/* fec0::/10 */
7962306a36Sopenharmony_ci		.prefix = &(struct in6_addr){ { { 0xfe, 0xc0 } } },
8062306a36Sopenharmony_ci		.prefixlen = 10,
8162306a36Sopenharmony_ci		.label = 11,
8262306a36Sopenharmony_ci	}, {	/* 2002::/16 */
8362306a36Sopenharmony_ci		.prefix = &(struct in6_addr){ { { 0x20, 0x02 } } },
8462306a36Sopenharmony_ci		.prefixlen = 16,
8562306a36Sopenharmony_ci		.label = 2,
8662306a36Sopenharmony_ci	}, {	/* 3ffe::/16 */
8762306a36Sopenharmony_ci		.prefix = &(struct in6_addr){ { { 0x3f, 0xfe } } },
8862306a36Sopenharmony_ci		.prefixlen = 16,
8962306a36Sopenharmony_ci		.label = 12,
9062306a36Sopenharmony_ci	}, {	/* 2001::/32 */
9162306a36Sopenharmony_ci		.prefix = &(struct in6_addr){ { { 0x20, 0x01 } } },
9262306a36Sopenharmony_ci		.prefixlen = 32,
9362306a36Sopenharmony_ci		.label = 6,
9462306a36Sopenharmony_ci	}, {	/* 2001:10::/28 */
9562306a36Sopenharmony_ci		.prefix = &(struct in6_addr){ { { 0x20, 0x01, 0x00, 0x10 } } },
9662306a36Sopenharmony_ci		.prefixlen = 28,
9762306a36Sopenharmony_ci		.label = 7,
9862306a36Sopenharmony_ci	}, {	/* ::ffff:0:0 */
9962306a36Sopenharmony_ci		.prefix = &(struct in6_addr){ { { [10] = 0xff, [11] = 0xff } } },
10062306a36Sopenharmony_ci		.prefixlen = 96,
10162306a36Sopenharmony_ci		.label = 4,
10262306a36Sopenharmony_ci	}, {	/* ::/96 */
10362306a36Sopenharmony_ci		.prefix = &in6addr_any,
10462306a36Sopenharmony_ci		.prefixlen = 96,
10562306a36Sopenharmony_ci		.label = 3,
10662306a36Sopenharmony_ci	}, {	/* ::1/128 */
10762306a36Sopenharmony_ci		.prefix = &in6addr_loopback,
10862306a36Sopenharmony_ci		.prefixlen = 128,
10962306a36Sopenharmony_ci		.label = 0,
11062306a36Sopenharmony_ci	}
11162306a36Sopenharmony_ci};
11262306a36Sopenharmony_ci
11362306a36Sopenharmony_ci/* Find label */
11462306a36Sopenharmony_cistatic bool __ip6addrlbl_match(const struct ip6addrlbl_entry *p,
11562306a36Sopenharmony_ci			       const struct in6_addr *addr,
11662306a36Sopenharmony_ci			       int addrtype, int ifindex)
11762306a36Sopenharmony_ci{
11862306a36Sopenharmony_ci	if (p->ifindex && p->ifindex != ifindex)
11962306a36Sopenharmony_ci		return false;
12062306a36Sopenharmony_ci	if (p->addrtype && p->addrtype != addrtype)
12162306a36Sopenharmony_ci		return false;
12262306a36Sopenharmony_ci	if (!ipv6_prefix_equal(addr, &p->prefix, p->prefixlen))
12362306a36Sopenharmony_ci		return false;
12462306a36Sopenharmony_ci	return true;
12562306a36Sopenharmony_ci}
12662306a36Sopenharmony_ci
12762306a36Sopenharmony_cistatic struct ip6addrlbl_entry *__ipv6_addr_label(struct net *net,
12862306a36Sopenharmony_ci						  const struct in6_addr *addr,
12962306a36Sopenharmony_ci						  int type, int ifindex)
13062306a36Sopenharmony_ci{
13162306a36Sopenharmony_ci	struct ip6addrlbl_entry *p;
13262306a36Sopenharmony_ci
13362306a36Sopenharmony_ci	hlist_for_each_entry_rcu(p, &net->ipv6.ip6addrlbl_table.head, list) {
13462306a36Sopenharmony_ci		if (__ip6addrlbl_match(p, addr, type, ifindex))
13562306a36Sopenharmony_ci			return p;
13662306a36Sopenharmony_ci	}
13762306a36Sopenharmony_ci	return NULL;
13862306a36Sopenharmony_ci}
13962306a36Sopenharmony_ci
14062306a36Sopenharmony_ciu32 ipv6_addr_label(struct net *net,
14162306a36Sopenharmony_ci		    const struct in6_addr *addr, int type, int ifindex)
14262306a36Sopenharmony_ci{
14362306a36Sopenharmony_ci	u32 label;
14462306a36Sopenharmony_ci	struct ip6addrlbl_entry *p;
14562306a36Sopenharmony_ci
14662306a36Sopenharmony_ci	type &= IPV6_ADDR_MAPPED | IPV6_ADDR_COMPATv4 | IPV6_ADDR_LOOPBACK;
14762306a36Sopenharmony_ci
14862306a36Sopenharmony_ci	rcu_read_lock();
14962306a36Sopenharmony_ci	p = __ipv6_addr_label(net, addr, type, ifindex);
15062306a36Sopenharmony_ci	label = p ? p->label : IPV6_ADDR_LABEL_DEFAULT;
15162306a36Sopenharmony_ci	rcu_read_unlock();
15262306a36Sopenharmony_ci
15362306a36Sopenharmony_ci	ADDRLABEL(KERN_DEBUG "%s(addr=%pI6, type=%d, ifindex=%d) => %08x\n",
15462306a36Sopenharmony_ci		  __func__, addr, type, ifindex, label);
15562306a36Sopenharmony_ci
15662306a36Sopenharmony_ci	return label;
15762306a36Sopenharmony_ci}
15862306a36Sopenharmony_ci
15962306a36Sopenharmony_ci/* allocate one entry */
16062306a36Sopenharmony_cistatic struct ip6addrlbl_entry *ip6addrlbl_alloc(const struct in6_addr *prefix,
16162306a36Sopenharmony_ci						 int prefixlen, int ifindex,
16262306a36Sopenharmony_ci						 u32 label)
16362306a36Sopenharmony_ci{
16462306a36Sopenharmony_ci	struct ip6addrlbl_entry *newp;
16562306a36Sopenharmony_ci	int addrtype;
16662306a36Sopenharmony_ci
16762306a36Sopenharmony_ci	ADDRLABEL(KERN_DEBUG "%s(prefix=%pI6, prefixlen=%d, ifindex=%d, label=%u)\n",
16862306a36Sopenharmony_ci		  __func__, prefix, prefixlen, ifindex, (unsigned int)label);
16962306a36Sopenharmony_ci
17062306a36Sopenharmony_ci	addrtype = ipv6_addr_type(prefix) & (IPV6_ADDR_MAPPED | IPV6_ADDR_COMPATv4 | IPV6_ADDR_LOOPBACK);
17162306a36Sopenharmony_ci
17262306a36Sopenharmony_ci	switch (addrtype) {
17362306a36Sopenharmony_ci	case IPV6_ADDR_MAPPED:
17462306a36Sopenharmony_ci		if (prefixlen > 96)
17562306a36Sopenharmony_ci			return ERR_PTR(-EINVAL);
17662306a36Sopenharmony_ci		if (prefixlen < 96)
17762306a36Sopenharmony_ci			addrtype = 0;
17862306a36Sopenharmony_ci		break;
17962306a36Sopenharmony_ci	case IPV6_ADDR_COMPATv4:
18062306a36Sopenharmony_ci		if (prefixlen != 96)
18162306a36Sopenharmony_ci			addrtype = 0;
18262306a36Sopenharmony_ci		break;
18362306a36Sopenharmony_ci	case IPV6_ADDR_LOOPBACK:
18462306a36Sopenharmony_ci		if (prefixlen != 128)
18562306a36Sopenharmony_ci			addrtype = 0;
18662306a36Sopenharmony_ci		break;
18762306a36Sopenharmony_ci	}
18862306a36Sopenharmony_ci
18962306a36Sopenharmony_ci	newp = kmalloc(sizeof(*newp), GFP_KERNEL);
19062306a36Sopenharmony_ci	if (!newp)
19162306a36Sopenharmony_ci		return ERR_PTR(-ENOMEM);
19262306a36Sopenharmony_ci
19362306a36Sopenharmony_ci	ipv6_addr_prefix(&newp->prefix, prefix, prefixlen);
19462306a36Sopenharmony_ci	newp->prefixlen = prefixlen;
19562306a36Sopenharmony_ci	newp->ifindex = ifindex;
19662306a36Sopenharmony_ci	newp->addrtype = addrtype;
19762306a36Sopenharmony_ci	newp->label = label;
19862306a36Sopenharmony_ci	INIT_HLIST_NODE(&newp->list);
19962306a36Sopenharmony_ci	return newp;
20062306a36Sopenharmony_ci}
20162306a36Sopenharmony_ci
20262306a36Sopenharmony_ci/* add a label */
20362306a36Sopenharmony_cistatic int __ip6addrlbl_add(struct net *net, struct ip6addrlbl_entry *newp,
20462306a36Sopenharmony_ci			    int replace)
20562306a36Sopenharmony_ci{
20662306a36Sopenharmony_ci	struct ip6addrlbl_entry *last = NULL, *p = NULL;
20762306a36Sopenharmony_ci	struct hlist_node *n;
20862306a36Sopenharmony_ci	int ret = 0;
20962306a36Sopenharmony_ci
21062306a36Sopenharmony_ci	ADDRLABEL(KERN_DEBUG "%s(newp=%p, replace=%d)\n", __func__, newp,
21162306a36Sopenharmony_ci		  replace);
21262306a36Sopenharmony_ci
21362306a36Sopenharmony_ci	hlist_for_each_entry_safe(p, n,	&net->ipv6.ip6addrlbl_table.head, list) {
21462306a36Sopenharmony_ci		if (p->prefixlen == newp->prefixlen &&
21562306a36Sopenharmony_ci		    p->ifindex == newp->ifindex &&
21662306a36Sopenharmony_ci		    ipv6_addr_equal(&p->prefix, &newp->prefix)) {
21762306a36Sopenharmony_ci			if (!replace) {
21862306a36Sopenharmony_ci				ret = -EEXIST;
21962306a36Sopenharmony_ci				goto out;
22062306a36Sopenharmony_ci			}
22162306a36Sopenharmony_ci			hlist_replace_rcu(&p->list, &newp->list);
22262306a36Sopenharmony_ci			kfree_rcu(p, rcu);
22362306a36Sopenharmony_ci			goto out;
22462306a36Sopenharmony_ci		} else if ((p->prefixlen == newp->prefixlen && !p->ifindex) ||
22562306a36Sopenharmony_ci			   (p->prefixlen < newp->prefixlen)) {
22662306a36Sopenharmony_ci			hlist_add_before_rcu(&newp->list, &p->list);
22762306a36Sopenharmony_ci			goto out;
22862306a36Sopenharmony_ci		}
22962306a36Sopenharmony_ci		last = p;
23062306a36Sopenharmony_ci	}
23162306a36Sopenharmony_ci	if (last)
23262306a36Sopenharmony_ci		hlist_add_behind_rcu(&newp->list, &last->list);
23362306a36Sopenharmony_ci	else
23462306a36Sopenharmony_ci		hlist_add_head_rcu(&newp->list, &net->ipv6.ip6addrlbl_table.head);
23562306a36Sopenharmony_ciout:
23662306a36Sopenharmony_ci	if (!ret)
23762306a36Sopenharmony_ci		net->ipv6.ip6addrlbl_table.seq++;
23862306a36Sopenharmony_ci	return ret;
23962306a36Sopenharmony_ci}
24062306a36Sopenharmony_ci
24162306a36Sopenharmony_ci/* add a label */
24262306a36Sopenharmony_cistatic int ip6addrlbl_add(struct net *net,
24362306a36Sopenharmony_ci			  const struct in6_addr *prefix, int prefixlen,
24462306a36Sopenharmony_ci			  int ifindex, u32 label, int replace)
24562306a36Sopenharmony_ci{
24662306a36Sopenharmony_ci	struct ip6addrlbl_entry *newp;
24762306a36Sopenharmony_ci	int ret = 0;
24862306a36Sopenharmony_ci
24962306a36Sopenharmony_ci	ADDRLABEL(KERN_DEBUG "%s(prefix=%pI6, prefixlen=%d, ifindex=%d, label=%u, replace=%d)\n",
25062306a36Sopenharmony_ci		  __func__, prefix, prefixlen, ifindex, (unsigned int)label,
25162306a36Sopenharmony_ci		  replace);
25262306a36Sopenharmony_ci
25362306a36Sopenharmony_ci	newp = ip6addrlbl_alloc(prefix, prefixlen, ifindex, label);
25462306a36Sopenharmony_ci	if (IS_ERR(newp))
25562306a36Sopenharmony_ci		return PTR_ERR(newp);
25662306a36Sopenharmony_ci	spin_lock(&net->ipv6.ip6addrlbl_table.lock);
25762306a36Sopenharmony_ci	ret = __ip6addrlbl_add(net, newp, replace);
25862306a36Sopenharmony_ci	spin_unlock(&net->ipv6.ip6addrlbl_table.lock);
25962306a36Sopenharmony_ci	if (ret)
26062306a36Sopenharmony_ci		kfree(newp);
26162306a36Sopenharmony_ci	return ret;
26262306a36Sopenharmony_ci}
26362306a36Sopenharmony_ci
26462306a36Sopenharmony_ci/* remove a label */
26562306a36Sopenharmony_cistatic int __ip6addrlbl_del(struct net *net,
26662306a36Sopenharmony_ci			    const struct in6_addr *prefix, int prefixlen,
26762306a36Sopenharmony_ci			    int ifindex)
26862306a36Sopenharmony_ci{
26962306a36Sopenharmony_ci	struct ip6addrlbl_entry *p = NULL;
27062306a36Sopenharmony_ci	struct hlist_node *n;
27162306a36Sopenharmony_ci	int ret = -ESRCH;
27262306a36Sopenharmony_ci
27362306a36Sopenharmony_ci	ADDRLABEL(KERN_DEBUG "%s(prefix=%pI6, prefixlen=%d, ifindex=%d)\n",
27462306a36Sopenharmony_ci		  __func__, prefix, prefixlen, ifindex);
27562306a36Sopenharmony_ci
27662306a36Sopenharmony_ci	hlist_for_each_entry_safe(p, n, &net->ipv6.ip6addrlbl_table.head, list) {
27762306a36Sopenharmony_ci		if (p->prefixlen == prefixlen &&
27862306a36Sopenharmony_ci		    p->ifindex == ifindex &&
27962306a36Sopenharmony_ci		    ipv6_addr_equal(&p->prefix, prefix)) {
28062306a36Sopenharmony_ci			hlist_del_rcu(&p->list);
28162306a36Sopenharmony_ci			kfree_rcu(p, rcu);
28262306a36Sopenharmony_ci			ret = 0;
28362306a36Sopenharmony_ci			break;
28462306a36Sopenharmony_ci		}
28562306a36Sopenharmony_ci	}
28662306a36Sopenharmony_ci	return ret;
28762306a36Sopenharmony_ci}
28862306a36Sopenharmony_ci
28962306a36Sopenharmony_cistatic int ip6addrlbl_del(struct net *net,
29062306a36Sopenharmony_ci			  const struct in6_addr *prefix, int prefixlen,
29162306a36Sopenharmony_ci			  int ifindex)
29262306a36Sopenharmony_ci{
29362306a36Sopenharmony_ci	struct in6_addr prefix_buf;
29462306a36Sopenharmony_ci	int ret;
29562306a36Sopenharmony_ci
29662306a36Sopenharmony_ci	ADDRLABEL(KERN_DEBUG "%s(prefix=%pI6, prefixlen=%d, ifindex=%d)\n",
29762306a36Sopenharmony_ci		  __func__, prefix, prefixlen, ifindex);
29862306a36Sopenharmony_ci
29962306a36Sopenharmony_ci	ipv6_addr_prefix(&prefix_buf, prefix, prefixlen);
30062306a36Sopenharmony_ci	spin_lock(&net->ipv6.ip6addrlbl_table.lock);
30162306a36Sopenharmony_ci	ret = __ip6addrlbl_del(net, &prefix_buf, prefixlen, ifindex);
30262306a36Sopenharmony_ci	spin_unlock(&net->ipv6.ip6addrlbl_table.lock);
30362306a36Sopenharmony_ci	return ret;
30462306a36Sopenharmony_ci}
30562306a36Sopenharmony_ci
30662306a36Sopenharmony_ci/* add default label */
30762306a36Sopenharmony_cistatic int __net_init ip6addrlbl_net_init(struct net *net)
30862306a36Sopenharmony_ci{
30962306a36Sopenharmony_ci	struct ip6addrlbl_entry *p = NULL;
31062306a36Sopenharmony_ci	struct hlist_node *n;
31162306a36Sopenharmony_ci	int err;
31262306a36Sopenharmony_ci	int i;
31362306a36Sopenharmony_ci
31462306a36Sopenharmony_ci	ADDRLABEL(KERN_DEBUG "%s\n", __func__);
31562306a36Sopenharmony_ci
31662306a36Sopenharmony_ci	spin_lock_init(&net->ipv6.ip6addrlbl_table.lock);
31762306a36Sopenharmony_ci	INIT_HLIST_HEAD(&net->ipv6.ip6addrlbl_table.head);
31862306a36Sopenharmony_ci
31962306a36Sopenharmony_ci	for (i = 0; i < ARRAY_SIZE(ip6addrlbl_init_table); i++) {
32062306a36Sopenharmony_ci		err = ip6addrlbl_add(net,
32162306a36Sopenharmony_ci				     ip6addrlbl_init_table[i].prefix,
32262306a36Sopenharmony_ci				     ip6addrlbl_init_table[i].prefixlen,
32362306a36Sopenharmony_ci				     0,
32462306a36Sopenharmony_ci				     ip6addrlbl_init_table[i].label, 0);
32562306a36Sopenharmony_ci		if (err)
32662306a36Sopenharmony_ci			goto err_ip6addrlbl_add;
32762306a36Sopenharmony_ci	}
32862306a36Sopenharmony_ci	return 0;
32962306a36Sopenharmony_ci
33062306a36Sopenharmony_cierr_ip6addrlbl_add:
33162306a36Sopenharmony_ci	hlist_for_each_entry_safe(p, n, &net->ipv6.ip6addrlbl_table.head, list) {
33262306a36Sopenharmony_ci		hlist_del_rcu(&p->list);
33362306a36Sopenharmony_ci		kfree_rcu(p, rcu);
33462306a36Sopenharmony_ci	}
33562306a36Sopenharmony_ci	return err;
33662306a36Sopenharmony_ci}
33762306a36Sopenharmony_ci
33862306a36Sopenharmony_cistatic void __net_exit ip6addrlbl_net_exit(struct net *net)
33962306a36Sopenharmony_ci{
34062306a36Sopenharmony_ci	struct ip6addrlbl_entry *p = NULL;
34162306a36Sopenharmony_ci	struct hlist_node *n;
34262306a36Sopenharmony_ci
34362306a36Sopenharmony_ci	/* Remove all labels belonging to the exiting net */
34462306a36Sopenharmony_ci	spin_lock(&net->ipv6.ip6addrlbl_table.lock);
34562306a36Sopenharmony_ci	hlist_for_each_entry_safe(p, n, &net->ipv6.ip6addrlbl_table.head, list) {
34662306a36Sopenharmony_ci		hlist_del_rcu(&p->list);
34762306a36Sopenharmony_ci		kfree_rcu(p, rcu);
34862306a36Sopenharmony_ci	}
34962306a36Sopenharmony_ci	spin_unlock(&net->ipv6.ip6addrlbl_table.lock);
35062306a36Sopenharmony_ci}
35162306a36Sopenharmony_ci
35262306a36Sopenharmony_cistatic struct pernet_operations ipv6_addr_label_ops = {
35362306a36Sopenharmony_ci	.init = ip6addrlbl_net_init,
35462306a36Sopenharmony_ci	.exit = ip6addrlbl_net_exit,
35562306a36Sopenharmony_ci};
35662306a36Sopenharmony_ci
35762306a36Sopenharmony_ciint __init ipv6_addr_label_init(void)
35862306a36Sopenharmony_ci{
35962306a36Sopenharmony_ci	return register_pernet_subsys(&ipv6_addr_label_ops);
36062306a36Sopenharmony_ci}
36162306a36Sopenharmony_ci
36262306a36Sopenharmony_civoid ipv6_addr_label_cleanup(void)
36362306a36Sopenharmony_ci{
36462306a36Sopenharmony_ci	unregister_pernet_subsys(&ipv6_addr_label_ops);
36562306a36Sopenharmony_ci}
36662306a36Sopenharmony_ci
36762306a36Sopenharmony_cistatic const struct nla_policy ifal_policy[IFAL_MAX+1] = {
36862306a36Sopenharmony_ci	[IFAL_ADDRESS]		= { .len = sizeof(struct in6_addr), },
36962306a36Sopenharmony_ci	[IFAL_LABEL]		= { .len = sizeof(u32), },
37062306a36Sopenharmony_ci};
37162306a36Sopenharmony_ci
37262306a36Sopenharmony_cistatic bool addrlbl_ifindex_exists(struct net *net, int ifindex)
37362306a36Sopenharmony_ci{
37462306a36Sopenharmony_ci
37562306a36Sopenharmony_ci	struct net_device *dev;
37662306a36Sopenharmony_ci
37762306a36Sopenharmony_ci	rcu_read_lock();
37862306a36Sopenharmony_ci	dev = dev_get_by_index_rcu(net, ifindex);
37962306a36Sopenharmony_ci	rcu_read_unlock();
38062306a36Sopenharmony_ci
38162306a36Sopenharmony_ci	return dev != NULL;
38262306a36Sopenharmony_ci}
38362306a36Sopenharmony_ci
38462306a36Sopenharmony_cistatic int ip6addrlbl_newdel(struct sk_buff *skb, struct nlmsghdr *nlh,
38562306a36Sopenharmony_ci			     struct netlink_ext_ack *extack)
38662306a36Sopenharmony_ci{
38762306a36Sopenharmony_ci	struct net *net = sock_net(skb->sk);
38862306a36Sopenharmony_ci	struct ifaddrlblmsg *ifal;
38962306a36Sopenharmony_ci	struct nlattr *tb[IFAL_MAX+1];
39062306a36Sopenharmony_ci	struct in6_addr *pfx;
39162306a36Sopenharmony_ci	u32 label;
39262306a36Sopenharmony_ci	int err = 0;
39362306a36Sopenharmony_ci
39462306a36Sopenharmony_ci	err = nlmsg_parse_deprecated(nlh, sizeof(*ifal), tb, IFAL_MAX,
39562306a36Sopenharmony_ci				     ifal_policy, extack);
39662306a36Sopenharmony_ci	if (err < 0)
39762306a36Sopenharmony_ci		return err;
39862306a36Sopenharmony_ci
39962306a36Sopenharmony_ci	ifal = nlmsg_data(nlh);
40062306a36Sopenharmony_ci
40162306a36Sopenharmony_ci	if (ifal->ifal_family != AF_INET6 ||
40262306a36Sopenharmony_ci	    ifal->ifal_prefixlen > 128)
40362306a36Sopenharmony_ci		return -EINVAL;
40462306a36Sopenharmony_ci
40562306a36Sopenharmony_ci	if (!tb[IFAL_ADDRESS])
40662306a36Sopenharmony_ci		return -EINVAL;
40762306a36Sopenharmony_ci	pfx = nla_data(tb[IFAL_ADDRESS]);
40862306a36Sopenharmony_ci
40962306a36Sopenharmony_ci	if (!tb[IFAL_LABEL])
41062306a36Sopenharmony_ci		return -EINVAL;
41162306a36Sopenharmony_ci	label = nla_get_u32(tb[IFAL_LABEL]);
41262306a36Sopenharmony_ci	if (label == IPV6_ADDR_LABEL_DEFAULT)
41362306a36Sopenharmony_ci		return -EINVAL;
41462306a36Sopenharmony_ci
41562306a36Sopenharmony_ci	switch (nlh->nlmsg_type) {
41662306a36Sopenharmony_ci	case RTM_NEWADDRLABEL:
41762306a36Sopenharmony_ci		if (ifal->ifal_index &&
41862306a36Sopenharmony_ci		    !addrlbl_ifindex_exists(net, ifal->ifal_index))
41962306a36Sopenharmony_ci			return -EINVAL;
42062306a36Sopenharmony_ci
42162306a36Sopenharmony_ci		err = ip6addrlbl_add(net, pfx, ifal->ifal_prefixlen,
42262306a36Sopenharmony_ci				     ifal->ifal_index, label,
42362306a36Sopenharmony_ci				     nlh->nlmsg_flags & NLM_F_REPLACE);
42462306a36Sopenharmony_ci		break;
42562306a36Sopenharmony_ci	case RTM_DELADDRLABEL:
42662306a36Sopenharmony_ci		err = ip6addrlbl_del(net, pfx, ifal->ifal_prefixlen,
42762306a36Sopenharmony_ci				     ifal->ifal_index);
42862306a36Sopenharmony_ci		break;
42962306a36Sopenharmony_ci	default:
43062306a36Sopenharmony_ci		err = -EOPNOTSUPP;
43162306a36Sopenharmony_ci	}
43262306a36Sopenharmony_ci	return err;
43362306a36Sopenharmony_ci}
43462306a36Sopenharmony_ci
43562306a36Sopenharmony_cistatic void ip6addrlbl_putmsg(struct nlmsghdr *nlh,
43662306a36Sopenharmony_ci			      int prefixlen, int ifindex, u32 lseq)
43762306a36Sopenharmony_ci{
43862306a36Sopenharmony_ci	struct ifaddrlblmsg *ifal = nlmsg_data(nlh);
43962306a36Sopenharmony_ci	ifal->ifal_family = AF_INET6;
44062306a36Sopenharmony_ci	ifal->__ifal_reserved = 0;
44162306a36Sopenharmony_ci	ifal->ifal_prefixlen = prefixlen;
44262306a36Sopenharmony_ci	ifal->ifal_flags = 0;
44362306a36Sopenharmony_ci	ifal->ifal_index = ifindex;
44462306a36Sopenharmony_ci	ifal->ifal_seq = lseq;
44562306a36Sopenharmony_ci};
44662306a36Sopenharmony_ci
44762306a36Sopenharmony_cistatic int ip6addrlbl_fill(struct sk_buff *skb,
44862306a36Sopenharmony_ci			   struct ip6addrlbl_entry *p,
44962306a36Sopenharmony_ci			   u32 lseq,
45062306a36Sopenharmony_ci			   u32 portid, u32 seq, int event,
45162306a36Sopenharmony_ci			   unsigned int flags)
45262306a36Sopenharmony_ci{
45362306a36Sopenharmony_ci	struct nlmsghdr *nlh = nlmsg_put(skb, portid, seq, event,
45462306a36Sopenharmony_ci					 sizeof(struct ifaddrlblmsg), flags);
45562306a36Sopenharmony_ci	if (!nlh)
45662306a36Sopenharmony_ci		return -EMSGSIZE;
45762306a36Sopenharmony_ci
45862306a36Sopenharmony_ci	ip6addrlbl_putmsg(nlh, p->prefixlen, p->ifindex, lseq);
45962306a36Sopenharmony_ci
46062306a36Sopenharmony_ci	if (nla_put_in6_addr(skb, IFAL_ADDRESS, &p->prefix) < 0 ||
46162306a36Sopenharmony_ci	    nla_put_u32(skb, IFAL_LABEL, p->label) < 0) {
46262306a36Sopenharmony_ci		nlmsg_cancel(skb, nlh);
46362306a36Sopenharmony_ci		return -EMSGSIZE;
46462306a36Sopenharmony_ci	}
46562306a36Sopenharmony_ci
46662306a36Sopenharmony_ci	nlmsg_end(skb, nlh);
46762306a36Sopenharmony_ci	return 0;
46862306a36Sopenharmony_ci}
46962306a36Sopenharmony_ci
47062306a36Sopenharmony_cistatic int ip6addrlbl_valid_dump_req(const struct nlmsghdr *nlh,
47162306a36Sopenharmony_ci				     struct netlink_ext_ack *extack)
47262306a36Sopenharmony_ci{
47362306a36Sopenharmony_ci	struct ifaddrlblmsg *ifal;
47462306a36Sopenharmony_ci
47562306a36Sopenharmony_ci	if (nlh->nlmsg_len < nlmsg_msg_size(sizeof(*ifal))) {
47662306a36Sopenharmony_ci		NL_SET_ERR_MSG_MOD(extack, "Invalid header for address label dump request");
47762306a36Sopenharmony_ci		return -EINVAL;
47862306a36Sopenharmony_ci	}
47962306a36Sopenharmony_ci
48062306a36Sopenharmony_ci	ifal = nlmsg_data(nlh);
48162306a36Sopenharmony_ci	if (ifal->__ifal_reserved || ifal->ifal_prefixlen ||
48262306a36Sopenharmony_ci	    ifal->ifal_flags || ifal->ifal_index || ifal->ifal_seq) {
48362306a36Sopenharmony_ci		NL_SET_ERR_MSG_MOD(extack, "Invalid values in header for address label dump request");
48462306a36Sopenharmony_ci		return -EINVAL;
48562306a36Sopenharmony_ci	}
48662306a36Sopenharmony_ci
48762306a36Sopenharmony_ci	if (nlmsg_attrlen(nlh, sizeof(*ifal))) {
48862306a36Sopenharmony_ci		NL_SET_ERR_MSG_MOD(extack, "Invalid data after header for address label dump request");
48962306a36Sopenharmony_ci		return -EINVAL;
49062306a36Sopenharmony_ci	}
49162306a36Sopenharmony_ci
49262306a36Sopenharmony_ci	return 0;
49362306a36Sopenharmony_ci}
49462306a36Sopenharmony_ci
49562306a36Sopenharmony_cistatic int ip6addrlbl_dump(struct sk_buff *skb, struct netlink_callback *cb)
49662306a36Sopenharmony_ci{
49762306a36Sopenharmony_ci	const struct nlmsghdr *nlh = cb->nlh;
49862306a36Sopenharmony_ci	struct net *net = sock_net(skb->sk);
49962306a36Sopenharmony_ci	struct ip6addrlbl_entry *p;
50062306a36Sopenharmony_ci	int idx = 0, s_idx = cb->args[0];
50162306a36Sopenharmony_ci	int err;
50262306a36Sopenharmony_ci
50362306a36Sopenharmony_ci	if (cb->strict_check) {
50462306a36Sopenharmony_ci		err = ip6addrlbl_valid_dump_req(nlh, cb->extack);
50562306a36Sopenharmony_ci		if (err < 0)
50662306a36Sopenharmony_ci			return err;
50762306a36Sopenharmony_ci	}
50862306a36Sopenharmony_ci
50962306a36Sopenharmony_ci	rcu_read_lock();
51062306a36Sopenharmony_ci	hlist_for_each_entry_rcu(p, &net->ipv6.ip6addrlbl_table.head, list) {
51162306a36Sopenharmony_ci		if (idx >= s_idx) {
51262306a36Sopenharmony_ci			err = ip6addrlbl_fill(skb, p,
51362306a36Sopenharmony_ci					      net->ipv6.ip6addrlbl_table.seq,
51462306a36Sopenharmony_ci					      NETLINK_CB(cb->skb).portid,
51562306a36Sopenharmony_ci					      nlh->nlmsg_seq,
51662306a36Sopenharmony_ci					      RTM_NEWADDRLABEL,
51762306a36Sopenharmony_ci					      NLM_F_MULTI);
51862306a36Sopenharmony_ci			if (err < 0)
51962306a36Sopenharmony_ci				break;
52062306a36Sopenharmony_ci		}
52162306a36Sopenharmony_ci		idx++;
52262306a36Sopenharmony_ci	}
52362306a36Sopenharmony_ci	rcu_read_unlock();
52462306a36Sopenharmony_ci	cb->args[0] = idx;
52562306a36Sopenharmony_ci	return skb->len;
52662306a36Sopenharmony_ci}
52762306a36Sopenharmony_ci
52862306a36Sopenharmony_cistatic inline int ip6addrlbl_msgsize(void)
52962306a36Sopenharmony_ci{
53062306a36Sopenharmony_ci	return NLMSG_ALIGN(sizeof(struct ifaddrlblmsg))
53162306a36Sopenharmony_ci		+ nla_total_size(16)	/* IFAL_ADDRESS */
53262306a36Sopenharmony_ci		+ nla_total_size(4);	/* IFAL_LABEL */
53362306a36Sopenharmony_ci}
53462306a36Sopenharmony_ci
53562306a36Sopenharmony_cistatic int ip6addrlbl_valid_get_req(struct sk_buff *skb,
53662306a36Sopenharmony_ci				    const struct nlmsghdr *nlh,
53762306a36Sopenharmony_ci				    struct nlattr **tb,
53862306a36Sopenharmony_ci				    struct netlink_ext_ack *extack)
53962306a36Sopenharmony_ci{
54062306a36Sopenharmony_ci	struct ifaddrlblmsg *ifal;
54162306a36Sopenharmony_ci	int i, err;
54262306a36Sopenharmony_ci
54362306a36Sopenharmony_ci	if (nlh->nlmsg_len < nlmsg_msg_size(sizeof(*ifal))) {
54462306a36Sopenharmony_ci		NL_SET_ERR_MSG_MOD(extack, "Invalid header for addrlabel get request");
54562306a36Sopenharmony_ci		return -EINVAL;
54662306a36Sopenharmony_ci	}
54762306a36Sopenharmony_ci
54862306a36Sopenharmony_ci	if (!netlink_strict_get_check(skb))
54962306a36Sopenharmony_ci		return nlmsg_parse_deprecated(nlh, sizeof(*ifal), tb,
55062306a36Sopenharmony_ci					      IFAL_MAX, ifal_policy, extack);
55162306a36Sopenharmony_ci
55262306a36Sopenharmony_ci	ifal = nlmsg_data(nlh);
55362306a36Sopenharmony_ci	if (ifal->__ifal_reserved || ifal->ifal_flags || ifal->ifal_seq) {
55462306a36Sopenharmony_ci		NL_SET_ERR_MSG_MOD(extack, "Invalid values in header for addrlabel get request");
55562306a36Sopenharmony_ci		return -EINVAL;
55662306a36Sopenharmony_ci	}
55762306a36Sopenharmony_ci
55862306a36Sopenharmony_ci	err = nlmsg_parse_deprecated_strict(nlh, sizeof(*ifal), tb, IFAL_MAX,
55962306a36Sopenharmony_ci					    ifal_policy, extack);
56062306a36Sopenharmony_ci	if (err)
56162306a36Sopenharmony_ci		return err;
56262306a36Sopenharmony_ci
56362306a36Sopenharmony_ci	for (i = 0; i <= IFAL_MAX; i++) {
56462306a36Sopenharmony_ci		if (!tb[i])
56562306a36Sopenharmony_ci			continue;
56662306a36Sopenharmony_ci
56762306a36Sopenharmony_ci		switch (i) {
56862306a36Sopenharmony_ci		case IFAL_ADDRESS:
56962306a36Sopenharmony_ci			break;
57062306a36Sopenharmony_ci		default:
57162306a36Sopenharmony_ci			NL_SET_ERR_MSG_MOD(extack, "Unsupported attribute in addrlabel get request");
57262306a36Sopenharmony_ci			return -EINVAL;
57362306a36Sopenharmony_ci		}
57462306a36Sopenharmony_ci	}
57562306a36Sopenharmony_ci
57662306a36Sopenharmony_ci	return 0;
57762306a36Sopenharmony_ci}
57862306a36Sopenharmony_ci
57962306a36Sopenharmony_cistatic int ip6addrlbl_get(struct sk_buff *in_skb, struct nlmsghdr *nlh,
58062306a36Sopenharmony_ci			  struct netlink_ext_ack *extack)
58162306a36Sopenharmony_ci{
58262306a36Sopenharmony_ci	struct net *net = sock_net(in_skb->sk);
58362306a36Sopenharmony_ci	struct ifaddrlblmsg *ifal;
58462306a36Sopenharmony_ci	struct nlattr *tb[IFAL_MAX+1];
58562306a36Sopenharmony_ci	struct in6_addr *addr;
58662306a36Sopenharmony_ci	u32 lseq;
58762306a36Sopenharmony_ci	int err = 0;
58862306a36Sopenharmony_ci	struct ip6addrlbl_entry *p;
58962306a36Sopenharmony_ci	struct sk_buff *skb;
59062306a36Sopenharmony_ci
59162306a36Sopenharmony_ci	err = ip6addrlbl_valid_get_req(in_skb, nlh, tb, extack);
59262306a36Sopenharmony_ci	if (err < 0)
59362306a36Sopenharmony_ci		return err;
59462306a36Sopenharmony_ci
59562306a36Sopenharmony_ci	ifal = nlmsg_data(nlh);
59662306a36Sopenharmony_ci
59762306a36Sopenharmony_ci	if (ifal->ifal_family != AF_INET6 ||
59862306a36Sopenharmony_ci	    ifal->ifal_prefixlen != 128)
59962306a36Sopenharmony_ci		return -EINVAL;
60062306a36Sopenharmony_ci
60162306a36Sopenharmony_ci	if (ifal->ifal_index &&
60262306a36Sopenharmony_ci	    !addrlbl_ifindex_exists(net, ifal->ifal_index))
60362306a36Sopenharmony_ci		return -EINVAL;
60462306a36Sopenharmony_ci
60562306a36Sopenharmony_ci	if (!tb[IFAL_ADDRESS])
60662306a36Sopenharmony_ci		return -EINVAL;
60762306a36Sopenharmony_ci	addr = nla_data(tb[IFAL_ADDRESS]);
60862306a36Sopenharmony_ci
60962306a36Sopenharmony_ci	skb = nlmsg_new(ip6addrlbl_msgsize(), GFP_KERNEL);
61062306a36Sopenharmony_ci	if (!skb)
61162306a36Sopenharmony_ci		return -ENOBUFS;
61262306a36Sopenharmony_ci
61362306a36Sopenharmony_ci	err = -ESRCH;
61462306a36Sopenharmony_ci
61562306a36Sopenharmony_ci	rcu_read_lock();
61662306a36Sopenharmony_ci	p = __ipv6_addr_label(net, addr, ipv6_addr_type(addr), ifal->ifal_index);
61762306a36Sopenharmony_ci	lseq = net->ipv6.ip6addrlbl_table.seq;
61862306a36Sopenharmony_ci	if (p)
61962306a36Sopenharmony_ci		err = ip6addrlbl_fill(skb, p, lseq,
62062306a36Sopenharmony_ci				      NETLINK_CB(in_skb).portid,
62162306a36Sopenharmony_ci				      nlh->nlmsg_seq,
62262306a36Sopenharmony_ci				      RTM_NEWADDRLABEL, 0);
62362306a36Sopenharmony_ci	rcu_read_unlock();
62462306a36Sopenharmony_ci
62562306a36Sopenharmony_ci	if (err < 0) {
62662306a36Sopenharmony_ci		WARN_ON(err == -EMSGSIZE);
62762306a36Sopenharmony_ci		kfree_skb(skb);
62862306a36Sopenharmony_ci	} else {
62962306a36Sopenharmony_ci		err = rtnl_unicast(skb, net, NETLINK_CB(in_skb).portid);
63062306a36Sopenharmony_ci	}
63162306a36Sopenharmony_ci	return err;
63262306a36Sopenharmony_ci}
63362306a36Sopenharmony_ci
63462306a36Sopenharmony_ciint __init ipv6_addr_label_rtnl_register(void)
63562306a36Sopenharmony_ci{
63662306a36Sopenharmony_ci	int ret;
63762306a36Sopenharmony_ci
63862306a36Sopenharmony_ci	ret = rtnl_register_module(THIS_MODULE, PF_INET6, RTM_NEWADDRLABEL,
63962306a36Sopenharmony_ci				   ip6addrlbl_newdel,
64062306a36Sopenharmony_ci				   NULL, RTNL_FLAG_DOIT_UNLOCKED);
64162306a36Sopenharmony_ci	if (ret < 0)
64262306a36Sopenharmony_ci		return ret;
64362306a36Sopenharmony_ci	ret = rtnl_register_module(THIS_MODULE, PF_INET6, RTM_DELADDRLABEL,
64462306a36Sopenharmony_ci				   ip6addrlbl_newdel,
64562306a36Sopenharmony_ci				   NULL, RTNL_FLAG_DOIT_UNLOCKED);
64662306a36Sopenharmony_ci	if (ret < 0)
64762306a36Sopenharmony_ci		return ret;
64862306a36Sopenharmony_ci	ret = rtnl_register_module(THIS_MODULE, PF_INET6, RTM_GETADDRLABEL,
64962306a36Sopenharmony_ci				   ip6addrlbl_get,
65062306a36Sopenharmony_ci				   ip6addrlbl_dump, RTNL_FLAG_DOIT_UNLOCKED);
65162306a36Sopenharmony_ci	return ret;
65262306a36Sopenharmony_ci}
653