xref: /freebsd/sys/netinet6/scope6.c (revision 6af83ee0d2941d18880b6aaa2b4facd1d30c6106)
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 the global sid default stored in
50  * sid_default below.
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 	struct scope6_id *sid;
75 
76 	sid = (struct scope6_id *)malloc(sizeof(*sid), M_IFADDR, M_WAITOK);
77 	bzero(sid, sizeof(*sid));
78 
79 	/*
80 	 * XXX: IPV6_ADDR_SCOPE_xxx macros are not standard.
81 	 * Should we rather hardcode here?
82 	 */
83 	sid->s6id_list[IPV6_ADDR_SCOPE_INTFACELOCAL] = ifp->if_index;
84 	sid->s6id_list[IPV6_ADDR_SCOPE_LINKLOCAL] = ifp->if_index;
85 #ifdef MULTI_SCOPE
86 	/* by default, we don't care about scope boundary for these scopes. */
87 	sid->s6id_list[IPV6_ADDR_SCOPE_SITELOCAL] = 1;
88 	sid->s6id_list[IPV6_ADDR_SCOPE_ORGLOCAL] = 1;
89 #endif
90 
91 	return sid;
92 }
93 
94 void
95 scope6_ifdetach(sid)
96 	struct scope6_id *sid;
97 {
98 
99 	free(sid, M_IFADDR);
100 }
101 
102 int
103 scope6_set(ifp, idlist)
104 	struct ifnet *ifp;
105 	struct scope6_id *idlist;
106 {
107 	int i;
108 	int error = 0;
109 	struct scope6_id *sid = NULL;
110 
111 	IF_AFDATA_LOCK(ifp);
112 	sid = SID(ifp);
113 
114 	if (!sid) {	/* paranoid? */
115 		IF_AFDATA_UNLOCK(ifp);
116 		return (EINVAL);
117 	}
118 
119 	/*
120 	 * XXX: We need more consistency checks of the relationship among
121 	 * scopes (e.g. an organization should be larger than a site).
122 	 */
123 
124 	/*
125 	 * TODO(XXX): after setting, we should reflect the changes to
126 	 * interface addresses, routing table entries, PCB entries...
127 	 */
128 
129 	SCOPE6_LOCK();
130 	for (i = 0; i < 16; i++) {
131 		if (idlist->s6id_list[i] &&
132 		    idlist->s6id_list[i] != sid->s6id_list[i]) {
133 			/*
134 			 * An interface zone ID must be the corresponding
135 			 * interface index by definition.
136 			 */
137 			if (i == IPV6_ADDR_SCOPE_INTFACELOCAL &&
138 			    idlist->s6id_list[i] != ifp->if_index) {
139 				IF_AFDATA_UNLOCK(ifp);
140 				SCOPE6_UNLOCK();
141 				return (EINVAL);
142 			}
143 
144 			if (i == IPV6_ADDR_SCOPE_LINKLOCAL &&
145 			    idlist->s6id_list[i] > if_index) {
146 				/*
147 				 * XXX: theoretically, there should be no
148 				 * relationship between link IDs and interface
149 				 * IDs, but we check the consistency for
150 				 * safety in later use.
151 				 */
152 				IF_AFDATA_UNLOCK(ifp);
153 				SCOPE6_UNLOCK();
154 				return (EINVAL);
155 			}
156 
157 			/*
158 			 * XXX: we must need lots of work in this case,
159 			 * but we simply set the new value in this initial
160 			 * implementation.
161 			 */
162 			sid->s6id_list[i] = idlist->s6id_list[i];
163 		}
164 	}
165 	SCOPE6_UNLOCK();
166 	IF_AFDATA_UNLOCK(ifp);
167 
168 	return (error);
169 }
170 
171 int
172 scope6_get(ifp, idlist)
173 	struct ifnet *ifp;
174 	struct scope6_id *idlist;
175 {
176 	/* We only need to lock the interface's afdata for SID() to work. */
177 	IF_AFDATA_LOCK(ifp);
178 	struct scope6_id *sid = SID(ifp);
179 
180 	if (sid == NULL) {	/* paranoid? */
181 		IF_AFDATA_UNLOCK(ifp);
182 		return (EINVAL);
183 	}
184 
185 	SCOPE6_LOCK();
186 	*idlist = *sid;
187 	SCOPE6_UNLOCK();
188 
189 	IF_AFDATA_UNLOCK(ifp);
190 	return (0);
191 }
192 
193 
194 /*
195  * Get a scope of the address. Node-local, link-local, site-local or global.
196  */
197 int
198 in6_addrscope(addr)
199 	struct in6_addr *addr;
200 {
201 	int scope;
202 
203 	if (addr->s6_addr[0] == 0xfe) {
204 		scope = addr->s6_addr[1] & 0xc0;
205 
206 		switch (scope) {
207 		case 0x80:
208 			return IPV6_ADDR_SCOPE_LINKLOCAL;
209 			break;
210 		case 0xc0:
211 			return IPV6_ADDR_SCOPE_SITELOCAL;
212 			break;
213 		default:
214 			return IPV6_ADDR_SCOPE_GLOBAL; /* just in case */
215 			break;
216 		}
217 	}
218 
219 
220 	if (addr->s6_addr[0] == 0xff) {
221 		scope = addr->s6_addr[1] & 0x0f;
222 
223 		/*
224 		 * due to other scope such as reserved,
225 		 * return scope doesn't work.
226 		 */
227 		switch (scope) {
228 		case IPV6_ADDR_SCOPE_INTFACELOCAL:
229 			return IPV6_ADDR_SCOPE_INTFACELOCAL;
230 			break;
231 		case IPV6_ADDR_SCOPE_LINKLOCAL:
232 			return IPV6_ADDR_SCOPE_LINKLOCAL;
233 			break;
234 		case IPV6_ADDR_SCOPE_SITELOCAL:
235 			return IPV6_ADDR_SCOPE_SITELOCAL;
236 			break;
237 		default:
238 			return IPV6_ADDR_SCOPE_GLOBAL;
239 			break;
240 		}
241 	}
242 
243 	/*
244 	 * Regard loopback and unspecified addresses as global, since
245 	 * they have no ambiguity.
246 	 */
247 	if (bcmp(&in6addr_loopback, addr, sizeof(*addr) - 1) == 0) {
248 		if (addr->s6_addr[15] == 1) /* loopback */
249 			return IPV6_ADDR_SCOPE_LINKLOCAL;
250 		if (addr->s6_addr[15] == 0) /* unspecified */
251 			return IPV6_ADDR_SCOPE_GLOBAL; /* XXX: correct? */
252 	}
253 
254 	return IPV6_ADDR_SCOPE_GLOBAL;
255 }
256 
257 /*
258  * When we introduce the "4+28" split semantics in sin6_scope_id,
259  * a 32bit integer is not enough to tell a large ID from an error (-1).
260  * So, we intentionally use a large type as the return value.
261  */
262 int
263 in6_addr2zoneid(ifp, addr, ret_id)
264 	struct ifnet *ifp;	/* must not be NULL */
265 	struct in6_addr *addr;	/* must not be NULL */
266 	u_int32_t *ret_id;	/* must not be NULL */
267 {
268 	int scope;
269 	u_int32_t zoneid = 0;
270 	struct scope6_id *sid = NULL;
271 
272 	IF_AFDATA_LOCK(ifp);
273 
274 	sid = SID(ifp);
275 
276 #ifdef DIAGNOSTIC
277 	if (sid == NULL) { /* should not happen */
278 		panic("in6_addr2zoneid: scope array is NULL");
279 		/* NOTREACHED */
280 	}
281 	if (ret_id == NULL) {
282 		panic("in6_addr2zoneid: return ID is null");
283 		/* NOTREACHED */
284 	}
285 #endif
286 
287 	/*
288 	 * special case: the loopback address can only belong to a loopback
289 	 * interface.
290 	 */
291 	if (IN6_IS_ADDR_LOOPBACK(addr)) {
292 		if (!(ifp->if_flags & IFF_LOOPBACK)) {
293 			IF_AFDATA_UNLOCK(ifp);
294 			return (-1);
295 		} else {
296 			*ret_id = 0; /* there's no ambiguity */
297 			IF_AFDATA_UNLOCK(ifp);
298 			return (0);
299 		}
300 	}
301 
302 	scope = in6_addrscope(addr);
303 
304 	/*
305 	 * XXX: These are all u_int32_t reads, so may not require locking.
306 	 */
307 	SCOPE6_LOCK();
308 	switch (scope) {
309 	case IPV6_ADDR_SCOPE_INTFACELOCAL: /* should be interface index */
310 		zoneid = sid->s6id_list[IPV6_ADDR_SCOPE_INTFACELOCAL];
311 		break;
312 
313 	case IPV6_ADDR_SCOPE_LINKLOCAL:
314 		zoneid = sid->s6id_list[IPV6_ADDR_SCOPE_LINKLOCAL];
315 		break;
316 
317 	case IPV6_ADDR_SCOPE_SITELOCAL:
318 		zoneid = sid->s6id_list[IPV6_ADDR_SCOPE_SITELOCAL];
319 		break;
320 
321 	case IPV6_ADDR_SCOPE_ORGLOCAL:
322 		zoneid = sid->s6id_list[IPV6_ADDR_SCOPE_ORGLOCAL];
323 		break;
324 
325 	default:
326 		zoneid = 0;	/* XXX: treat as global. */
327 		break;
328 	}
329 	SCOPE6_UNLOCK();
330 
331 	*ret_id = zoneid;
332 
333 	IF_AFDATA_UNLOCK(ifp);
334 
335 	return (0);
336 }
337 
338 void
339 scope6_setdefault(ifp)
340 	struct ifnet *ifp;	/* note that this might be NULL */
341 {
342 	/*
343 	 * Currently, this function just sets the default "interfaces"
344 	 * and "links" according to the given interface.
345 	 * We might eventually have to separate the notion of "link" from
346 	 * "interface" and provide a user interface to set the default.
347 	 */
348 	SCOPE6_LOCK();
349 	if (ifp) {
350 		sid_default.s6id_list[IPV6_ADDR_SCOPE_INTFACELOCAL] =
351 			ifp->if_index;
352 		sid_default.s6id_list[IPV6_ADDR_SCOPE_LINKLOCAL] =
353 			ifp->if_index;
354 	} else {
355 		sid_default.s6id_list[IPV6_ADDR_SCOPE_INTFACELOCAL] = 0;
356 		sid_default.s6id_list[IPV6_ADDR_SCOPE_LINKLOCAL] = 0;
357 	}
358 	SCOPE6_UNLOCK();
359 }
360 
361 int
362 scope6_get_default(idlist)
363 	struct scope6_id *idlist;
364 {
365 
366 	SCOPE6_LOCK();
367 	*idlist = sid_default;
368 	SCOPE6_UNLOCK();
369 
370 	return (0);
371 }
372 
373 u_int32_t
374 scope6_addr2default(addr)
375 	struct in6_addr *addr;
376 {
377 	u_int32_t id;
378 
379 	/*
380 	 * special case: The loopback address should be considered as
381 	 * link-local, but there's no ambiguity in the syntax.
382 	 */
383 	if (IN6_IS_ADDR_LOOPBACK(addr))
384 		return (0);
385 
386 	/*
387 	 * XXX: 32-bit read is atomic on all our platforms, is it OK
388 	 * not to lock here?
389 	 */
390 	SCOPE6_LOCK();
391 	id = sid_default.s6id_list[in6_addrscope(addr)];
392 	SCOPE6_UNLOCK();
393 	return (id);
394 }
395