xref: /OK3568_Linux_fs/kernel/net/ipv6/addrlabel.c (revision 4882a59341e53eb6f0b4789bf948001014eff981)
1*4882a593Smuzhiyun // SPDX-License-Identifier: GPL-2.0
2*4882a593Smuzhiyun /*
3*4882a593Smuzhiyun  * IPv6 Address Label subsystem
4*4882a593Smuzhiyun  * for the IPv6 "Default" Source Address Selection
5*4882a593Smuzhiyun  *
6*4882a593Smuzhiyun  * Copyright (C)2007 USAGI/WIDE Project
7*4882a593Smuzhiyun  */
8*4882a593Smuzhiyun /*
9*4882a593Smuzhiyun  * Author:
10*4882a593Smuzhiyun  *	YOSHIFUJI Hideaki @ USAGI/WIDE Project <yoshfuji@linux-ipv6.org>
11*4882a593Smuzhiyun  */
12*4882a593Smuzhiyun 
13*4882a593Smuzhiyun #include <linux/kernel.h>
14*4882a593Smuzhiyun #include <linux/list.h>
15*4882a593Smuzhiyun #include <linux/rcupdate.h>
16*4882a593Smuzhiyun #include <linux/in6.h>
17*4882a593Smuzhiyun #include <linux/slab.h>
18*4882a593Smuzhiyun #include <net/addrconf.h>
19*4882a593Smuzhiyun #include <linux/if_addrlabel.h>
20*4882a593Smuzhiyun #include <linux/netlink.h>
21*4882a593Smuzhiyun #include <linux/rtnetlink.h>
22*4882a593Smuzhiyun 
23*4882a593Smuzhiyun #if 0
24*4882a593Smuzhiyun #define ADDRLABEL(x...) printk(x)
25*4882a593Smuzhiyun #else
26*4882a593Smuzhiyun #define ADDRLABEL(x...) do { ; } while (0)
27*4882a593Smuzhiyun #endif
28*4882a593Smuzhiyun 
29*4882a593Smuzhiyun /*
30*4882a593Smuzhiyun  * Policy Table
31*4882a593Smuzhiyun  */
32*4882a593Smuzhiyun struct ip6addrlbl_entry {
33*4882a593Smuzhiyun 	struct in6_addr prefix;
34*4882a593Smuzhiyun 	int prefixlen;
35*4882a593Smuzhiyun 	int ifindex;
36*4882a593Smuzhiyun 	int addrtype;
37*4882a593Smuzhiyun 	u32 label;
38*4882a593Smuzhiyun 	struct hlist_node list;
39*4882a593Smuzhiyun 	struct rcu_head rcu;
40*4882a593Smuzhiyun };
41*4882a593Smuzhiyun 
42*4882a593Smuzhiyun /*
43*4882a593Smuzhiyun  * Default policy table (RFC6724 + extensions)
44*4882a593Smuzhiyun  *
45*4882a593Smuzhiyun  * prefix		addr_type	label
46*4882a593Smuzhiyun  * -------------------------------------------------------------------------
47*4882a593Smuzhiyun  * ::1/128		LOOPBACK	0
48*4882a593Smuzhiyun  * ::/0			N/A		1
49*4882a593Smuzhiyun  * 2002::/16		N/A		2
50*4882a593Smuzhiyun  * ::/96		COMPATv4	3
51*4882a593Smuzhiyun  * ::ffff:0:0/96	V4MAPPED	4
52*4882a593Smuzhiyun  * fc00::/7		N/A		5		ULA (RFC 4193)
53*4882a593Smuzhiyun  * 2001::/32		N/A		6		Teredo (RFC 4380)
54*4882a593Smuzhiyun  * 2001:10::/28		N/A		7		ORCHID (RFC 4843)
55*4882a593Smuzhiyun  * fec0::/10		N/A		11		Site-local
56*4882a593Smuzhiyun  *							(deprecated by RFC3879)
57*4882a593Smuzhiyun  * 3ffe::/16		N/A		12		6bone
58*4882a593Smuzhiyun  *
59*4882a593Smuzhiyun  * Note: 0xffffffff is used if we do not have any policies.
60*4882a593Smuzhiyun  * Note: Labels for ULA and 6to4 are different from labels listed in RFC6724.
61*4882a593Smuzhiyun  */
62*4882a593Smuzhiyun 
63*4882a593Smuzhiyun #define IPV6_ADDR_LABEL_DEFAULT	0xffffffffUL
64*4882a593Smuzhiyun 
65*4882a593Smuzhiyun static const __net_initconst struct ip6addrlbl_init_table
66*4882a593Smuzhiyun {
67*4882a593Smuzhiyun 	const struct in6_addr *prefix;
68*4882a593Smuzhiyun 	int prefixlen;
69*4882a593Smuzhiyun 	u32 label;
70*4882a593Smuzhiyun } ip6addrlbl_init_table[] = {
71*4882a593Smuzhiyun 	{	/* ::/0 */
72*4882a593Smuzhiyun 		.prefix = &in6addr_any,
73*4882a593Smuzhiyun 		.label = 1,
74*4882a593Smuzhiyun 	}, {	/* fc00::/7 */
75*4882a593Smuzhiyun 		.prefix = &(struct in6_addr){ { { 0xfc } } } ,
76*4882a593Smuzhiyun 		.prefixlen = 7,
77*4882a593Smuzhiyun 		.label = 5,
78*4882a593Smuzhiyun 	}, {	/* fec0::/10 */
79*4882a593Smuzhiyun 		.prefix = &(struct in6_addr){ { { 0xfe, 0xc0 } } },
80*4882a593Smuzhiyun 		.prefixlen = 10,
81*4882a593Smuzhiyun 		.label = 11,
82*4882a593Smuzhiyun 	}, {	/* 2002::/16 */
83*4882a593Smuzhiyun 		.prefix = &(struct in6_addr){ { { 0x20, 0x02 } } },
84*4882a593Smuzhiyun 		.prefixlen = 16,
85*4882a593Smuzhiyun 		.label = 2,
86*4882a593Smuzhiyun 	}, {	/* 3ffe::/16 */
87*4882a593Smuzhiyun 		.prefix = &(struct in6_addr){ { { 0x3f, 0xfe } } },
88*4882a593Smuzhiyun 		.prefixlen = 16,
89*4882a593Smuzhiyun 		.label = 12,
90*4882a593Smuzhiyun 	}, {	/* 2001::/32 */
91*4882a593Smuzhiyun 		.prefix = &(struct in6_addr){ { { 0x20, 0x01 } } },
92*4882a593Smuzhiyun 		.prefixlen = 32,
93*4882a593Smuzhiyun 		.label = 6,
94*4882a593Smuzhiyun 	}, {	/* 2001:10::/28 */
95*4882a593Smuzhiyun 		.prefix = &(struct in6_addr){ { { 0x20, 0x01, 0x00, 0x10 } } },
96*4882a593Smuzhiyun 		.prefixlen = 28,
97*4882a593Smuzhiyun 		.label = 7,
98*4882a593Smuzhiyun 	}, {	/* ::ffff:0:0 */
99*4882a593Smuzhiyun 		.prefix = &(struct in6_addr){ { { [10] = 0xff, [11] = 0xff } } },
100*4882a593Smuzhiyun 		.prefixlen = 96,
101*4882a593Smuzhiyun 		.label = 4,
102*4882a593Smuzhiyun 	}, {	/* ::/96 */
103*4882a593Smuzhiyun 		.prefix = &in6addr_any,
104*4882a593Smuzhiyun 		.prefixlen = 96,
105*4882a593Smuzhiyun 		.label = 3,
106*4882a593Smuzhiyun 	}, {	/* ::1/128 */
107*4882a593Smuzhiyun 		.prefix = &in6addr_loopback,
108*4882a593Smuzhiyun 		.prefixlen = 128,
109*4882a593Smuzhiyun 		.label = 0,
110*4882a593Smuzhiyun 	}
111*4882a593Smuzhiyun };
112*4882a593Smuzhiyun 
113*4882a593Smuzhiyun /* Find label */
__ip6addrlbl_match(const struct ip6addrlbl_entry * p,const struct in6_addr * addr,int addrtype,int ifindex)114*4882a593Smuzhiyun static bool __ip6addrlbl_match(const struct ip6addrlbl_entry *p,
115*4882a593Smuzhiyun 			       const struct in6_addr *addr,
116*4882a593Smuzhiyun 			       int addrtype, int ifindex)
117*4882a593Smuzhiyun {
118*4882a593Smuzhiyun 	if (p->ifindex && p->ifindex != ifindex)
119*4882a593Smuzhiyun 		return false;
120*4882a593Smuzhiyun 	if (p->addrtype && p->addrtype != addrtype)
121*4882a593Smuzhiyun 		return false;
122*4882a593Smuzhiyun 	if (!ipv6_prefix_equal(addr, &p->prefix, p->prefixlen))
123*4882a593Smuzhiyun 		return false;
124*4882a593Smuzhiyun 	return true;
125*4882a593Smuzhiyun }
126*4882a593Smuzhiyun 
__ipv6_addr_label(struct net * net,const struct in6_addr * addr,int type,int ifindex)127*4882a593Smuzhiyun static struct ip6addrlbl_entry *__ipv6_addr_label(struct net *net,
128*4882a593Smuzhiyun 						  const struct in6_addr *addr,
129*4882a593Smuzhiyun 						  int type, int ifindex)
130*4882a593Smuzhiyun {
131*4882a593Smuzhiyun 	struct ip6addrlbl_entry *p;
132*4882a593Smuzhiyun 
133*4882a593Smuzhiyun 	hlist_for_each_entry_rcu(p, &net->ipv6.ip6addrlbl_table.head, list) {
134*4882a593Smuzhiyun 		if (__ip6addrlbl_match(p, addr, type, ifindex))
135*4882a593Smuzhiyun 			return p;
136*4882a593Smuzhiyun 	}
137*4882a593Smuzhiyun 	return NULL;
138*4882a593Smuzhiyun }
139*4882a593Smuzhiyun 
ipv6_addr_label(struct net * net,const struct in6_addr * addr,int type,int ifindex)140*4882a593Smuzhiyun u32 ipv6_addr_label(struct net *net,
141*4882a593Smuzhiyun 		    const struct in6_addr *addr, int type, int ifindex)
142*4882a593Smuzhiyun {
143*4882a593Smuzhiyun 	u32 label;
144*4882a593Smuzhiyun 	struct ip6addrlbl_entry *p;
145*4882a593Smuzhiyun 
146*4882a593Smuzhiyun 	type &= IPV6_ADDR_MAPPED | IPV6_ADDR_COMPATv4 | IPV6_ADDR_LOOPBACK;
147*4882a593Smuzhiyun 
148*4882a593Smuzhiyun 	rcu_read_lock();
149*4882a593Smuzhiyun 	p = __ipv6_addr_label(net, addr, type, ifindex);
150*4882a593Smuzhiyun 	label = p ? p->label : IPV6_ADDR_LABEL_DEFAULT;
151*4882a593Smuzhiyun 	rcu_read_unlock();
152*4882a593Smuzhiyun 
153*4882a593Smuzhiyun 	ADDRLABEL(KERN_DEBUG "%s(addr=%pI6, type=%d, ifindex=%d) => %08x\n",
154*4882a593Smuzhiyun 		  __func__, addr, type, ifindex, label);
155*4882a593Smuzhiyun 
156*4882a593Smuzhiyun 	return label;
157*4882a593Smuzhiyun }
158*4882a593Smuzhiyun 
159*4882a593Smuzhiyun /* allocate one entry */
ip6addrlbl_alloc(const struct in6_addr * prefix,int prefixlen,int ifindex,u32 label)160*4882a593Smuzhiyun static struct ip6addrlbl_entry *ip6addrlbl_alloc(const struct in6_addr *prefix,
161*4882a593Smuzhiyun 						 int prefixlen, int ifindex,
162*4882a593Smuzhiyun 						 u32 label)
163*4882a593Smuzhiyun {
164*4882a593Smuzhiyun 	struct ip6addrlbl_entry *newp;
165*4882a593Smuzhiyun 	int addrtype;
166*4882a593Smuzhiyun 
167*4882a593Smuzhiyun 	ADDRLABEL(KERN_DEBUG "%s(prefix=%pI6, prefixlen=%d, ifindex=%d, label=%u)\n",
168*4882a593Smuzhiyun 		  __func__, prefix, prefixlen, ifindex, (unsigned int)label);
169*4882a593Smuzhiyun 
170*4882a593Smuzhiyun 	addrtype = ipv6_addr_type(prefix) & (IPV6_ADDR_MAPPED | IPV6_ADDR_COMPATv4 | IPV6_ADDR_LOOPBACK);
171*4882a593Smuzhiyun 
172*4882a593Smuzhiyun 	switch (addrtype) {
173*4882a593Smuzhiyun 	case IPV6_ADDR_MAPPED:
174*4882a593Smuzhiyun 		if (prefixlen > 96)
175*4882a593Smuzhiyun 			return ERR_PTR(-EINVAL);
176*4882a593Smuzhiyun 		if (prefixlen < 96)
177*4882a593Smuzhiyun 			addrtype = 0;
178*4882a593Smuzhiyun 		break;
179*4882a593Smuzhiyun 	case IPV6_ADDR_COMPATv4:
180*4882a593Smuzhiyun 		if (prefixlen != 96)
181*4882a593Smuzhiyun 			addrtype = 0;
182*4882a593Smuzhiyun 		break;
183*4882a593Smuzhiyun 	case IPV6_ADDR_LOOPBACK:
184*4882a593Smuzhiyun 		if (prefixlen != 128)
185*4882a593Smuzhiyun 			addrtype = 0;
186*4882a593Smuzhiyun 		break;
187*4882a593Smuzhiyun 	}
188*4882a593Smuzhiyun 
189*4882a593Smuzhiyun 	newp = kmalloc(sizeof(*newp), GFP_KERNEL);
190*4882a593Smuzhiyun 	if (!newp)
191*4882a593Smuzhiyun 		return ERR_PTR(-ENOMEM);
192*4882a593Smuzhiyun 
193*4882a593Smuzhiyun 	ipv6_addr_prefix(&newp->prefix, prefix, prefixlen);
194*4882a593Smuzhiyun 	newp->prefixlen = prefixlen;
195*4882a593Smuzhiyun 	newp->ifindex = ifindex;
196*4882a593Smuzhiyun 	newp->addrtype = addrtype;
197*4882a593Smuzhiyun 	newp->label = label;
198*4882a593Smuzhiyun 	INIT_HLIST_NODE(&newp->list);
199*4882a593Smuzhiyun 	return newp;
200*4882a593Smuzhiyun }
201*4882a593Smuzhiyun 
202*4882a593Smuzhiyun /* add a label */
__ip6addrlbl_add(struct net * net,struct ip6addrlbl_entry * newp,int replace)203*4882a593Smuzhiyun static int __ip6addrlbl_add(struct net *net, struct ip6addrlbl_entry *newp,
204*4882a593Smuzhiyun 			    int replace)
205*4882a593Smuzhiyun {
206*4882a593Smuzhiyun 	struct ip6addrlbl_entry *last = NULL, *p = NULL;
207*4882a593Smuzhiyun 	struct hlist_node *n;
208*4882a593Smuzhiyun 	int ret = 0;
209*4882a593Smuzhiyun 
210*4882a593Smuzhiyun 	ADDRLABEL(KERN_DEBUG "%s(newp=%p, replace=%d)\n", __func__, newp,
211*4882a593Smuzhiyun 		  replace);
212*4882a593Smuzhiyun 
213*4882a593Smuzhiyun 	hlist_for_each_entry_safe(p, n,	&net->ipv6.ip6addrlbl_table.head, list) {
214*4882a593Smuzhiyun 		if (p->prefixlen == newp->prefixlen &&
215*4882a593Smuzhiyun 		    p->ifindex == newp->ifindex &&
216*4882a593Smuzhiyun 		    ipv6_addr_equal(&p->prefix, &newp->prefix)) {
217*4882a593Smuzhiyun 			if (!replace) {
218*4882a593Smuzhiyun 				ret = -EEXIST;
219*4882a593Smuzhiyun 				goto out;
220*4882a593Smuzhiyun 			}
221*4882a593Smuzhiyun 			hlist_replace_rcu(&p->list, &newp->list);
222*4882a593Smuzhiyun 			kfree_rcu(p, rcu);
223*4882a593Smuzhiyun 			goto out;
224*4882a593Smuzhiyun 		} else if ((p->prefixlen == newp->prefixlen && !p->ifindex) ||
225*4882a593Smuzhiyun 			   (p->prefixlen < newp->prefixlen)) {
226*4882a593Smuzhiyun 			hlist_add_before_rcu(&newp->list, &p->list);
227*4882a593Smuzhiyun 			goto out;
228*4882a593Smuzhiyun 		}
229*4882a593Smuzhiyun 		last = p;
230*4882a593Smuzhiyun 	}
231*4882a593Smuzhiyun 	if (last)
232*4882a593Smuzhiyun 		hlist_add_behind_rcu(&newp->list, &last->list);
233*4882a593Smuzhiyun 	else
234*4882a593Smuzhiyun 		hlist_add_head_rcu(&newp->list, &net->ipv6.ip6addrlbl_table.head);
235*4882a593Smuzhiyun out:
236*4882a593Smuzhiyun 	if (!ret)
237*4882a593Smuzhiyun 		net->ipv6.ip6addrlbl_table.seq++;
238*4882a593Smuzhiyun 	return ret;
239*4882a593Smuzhiyun }
240*4882a593Smuzhiyun 
241*4882a593Smuzhiyun /* add a label */
ip6addrlbl_add(struct net * net,const struct in6_addr * prefix,int prefixlen,int ifindex,u32 label,int replace)242*4882a593Smuzhiyun static int ip6addrlbl_add(struct net *net,
243*4882a593Smuzhiyun 			  const struct in6_addr *prefix, int prefixlen,
244*4882a593Smuzhiyun 			  int ifindex, u32 label, int replace)
245*4882a593Smuzhiyun {
246*4882a593Smuzhiyun 	struct ip6addrlbl_entry *newp;
247*4882a593Smuzhiyun 	int ret = 0;
248*4882a593Smuzhiyun 
249*4882a593Smuzhiyun 	ADDRLABEL(KERN_DEBUG "%s(prefix=%pI6, prefixlen=%d, ifindex=%d, label=%u, replace=%d)\n",
250*4882a593Smuzhiyun 		  __func__, prefix, prefixlen, ifindex, (unsigned int)label,
251*4882a593Smuzhiyun 		  replace);
252*4882a593Smuzhiyun 
253*4882a593Smuzhiyun 	newp = ip6addrlbl_alloc(prefix, prefixlen, ifindex, label);
254*4882a593Smuzhiyun 	if (IS_ERR(newp))
255*4882a593Smuzhiyun 		return PTR_ERR(newp);
256*4882a593Smuzhiyun 	spin_lock(&net->ipv6.ip6addrlbl_table.lock);
257*4882a593Smuzhiyun 	ret = __ip6addrlbl_add(net, newp, replace);
258*4882a593Smuzhiyun 	spin_unlock(&net->ipv6.ip6addrlbl_table.lock);
259*4882a593Smuzhiyun 	if (ret)
260*4882a593Smuzhiyun 		kfree(newp);
261*4882a593Smuzhiyun 	return ret;
262*4882a593Smuzhiyun }
263*4882a593Smuzhiyun 
264*4882a593Smuzhiyun /* remove a label */
__ip6addrlbl_del(struct net * net,const struct in6_addr * prefix,int prefixlen,int ifindex)265*4882a593Smuzhiyun static int __ip6addrlbl_del(struct net *net,
266*4882a593Smuzhiyun 			    const struct in6_addr *prefix, int prefixlen,
267*4882a593Smuzhiyun 			    int ifindex)
268*4882a593Smuzhiyun {
269*4882a593Smuzhiyun 	struct ip6addrlbl_entry *p = NULL;
270*4882a593Smuzhiyun 	struct hlist_node *n;
271*4882a593Smuzhiyun 	int ret = -ESRCH;
272*4882a593Smuzhiyun 
273*4882a593Smuzhiyun 	ADDRLABEL(KERN_DEBUG "%s(prefix=%pI6, prefixlen=%d, ifindex=%d)\n",
274*4882a593Smuzhiyun 		  __func__, prefix, prefixlen, ifindex);
275*4882a593Smuzhiyun 
276*4882a593Smuzhiyun 	hlist_for_each_entry_safe(p, n, &net->ipv6.ip6addrlbl_table.head, list) {
277*4882a593Smuzhiyun 		if (p->prefixlen == prefixlen &&
278*4882a593Smuzhiyun 		    p->ifindex == ifindex &&
279*4882a593Smuzhiyun 		    ipv6_addr_equal(&p->prefix, prefix)) {
280*4882a593Smuzhiyun 			hlist_del_rcu(&p->list);
281*4882a593Smuzhiyun 			kfree_rcu(p, rcu);
282*4882a593Smuzhiyun 			ret = 0;
283*4882a593Smuzhiyun 			break;
284*4882a593Smuzhiyun 		}
285*4882a593Smuzhiyun 	}
286*4882a593Smuzhiyun 	return ret;
287*4882a593Smuzhiyun }
288*4882a593Smuzhiyun 
ip6addrlbl_del(struct net * net,const struct in6_addr * prefix,int prefixlen,int ifindex)289*4882a593Smuzhiyun static int ip6addrlbl_del(struct net *net,
290*4882a593Smuzhiyun 			  const struct in6_addr *prefix, int prefixlen,
291*4882a593Smuzhiyun 			  int ifindex)
292*4882a593Smuzhiyun {
293*4882a593Smuzhiyun 	struct in6_addr prefix_buf;
294*4882a593Smuzhiyun 	int ret;
295*4882a593Smuzhiyun 
296*4882a593Smuzhiyun 	ADDRLABEL(KERN_DEBUG "%s(prefix=%pI6, prefixlen=%d, ifindex=%d)\n",
297*4882a593Smuzhiyun 		  __func__, prefix, prefixlen, ifindex);
298*4882a593Smuzhiyun 
299*4882a593Smuzhiyun 	ipv6_addr_prefix(&prefix_buf, prefix, prefixlen);
300*4882a593Smuzhiyun 	spin_lock(&net->ipv6.ip6addrlbl_table.lock);
301*4882a593Smuzhiyun 	ret = __ip6addrlbl_del(net, &prefix_buf, prefixlen, ifindex);
302*4882a593Smuzhiyun 	spin_unlock(&net->ipv6.ip6addrlbl_table.lock);
303*4882a593Smuzhiyun 	return ret;
304*4882a593Smuzhiyun }
305*4882a593Smuzhiyun 
306*4882a593Smuzhiyun /* add default label */
ip6addrlbl_net_init(struct net * net)307*4882a593Smuzhiyun static int __net_init ip6addrlbl_net_init(struct net *net)
308*4882a593Smuzhiyun {
309*4882a593Smuzhiyun 	struct ip6addrlbl_entry *p = NULL;
310*4882a593Smuzhiyun 	struct hlist_node *n;
311*4882a593Smuzhiyun 	int err;
312*4882a593Smuzhiyun 	int i;
313*4882a593Smuzhiyun 
314*4882a593Smuzhiyun 	ADDRLABEL(KERN_DEBUG "%s\n", __func__);
315*4882a593Smuzhiyun 
316*4882a593Smuzhiyun 	spin_lock_init(&net->ipv6.ip6addrlbl_table.lock);
317*4882a593Smuzhiyun 	INIT_HLIST_HEAD(&net->ipv6.ip6addrlbl_table.head);
318*4882a593Smuzhiyun 
319*4882a593Smuzhiyun 	for (i = 0; i < ARRAY_SIZE(ip6addrlbl_init_table); i++) {
320*4882a593Smuzhiyun 		err = ip6addrlbl_add(net,
321*4882a593Smuzhiyun 				     ip6addrlbl_init_table[i].prefix,
322*4882a593Smuzhiyun 				     ip6addrlbl_init_table[i].prefixlen,
323*4882a593Smuzhiyun 				     0,
324*4882a593Smuzhiyun 				     ip6addrlbl_init_table[i].label, 0);
325*4882a593Smuzhiyun 		if (err)
326*4882a593Smuzhiyun 			goto err_ip6addrlbl_add;
327*4882a593Smuzhiyun 	}
328*4882a593Smuzhiyun 	return 0;
329*4882a593Smuzhiyun 
330*4882a593Smuzhiyun err_ip6addrlbl_add:
331*4882a593Smuzhiyun 	hlist_for_each_entry_safe(p, n, &net->ipv6.ip6addrlbl_table.head, list) {
332*4882a593Smuzhiyun 		hlist_del_rcu(&p->list);
333*4882a593Smuzhiyun 		kfree_rcu(p, rcu);
334*4882a593Smuzhiyun 	}
335*4882a593Smuzhiyun 	return err;
336*4882a593Smuzhiyun }
337*4882a593Smuzhiyun 
ip6addrlbl_net_exit(struct net * net)338*4882a593Smuzhiyun static void __net_exit ip6addrlbl_net_exit(struct net *net)
339*4882a593Smuzhiyun {
340*4882a593Smuzhiyun 	struct ip6addrlbl_entry *p = NULL;
341*4882a593Smuzhiyun 	struct hlist_node *n;
342*4882a593Smuzhiyun 
343*4882a593Smuzhiyun 	/* Remove all labels belonging to the exiting net */
344*4882a593Smuzhiyun 	spin_lock(&net->ipv6.ip6addrlbl_table.lock);
345*4882a593Smuzhiyun 	hlist_for_each_entry_safe(p, n, &net->ipv6.ip6addrlbl_table.head, list) {
346*4882a593Smuzhiyun 		hlist_del_rcu(&p->list);
347*4882a593Smuzhiyun 		kfree_rcu(p, rcu);
348*4882a593Smuzhiyun 	}
349*4882a593Smuzhiyun 	spin_unlock(&net->ipv6.ip6addrlbl_table.lock);
350*4882a593Smuzhiyun }
351*4882a593Smuzhiyun 
352*4882a593Smuzhiyun static struct pernet_operations ipv6_addr_label_ops = {
353*4882a593Smuzhiyun 	.init = ip6addrlbl_net_init,
354*4882a593Smuzhiyun 	.exit = ip6addrlbl_net_exit,
355*4882a593Smuzhiyun };
356*4882a593Smuzhiyun 
ipv6_addr_label_init(void)357*4882a593Smuzhiyun int __init ipv6_addr_label_init(void)
358*4882a593Smuzhiyun {
359*4882a593Smuzhiyun 	return register_pernet_subsys(&ipv6_addr_label_ops);
360*4882a593Smuzhiyun }
361*4882a593Smuzhiyun 
ipv6_addr_label_cleanup(void)362*4882a593Smuzhiyun void ipv6_addr_label_cleanup(void)
363*4882a593Smuzhiyun {
364*4882a593Smuzhiyun 	unregister_pernet_subsys(&ipv6_addr_label_ops);
365*4882a593Smuzhiyun }
366*4882a593Smuzhiyun 
367*4882a593Smuzhiyun static const struct nla_policy ifal_policy[IFAL_MAX+1] = {
368*4882a593Smuzhiyun 	[IFAL_ADDRESS]		= { .len = sizeof(struct in6_addr), },
369*4882a593Smuzhiyun 	[IFAL_LABEL]		= { .len = sizeof(u32), },
370*4882a593Smuzhiyun };
371*4882a593Smuzhiyun 
addrlbl_ifindex_exists(struct net * net,int ifindex)372*4882a593Smuzhiyun static bool addrlbl_ifindex_exists(struct net *net, int ifindex)
373*4882a593Smuzhiyun {
374*4882a593Smuzhiyun 
375*4882a593Smuzhiyun 	struct net_device *dev;
376*4882a593Smuzhiyun 
377*4882a593Smuzhiyun 	rcu_read_lock();
378*4882a593Smuzhiyun 	dev = dev_get_by_index_rcu(net, ifindex);
379*4882a593Smuzhiyun 	rcu_read_unlock();
380*4882a593Smuzhiyun 
381*4882a593Smuzhiyun 	return dev != NULL;
382*4882a593Smuzhiyun }
383*4882a593Smuzhiyun 
ip6addrlbl_newdel(struct sk_buff * skb,struct nlmsghdr * nlh,struct netlink_ext_ack * extack)384*4882a593Smuzhiyun static int ip6addrlbl_newdel(struct sk_buff *skb, struct nlmsghdr *nlh,
385*4882a593Smuzhiyun 			     struct netlink_ext_ack *extack)
386*4882a593Smuzhiyun {
387*4882a593Smuzhiyun 	struct net *net = sock_net(skb->sk);
388*4882a593Smuzhiyun 	struct ifaddrlblmsg *ifal;
389*4882a593Smuzhiyun 	struct nlattr *tb[IFAL_MAX+1];
390*4882a593Smuzhiyun 	struct in6_addr *pfx;
391*4882a593Smuzhiyun 	u32 label;
392*4882a593Smuzhiyun 	int err = 0;
393*4882a593Smuzhiyun 
394*4882a593Smuzhiyun 	err = nlmsg_parse_deprecated(nlh, sizeof(*ifal), tb, IFAL_MAX,
395*4882a593Smuzhiyun 				     ifal_policy, extack);
396*4882a593Smuzhiyun 	if (err < 0)
397*4882a593Smuzhiyun 		return err;
398*4882a593Smuzhiyun 
399*4882a593Smuzhiyun 	ifal = nlmsg_data(nlh);
400*4882a593Smuzhiyun 
401*4882a593Smuzhiyun 	if (ifal->ifal_family != AF_INET6 ||
402*4882a593Smuzhiyun 	    ifal->ifal_prefixlen > 128)
403*4882a593Smuzhiyun 		return -EINVAL;
404*4882a593Smuzhiyun 
405*4882a593Smuzhiyun 	if (!tb[IFAL_ADDRESS])
406*4882a593Smuzhiyun 		return -EINVAL;
407*4882a593Smuzhiyun 	pfx = nla_data(tb[IFAL_ADDRESS]);
408*4882a593Smuzhiyun 
409*4882a593Smuzhiyun 	if (!tb[IFAL_LABEL])
410*4882a593Smuzhiyun 		return -EINVAL;
411*4882a593Smuzhiyun 	label = nla_get_u32(tb[IFAL_LABEL]);
412*4882a593Smuzhiyun 	if (label == IPV6_ADDR_LABEL_DEFAULT)
413*4882a593Smuzhiyun 		return -EINVAL;
414*4882a593Smuzhiyun 
415*4882a593Smuzhiyun 	switch (nlh->nlmsg_type) {
416*4882a593Smuzhiyun 	case RTM_NEWADDRLABEL:
417*4882a593Smuzhiyun 		if (ifal->ifal_index &&
418*4882a593Smuzhiyun 		    !addrlbl_ifindex_exists(net, ifal->ifal_index))
419*4882a593Smuzhiyun 			return -EINVAL;
420*4882a593Smuzhiyun 
421*4882a593Smuzhiyun 		err = ip6addrlbl_add(net, pfx, ifal->ifal_prefixlen,
422*4882a593Smuzhiyun 				     ifal->ifal_index, label,
423*4882a593Smuzhiyun 				     nlh->nlmsg_flags & NLM_F_REPLACE);
424*4882a593Smuzhiyun 		break;
425*4882a593Smuzhiyun 	case RTM_DELADDRLABEL:
426*4882a593Smuzhiyun 		err = ip6addrlbl_del(net, pfx, ifal->ifal_prefixlen,
427*4882a593Smuzhiyun 				     ifal->ifal_index);
428*4882a593Smuzhiyun 		break;
429*4882a593Smuzhiyun 	default:
430*4882a593Smuzhiyun 		err = -EOPNOTSUPP;
431*4882a593Smuzhiyun 	}
432*4882a593Smuzhiyun 	return err;
433*4882a593Smuzhiyun }
434*4882a593Smuzhiyun 
ip6addrlbl_putmsg(struct nlmsghdr * nlh,int prefixlen,int ifindex,u32 lseq)435*4882a593Smuzhiyun static void ip6addrlbl_putmsg(struct nlmsghdr *nlh,
436*4882a593Smuzhiyun 			      int prefixlen, int ifindex, u32 lseq)
437*4882a593Smuzhiyun {
438*4882a593Smuzhiyun 	struct ifaddrlblmsg *ifal = nlmsg_data(nlh);
439*4882a593Smuzhiyun 	ifal->ifal_family = AF_INET6;
440*4882a593Smuzhiyun 	ifal->__ifal_reserved = 0;
441*4882a593Smuzhiyun 	ifal->ifal_prefixlen = prefixlen;
442*4882a593Smuzhiyun 	ifal->ifal_flags = 0;
443*4882a593Smuzhiyun 	ifal->ifal_index = ifindex;
444*4882a593Smuzhiyun 	ifal->ifal_seq = lseq;
445*4882a593Smuzhiyun };
446*4882a593Smuzhiyun 
ip6addrlbl_fill(struct sk_buff * skb,struct ip6addrlbl_entry * p,u32 lseq,u32 portid,u32 seq,int event,unsigned int flags)447*4882a593Smuzhiyun static int ip6addrlbl_fill(struct sk_buff *skb,
448*4882a593Smuzhiyun 			   struct ip6addrlbl_entry *p,
449*4882a593Smuzhiyun 			   u32 lseq,
450*4882a593Smuzhiyun 			   u32 portid, u32 seq, int event,
451*4882a593Smuzhiyun 			   unsigned int flags)
452*4882a593Smuzhiyun {
453*4882a593Smuzhiyun 	struct nlmsghdr *nlh = nlmsg_put(skb, portid, seq, event,
454*4882a593Smuzhiyun 					 sizeof(struct ifaddrlblmsg), flags);
455*4882a593Smuzhiyun 	if (!nlh)
456*4882a593Smuzhiyun 		return -EMSGSIZE;
457*4882a593Smuzhiyun 
458*4882a593Smuzhiyun 	ip6addrlbl_putmsg(nlh, p->prefixlen, p->ifindex, lseq);
459*4882a593Smuzhiyun 
460*4882a593Smuzhiyun 	if (nla_put_in6_addr(skb, IFAL_ADDRESS, &p->prefix) < 0 ||
461*4882a593Smuzhiyun 	    nla_put_u32(skb, IFAL_LABEL, p->label) < 0) {
462*4882a593Smuzhiyun 		nlmsg_cancel(skb, nlh);
463*4882a593Smuzhiyun 		return -EMSGSIZE;
464*4882a593Smuzhiyun 	}
465*4882a593Smuzhiyun 
466*4882a593Smuzhiyun 	nlmsg_end(skb, nlh);
467*4882a593Smuzhiyun 	return 0;
468*4882a593Smuzhiyun }
469*4882a593Smuzhiyun 
ip6addrlbl_valid_dump_req(const struct nlmsghdr * nlh,struct netlink_ext_ack * extack)470*4882a593Smuzhiyun static int ip6addrlbl_valid_dump_req(const struct nlmsghdr *nlh,
471*4882a593Smuzhiyun 				     struct netlink_ext_ack *extack)
472*4882a593Smuzhiyun {
473*4882a593Smuzhiyun 	struct ifaddrlblmsg *ifal;
474*4882a593Smuzhiyun 
475*4882a593Smuzhiyun 	if (nlh->nlmsg_len < nlmsg_msg_size(sizeof(*ifal))) {
476*4882a593Smuzhiyun 		NL_SET_ERR_MSG_MOD(extack, "Invalid header for address label dump request");
477*4882a593Smuzhiyun 		return -EINVAL;
478*4882a593Smuzhiyun 	}
479*4882a593Smuzhiyun 
480*4882a593Smuzhiyun 	ifal = nlmsg_data(nlh);
481*4882a593Smuzhiyun 	if (ifal->__ifal_reserved || ifal->ifal_prefixlen ||
482*4882a593Smuzhiyun 	    ifal->ifal_flags || ifal->ifal_index || ifal->ifal_seq) {
483*4882a593Smuzhiyun 		NL_SET_ERR_MSG_MOD(extack, "Invalid values in header for address label dump request");
484*4882a593Smuzhiyun 		return -EINVAL;
485*4882a593Smuzhiyun 	}
486*4882a593Smuzhiyun 
487*4882a593Smuzhiyun 	if (nlmsg_attrlen(nlh, sizeof(*ifal))) {
488*4882a593Smuzhiyun 		NL_SET_ERR_MSG_MOD(extack, "Invalid data after header for address label dump request");
489*4882a593Smuzhiyun 		return -EINVAL;
490*4882a593Smuzhiyun 	}
491*4882a593Smuzhiyun 
492*4882a593Smuzhiyun 	return 0;
493*4882a593Smuzhiyun }
494*4882a593Smuzhiyun 
ip6addrlbl_dump(struct sk_buff * skb,struct netlink_callback * cb)495*4882a593Smuzhiyun static int ip6addrlbl_dump(struct sk_buff *skb, struct netlink_callback *cb)
496*4882a593Smuzhiyun {
497*4882a593Smuzhiyun 	const struct nlmsghdr *nlh = cb->nlh;
498*4882a593Smuzhiyun 	struct net *net = sock_net(skb->sk);
499*4882a593Smuzhiyun 	struct ip6addrlbl_entry *p;
500*4882a593Smuzhiyun 	int idx = 0, s_idx = cb->args[0];
501*4882a593Smuzhiyun 	int err;
502*4882a593Smuzhiyun 
503*4882a593Smuzhiyun 	if (cb->strict_check) {
504*4882a593Smuzhiyun 		err = ip6addrlbl_valid_dump_req(nlh, cb->extack);
505*4882a593Smuzhiyun 		if (err < 0)
506*4882a593Smuzhiyun 			return err;
507*4882a593Smuzhiyun 	}
508*4882a593Smuzhiyun 
509*4882a593Smuzhiyun 	rcu_read_lock();
510*4882a593Smuzhiyun 	hlist_for_each_entry_rcu(p, &net->ipv6.ip6addrlbl_table.head, list) {
511*4882a593Smuzhiyun 		if (idx >= s_idx) {
512*4882a593Smuzhiyun 			err = ip6addrlbl_fill(skb, p,
513*4882a593Smuzhiyun 					      net->ipv6.ip6addrlbl_table.seq,
514*4882a593Smuzhiyun 					      NETLINK_CB(cb->skb).portid,
515*4882a593Smuzhiyun 					      nlh->nlmsg_seq,
516*4882a593Smuzhiyun 					      RTM_NEWADDRLABEL,
517*4882a593Smuzhiyun 					      NLM_F_MULTI);
518*4882a593Smuzhiyun 			if (err < 0)
519*4882a593Smuzhiyun 				break;
520*4882a593Smuzhiyun 		}
521*4882a593Smuzhiyun 		idx++;
522*4882a593Smuzhiyun 	}
523*4882a593Smuzhiyun 	rcu_read_unlock();
524*4882a593Smuzhiyun 	cb->args[0] = idx;
525*4882a593Smuzhiyun 	return skb->len;
526*4882a593Smuzhiyun }
527*4882a593Smuzhiyun 
ip6addrlbl_msgsize(void)528*4882a593Smuzhiyun static inline int ip6addrlbl_msgsize(void)
529*4882a593Smuzhiyun {
530*4882a593Smuzhiyun 	return NLMSG_ALIGN(sizeof(struct ifaddrlblmsg))
531*4882a593Smuzhiyun 		+ nla_total_size(16)	/* IFAL_ADDRESS */
532*4882a593Smuzhiyun 		+ nla_total_size(4);	/* IFAL_LABEL */
533*4882a593Smuzhiyun }
534*4882a593Smuzhiyun 
ip6addrlbl_valid_get_req(struct sk_buff * skb,const struct nlmsghdr * nlh,struct nlattr ** tb,struct netlink_ext_ack * extack)535*4882a593Smuzhiyun static int ip6addrlbl_valid_get_req(struct sk_buff *skb,
536*4882a593Smuzhiyun 				    const struct nlmsghdr *nlh,
537*4882a593Smuzhiyun 				    struct nlattr **tb,
538*4882a593Smuzhiyun 				    struct netlink_ext_ack *extack)
539*4882a593Smuzhiyun {
540*4882a593Smuzhiyun 	struct ifaddrlblmsg *ifal;
541*4882a593Smuzhiyun 	int i, err;
542*4882a593Smuzhiyun 
543*4882a593Smuzhiyun 	if (nlh->nlmsg_len < nlmsg_msg_size(sizeof(*ifal))) {
544*4882a593Smuzhiyun 		NL_SET_ERR_MSG_MOD(extack, "Invalid header for addrlabel get request");
545*4882a593Smuzhiyun 		return -EINVAL;
546*4882a593Smuzhiyun 	}
547*4882a593Smuzhiyun 
548*4882a593Smuzhiyun 	if (!netlink_strict_get_check(skb))
549*4882a593Smuzhiyun 		return nlmsg_parse_deprecated(nlh, sizeof(*ifal), tb,
550*4882a593Smuzhiyun 					      IFAL_MAX, ifal_policy, extack);
551*4882a593Smuzhiyun 
552*4882a593Smuzhiyun 	ifal = nlmsg_data(nlh);
553*4882a593Smuzhiyun 	if (ifal->__ifal_reserved || ifal->ifal_flags || ifal->ifal_seq) {
554*4882a593Smuzhiyun 		NL_SET_ERR_MSG_MOD(extack, "Invalid values in header for addrlabel get request");
555*4882a593Smuzhiyun 		return -EINVAL;
556*4882a593Smuzhiyun 	}
557*4882a593Smuzhiyun 
558*4882a593Smuzhiyun 	err = nlmsg_parse_deprecated_strict(nlh, sizeof(*ifal), tb, IFAL_MAX,
559*4882a593Smuzhiyun 					    ifal_policy, extack);
560*4882a593Smuzhiyun 	if (err)
561*4882a593Smuzhiyun 		return err;
562*4882a593Smuzhiyun 
563*4882a593Smuzhiyun 	for (i = 0; i <= IFAL_MAX; i++) {
564*4882a593Smuzhiyun 		if (!tb[i])
565*4882a593Smuzhiyun 			continue;
566*4882a593Smuzhiyun 
567*4882a593Smuzhiyun 		switch (i) {
568*4882a593Smuzhiyun 		case IFAL_ADDRESS:
569*4882a593Smuzhiyun 			break;
570*4882a593Smuzhiyun 		default:
571*4882a593Smuzhiyun 			NL_SET_ERR_MSG_MOD(extack, "Unsupported attribute in addrlabel get request");
572*4882a593Smuzhiyun 			return -EINVAL;
573*4882a593Smuzhiyun 		}
574*4882a593Smuzhiyun 	}
575*4882a593Smuzhiyun 
576*4882a593Smuzhiyun 	return 0;
577*4882a593Smuzhiyun }
578*4882a593Smuzhiyun 
ip6addrlbl_get(struct sk_buff * in_skb,struct nlmsghdr * nlh,struct netlink_ext_ack * extack)579*4882a593Smuzhiyun static int ip6addrlbl_get(struct sk_buff *in_skb, struct nlmsghdr *nlh,
580*4882a593Smuzhiyun 			  struct netlink_ext_ack *extack)
581*4882a593Smuzhiyun {
582*4882a593Smuzhiyun 	struct net *net = sock_net(in_skb->sk);
583*4882a593Smuzhiyun 	struct ifaddrlblmsg *ifal;
584*4882a593Smuzhiyun 	struct nlattr *tb[IFAL_MAX+1];
585*4882a593Smuzhiyun 	struct in6_addr *addr;
586*4882a593Smuzhiyun 	u32 lseq;
587*4882a593Smuzhiyun 	int err = 0;
588*4882a593Smuzhiyun 	struct ip6addrlbl_entry *p;
589*4882a593Smuzhiyun 	struct sk_buff *skb;
590*4882a593Smuzhiyun 
591*4882a593Smuzhiyun 	err = ip6addrlbl_valid_get_req(in_skb, nlh, tb, extack);
592*4882a593Smuzhiyun 	if (err < 0)
593*4882a593Smuzhiyun 		return err;
594*4882a593Smuzhiyun 
595*4882a593Smuzhiyun 	ifal = nlmsg_data(nlh);
596*4882a593Smuzhiyun 
597*4882a593Smuzhiyun 	if (ifal->ifal_family != AF_INET6 ||
598*4882a593Smuzhiyun 	    ifal->ifal_prefixlen != 128)
599*4882a593Smuzhiyun 		return -EINVAL;
600*4882a593Smuzhiyun 
601*4882a593Smuzhiyun 	if (ifal->ifal_index &&
602*4882a593Smuzhiyun 	    !addrlbl_ifindex_exists(net, ifal->ifal_index))
603*4882a593Smuzhiyun 		return -EINVAL;
604*4882a593Smuzhiyun 
605*4882a593Smuzhiyun 	if (!tb[IFAL_ADDRESS])
606*4882a593Smuzhiyun 		return -EINVAL;
607*4882a593Smuzhiyun 	addr = nla_data(tb[IFAL_ADDRESS]);
608*4882a593Smuzhiyun 
609*4882a593Smuzhiyun 	skb = nlmsg_new(ip6addrlbl_msgsize(), GFP_KERNEL);
610*4882a593Smuzhiyun 	if (!skb)
611*4882a593Smuzhiyun 		return -ENOBUFS;
612*4882a593Smuzhiyun 
613*4882a593Smuzhiyun 	err = -ESRCH;
614*4882a593Smuzhiyun 
615*4882a593Smuzhiyun 	rcu_read_lock();
616*4882a593Smuzhiyun 	p = __ipv6_addr_label(net, addr, ipv6_addr_type(addr), ifal->ifal_index);
617*4882a593Smuzhiyun 	lseq = net->ipv6.ip6addrlbl_table.seq;
618*4882a593Smuzhiyun 	if (p)
619*4882a593Smuzhiyun 		err = ip6addrlbl_fill(skb, p, lseq,
620*4882a593Smuzhiyun 				      NETLINK_CB(in_skb).portid,
621*4882a593Smuzhiyun 				      nlh->nlmsg_seq,
622*4882a593Smuzhiyun 				      RTM_NEWADDRLABEL, 0);
623*4882a593Smuzhiyun 	rcu_read_unlock();
624*4882a593Smuzhiyun 
625*4882a593Smuzhiyun 	if (err < 0) {
626*4882a593Smuzhiyun 		WARN_ON(err == -EMSGSIZE);
627*4882a593Smuzhiyun 		kfree_skb(skb);
628*4882a593Smuzhiyun 	} else {
629*4882a593Smuzhiyun 		err = rtnl_unicast(skb, net, NETLINK_CB(in_skb).portid);
630*4882a593Smuzhiyun 	}
631*4882a593Smuzhiyun 	return err;
632*4882a593Smuzhiyun }
633*4882a593Smuzhiyun 
ipv6_addr_label_rtnl_register(void)634*4882a593Smuzhiyun int __init ipv6_addr_label_rtnl_register(void)
635*4882a593Smuzhiyun {
636*4882a593Smuzhiyun 	int ret;
637*4882a593Smuzhiyun 
638*4882a593Smuzhiyun 	ret = rtnl_register_module(THIS_MODULE, PF_INET6, RTM_NEWADDRLABEL,
639*4882a593Smuzhiyun 				   ip6addrlbl_newdel,
640*4882a593Smuzhiyun 				   NULL, RTNL_FLAG_DOIT_UNLOCKED);
641*4882a593Smuzhiyun 	if (ret < 0)
642*4882a593Smuzhiyun 		return ret;
643*4882a593Smuzhiyun 	ret = rtnl_register_module(THIS_MODULE, PF_INET6, RTM_DELADDRLABEL,
644*4882a593Smuzhiyun 				   ip6addrlbl_newdel,
645*4882a593Smuzhiyun 				   NULL, RTNL_FLAG_DOIT_UNLOCKED);
646*4882a593Smuzhiyun 	if (ret < 0)
647*4882a593Smuzhiyun 		return ret;
648*4882a593Smuzhiyun 	ret = rtnl_register_module(THIS_MODULE, PF_INET6, RTM_GETADDRLABEL,
649*4882a593Smuzhiyun 				   ip6addrlbl_get,
650*4882a593Smuzhiyun 				   ip6addrlbl_dump, RTNL_FLAG_DOIT_UNLOCKED);
651*4882a593Smuzhiyun 	return ret;
652*4882a593Smuzhiyun }
653