xref: /freebsd/sys/netinet6/scope6.c (revision 2546665afcaf0d53dc2c7058fee96354b3680f5a)
1 /*	$FreeBSD$	*/
2 /*	$KAME: scope6.c,v 1.10 2000/07/24 13:29:31 itojun Exp $	*/
3 
4 /*
5  * Copyright (C) 2000 WIDE Project.
6  * All rights reserved.
7  *
8  * Redistribution and use in source and binary forms, with or without
9  * modification, are permitted provided that the following conditions
10  * are met:
11  * 1. Redistributions of source code must retain the above copyright
12  *    notice, this list of conditions and the following disclaimer.
13  * 2. Redistributions in binary form must reproduce the above copyright
14  *    notice, this list of conditions and the following disclaimer in the
15  *    documentation and/or other materials provided with the distribution.
16  * 3. Neither the name of the project nor the names of its contributors
17  *    may be used to endorse or promote products derived from this software
18  *    without specific prior written permission.
19  *
20  * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND
21  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
22  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
23  * ARE DISCLAIMED.  IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE
24  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
25  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
26  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
27  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
28  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
29  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
30  * SUCH DAMAGE.
31  */
32 
33 #include <sys/param.h>
34 #include <sys/malloc.h>
35 #include <sys/mbuf.h>
36 #include <sys/socket.h>
37 #include <sys/systm.h>
38 #include <sys/queue.h>
39 
40 #include <net/route.h>
41 #include <net/if.h>
42 
43 #include <netinet/in.h>
44 
45 #include <netinet6/in6_var.h>
46 #include <netinet6/scope6_var.h>
47 
48 /*
49  * The scope6_lock protects both the global sid default stored in
50  * sid_default below, but also per-interface sid data.
51  */
52 static struct mtx scope6_lock;
53 #define	SCOPE6_LOCK_INIT()	mtx_init(&scope6_lock, "scope6_lock", NULL, MTX_DEF)
54 #define	SCOPE6_LOCK()		mtx_lock(&scope6_lock)
55 #define	SCOPE6_UNLOCK()		mtx_unlock(&scope6_lock)
56 #define	SCOPE6_LOCK_ASSERT()	mtx_assert(&scope6_lock, MA_OWNED)
57 
58 static struct scope6_id sid_default;
59 #define SID(ifp) \
60 	(((struct in6_ifextra *)(ifp)->if_afdata[AF_INET6])->scope6_id)
61 
62 void
63 scope6_init()
64 {
65 
66 	SCOPE6_LOCK_INIT();
67 	bzero(&sid_default, sizeof(sid_default));
68 }
69 
70 struct scope6_id *
71 scope6_ifattach(ifp)
72 	struct ifnet *ifp;
73 {
74 	int s = splnet();
75 	struct scope6_id *sid;
76 
77 	sid = (struct scope6_id *)malloc(sizeof(*sid), M_IFADDR, M_WAITOK);
78 	bzero(sid, sizeof(*sid));
79 
80 	/*
81 	 * XXX: IPV6_ADDR_SCOPE_xxx macros are not standard.
82 	 * Should we rather hardcode here?
83 	 */
84 	sid->s6id_list[IPV6_ADDR_SCOPE_INTFACELOCAL] = ifp->if_index;
85 	sid->s6id_list[IPV6_ADDR_SCOPE_LINKLOCAL] = ifp->if_index;
86 #ifdef MULTI_SCOPE
87 	/* by default, we don't care about scope boundary for these scopes. */
88 	sid->s6id_list[IPV6_ADDR_SCOPE_SITELOCAL] = 1;
89 	sid->s6id_list[IPV6_ADDR_SCOPE_ORGLOCAL] = 1;
90 #endif
91 
92 	splx(s);
93 	return sid;
94 }
95 
96 void
97 scope6_ifdetach(sid)
98 	struct scope6_id *sid;
99 {
100 
101 	free(sid, M_IFADDR);
102 }
103 
104 int
105 scope6_set(ifp, idlist)
106 	struct ifnet *ifp;
107 	struct scope6_id *idlist;
108 {
109 	int i, s;
110 	int error = 0;
111 	struct scope6_id *sid = SID(ifp);
112 
113 	if (!sid)	/* paranoid? */
114 		return (EINVAL);
115 
116 	/*
117 	 * XXX: We need more consistency checks of the relationship among
118 	 * scopes (e.g. an organization should be larger than a site).
119 	 */
120 
121 	/*
122 	 * TODO(XXX): after setting, we should reflect the changes to
123 	 * interface addresses, routing table entries, PCB entries...
124 	 */
125 
126 	s = splnet();
127 
128 	SCOPE6_LOCK();
129 	for (i = 0; i < 16; i++) {
130 		if (idlist->s6id_list[i] &&
131 		    idlist->s6id_list[i] != sid->s6id_list[i]) {
132 			/*
133 			 * An interface zone ID must be the corresponding
134 			 * interface index by definition.
135 			 */
136 			if (i == IPV6_ADDR_SCOPE_INTFACELOCAL &&
137 			    idlist->s6id_list[i] != ifp->if_index) {
138 				splx(s);
139 				return (EINVAL);
140 			}
141 
142 			if (i == IPV6_ADDR_SCOPE_LINKLOCAL &&
143 			    idlist->s6id_list[i] > if_index) {
144 				/*
145 				 * XXX: theoretically, there should be no
146 				 * relationship between link IDs and interface
147 				 * IDs, but we check the consistency for
148 				 * safety in later use.
149 				 */
150 				splx(s);
151 				return (EINVAL);
152 			}
153 
154 			/*
155 			 * XXX: we must need lots of work in this case,
156 			 * but we simply set the new value in this initial
157 			 * implementation.
158 			 */
159 			sid->s6id_list[i] = idlist->s6id_list[i];
160 		}
161 	}
162 	SCOPE6_UNLOCK();
163 	splx(s);
164 
165 	return (error);
166 }
167 
168 int
169 scope6_get(ifp, idlist)
170 	struct ifnet *ifp;
171 	struct scope6_id *idlist;
172 {
173 	struct scope6_id *sid = SID(ifp);
174 
175 	if (sid == NULL)	/* paranoid? */
176 		return (EINVAL);
177 
178 	SCOPE6_LOCK();
179 	*idlist = *sid;
180 	SCOPE6_UNLOCK();
181 
182 	return (0);
183 }
184 
185 
186 /*
187  * Get a scope of the address. Node-local, link-local, site-local or global.
188  */
189 int
190 in6_addrscope(addr)
191 	struct in6_addr *addr;
192 {
193 	int scope;
194 
195 	if (addr->s6_addr[0] == 0xfe) {
196 		scope = addr->s6_addr[1] & 0xc0;
197 
198 		switch (scope) {
199 		case 0x80:
200 			return IPV6_ADDR_SCOPE_LINKLOCAL;
201 			break;
202 		case 0xc0:
203 			return IPV6_ADDR_SCOPE_SITELOCAL;
204 			break;
205 		default:
206 			return IPV6_ADDR_SCOPE_GLOBAL; /* just in case */
207 			break;
208 		}
209 	}
210 
211 
212 	if (addr->s6_addr[0] == 0xff) {
213 		scope = addr->s6_addr[1] & 0x0f;
214 
215 		/*
216 		 * due to other scope such as reserved,
217 		 * return scope doesn't work.
218 		 */
219 		switch (scope) {
220 		case IPV6_ADDR_SCOPE_INTFACELOCAL:
221 			return IPV6_ADDR_SCOPE_INTFACELOCAL;
222 			break;
223 		case IPV6_ADDR_SCOPE_LINKLOCAL:
224 			return IPV6_ADDR_SCOPE_LINKLOCAL;
225 			break;
226 		case IPV6_ADDR_SCOPE_SITELOCAL:
227 			return IPV6_ADDR_SCOPE_SITELOCAL;
228 			break;
229 		default:
230 			return IPV6_ADDR_SCOPE_GLOBAL;
231 			break;
232 		}
233 	}
234 
235 	/*
236 	 * Regard loopback and unspecified addresses as global, since
237 	 * they have no ambiguity.
238 	 */
239 	if (bcmp(&in6addr_loopback, addr, sizeof(*addr) - 1) == 0) {
240 		if (addr->s6_addr[15] == 1) /* loopback */
241 			return IPV6_ADDR_SCOPE_LINKLOCAL;
242 		if (addr->s6_addr[15] == 0) /* unspecified */
243 			return IPV6_ADDR_SCOPE_GLOBAL; /* XXX: correct? */
244 	}
245 
246 	return IPV6_ADDR_SCOPE_GLOBAL;
247 }
248 
249 /*
250  * When we introduce the "4+28" split semantics in sin6_scope_id,
251  * a 32bit integer is not enough to tell a large ID from an error (-1).
252  * So, we intentionally use a large type as the return value.
253  */
254 int
255 in6_addr2zoneid(ifp, addr, ret_id)
256 	struct ifnet *ifp;	/* must not be NULL */
257 	struct in6_addr *addr;	/* must not be NULL */
258 	u_int32_t *ret_id;	/* must not be NULL */
259 {
260 	int scope;
261 	u_int32_t zoneid = 0;
262 	struct scope6_id *sid = SID(ifp);
263 
264 #ifdef DIAGNOSTIC
265 	if (sid == NULL) { /* should not happen */
266 		panic("in6_addr2zoneid: scope array is NULL");
267 		/* NOTREACHED */
268 	}
269 	if (ret_id == NULL) {
270 		panic("in6_addr2zoneid: return ID is null");
271 		/* NOTREACHED */
272 	}
273 #endif
274 
275 	/*
276 	 * special case: the loopback address can only belong to a loopback
277 	 * interface.
278 	 */
279 	if (IN6_IS_ADDR_LOOPBACK(addr)) {
280 		if (!(ifp->if_flags & IFF_LOOPBACK))
281 			return (-1);
282 		else {
283 			*ret_id = 0; /* there's no ambiguity */
284 			return (0);
285 		}
286 	}
287 
288 	scope = in6_addrscope(addr);
289 
290 	/*
291 	 * XXX: These are all u_int32_t reads, so may not require locking.
292 	 */
293 	SCOPE6_LOCK();
294 	switch (scope) {
295 	case IPV6_ADDR_SCOPE_INTFACELOCAL: /* should be interface index */
296 		zoneid = sid->s6id_list[IPV6_ADDR_SCOPE_INTFACELOCAL];
297 		break;
298 
299 	case IPV6_ADDR_SCOPE_LINKLOCAL:
300 		zoneid = sid->s6id_list[IPV6_ADDR_SCOPE_LINKLOCAL];
301 		break;
302 
303 	case IPV6_ADDR_SCOPE_SITELOCAL:
304 		zoneid = sid->s6id_list[IPV6_ADDR_SCOPE_SITELOCAL];
305 		break;
306 
307 	case IPV6_ADDR_SCOPE_ORGLOCAL:
308 		zoneid = sid->s6id_list[IPV6_ADDR_SCOPE_ORGLOCAL];
309 		break;
310 
311 	default:
312 		zoneid = 0;	/* XXX: treat as global. */
313 		break;
314 	}
315 	SCOPE6_UNLOCK();
316 
317 	*ret_id = zoneid;
318 	return (0);
319 }
320 
321 void
322 scope6_setdefault(ifp)
323 	struct ifnet *ifp;	/* note that this might be NULL */
324 {
325 	/*
326 	 * Currently, this function just set the default "interfaces"
327 	 * and "links" according to the given interface.
328 	 * We might eventually have to separate the notion of "link" from
329 	 * "interface" and provide a user interface to set the default.
330 	 */
331 	SCOPE6_LOCK();
332 	if (ifp) {
333 		sid_default.s6id_list[IPV6_ADDR_SCOPE_INTFACELOCAL] =
334 			ifp->if_index;
335 		sid_default.s6id_list[IPV6_ADDR_SCOPE_LINKLOCAL] =
336 			ifp->if_index;
337 	} else {
338 		sid_default.s6id_list[IPV6_ADDR_SCOPE_INTFACELOCAL] = 0;
339 		sid_default.s6id_list[IPV6_ADDR_SCOPE_LINKLOCAL] = 0;
340 	}
341 	SCOPE6_UNLOCK();
342 }
343 
344 int
345 scope6_get_default(idlist)
346 	struct scope6_id *idlist;
347 {
348 
349 	SCOPE6_LOCK();
350 	*idlist = sid_default;
351 	SCOPE6_UNLOCK();
352 
353 	return (0);
354 }
355 
356 u_int32_t
357 scope6_addr2default(addr)
358 	struct in6_addr *addr;
359 {
360 	u_int32_t id;
361 
362 	/*
363 	 * special case: The loopback address should be considered as
364 	 * link-local, but there's no ambiguity in the syntax.
365 	 */
366 	if (IN6_IS_ADDR_LOOPBACK(addr))
367 		return (0);
368 
369 	/*
370 	 * XXX: 32-bit read is atomic on all our platforms, is it OK
371 	 * not to lock here?
372 	 */
373 	SCOPE6_LOCK();
374 	id = sid_default.s6id_list[in6_addrscope(addr)];
375 	SCOPE6_UNLOCK();
376 	return (id);
377 }
378