xref: /dflybsd-src/sys/netinet6/scope6.c (revision 48d201a5a8c1dab4aa7166b0812594c101fc43c3)
1 /*	$FreeBSD: src/sys/netinet6/scope6.c,v 1.1.2.3 2002/04/01 15:29:04 ume Exp $	*/
2 /*	$DragonFly: src/sys/netinet6/scope6.c,v 1.3 2004/05/20 18:30:36 cpressey Exp $	*/
3 /*	$KAME: scope6.c,v 1.10 2000/07/24 13:29:31 itojun Exp $	*/
4 
5 /*
6  * Copyright (C) 2000 WIDE Project.
7  * All rights reserved.
8  *
9  * Redistribution and use in source and binary forms, with or without
10  * modification, are permitted provided that the following conditions
11  * are met:
12  * 1. Redistributions of source code must retain the above copyright
13  *    notice, this list of conditions and the following disclaimer.
14  * 2. Redistributions in binary form must reproduce the above copyright
15  *    notice, this list of conditions and the following disclaimer in the
16  *    documentation and/or other materials provided with the distribution.
17  * 3. Neither the name of the project nor the names of its contributors
18  *    may be used to endorse or promote products derived from this software
19  *    without specific prior written permission.
20  *
21  * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND
22  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
23  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
24  * ARE DISCLAIMED.  IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE
25  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
26  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
27  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
28  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
29  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
30  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
31  * SUCH DAMAGE.
32  */
33 
34 #include <sys/param.h>
35 #include <sys/malloc.h>
36 #include <sys/mbuf.h>
37 #include <sys/socket.h>
38 #include <sys/systm.h>
39 #include <sys/queue.h>
40 
41 #include <net/route.h>
42 #include <net/if.h>
43 
44 #include <netinet/in.h>
45 
46 #include <netinet6/in6_var.h>
47 #include <netinet6/scope6_var.h>
48 
49 struct scope6_id {
50 	/*
51 	 * 16 is correspondent to 4bit multicast scope field.
52 	 * i.e. from node-local to global with some reserved/unassigned types.
53 	 */
54 	u_int32_t s6id_list[16];
55 };
56 static size_t if_indexlim = 8;
57 struct scope6_id *scope6_ids = NULL;
58 
59 void
60 scope6_ifattach(struct ifnet *ifp)
61 {
62 	int s = splnet();
63 
64 	/*
65 	 * We have some arrays that should be indexed by if_index.
66 	 * since if_index will grow dynamically, they should grow too.
67 	 */
68 	if (scope6_ids == NULL || if_index >= if_indexlim) {
69 		size_t n;
70 		caddr_t q;
71 
72 		while (if_index >= if_indexlim)
73 			if_indexlim <<= 1;
74 
75 		/* grow scope index array */
76 		n = if_indexlim * sizeof(struct scope6_id);
77 		/* XXX: need new malloc type? */
78 		q = (caddr_t)malloc(n, M_IFADDR, M_WAITOK);
79 		bzero(q, n);
80 		if (scope6_ids) {
81 			bcopy((caddr_t)scope6_ids, q, n/2);
82 			free((caddr_t)scope6_ids, M_IFADDR);
83 		}
84 		scope6_ids = (struct scope6_id *)q;
85 	}
86 
87 #define SID scope6_ids[ifp->if_index]
88 
89 	/* don't initialize if called twice */
90 	if (SID.s6id_list[IPV6_ADDR_SCOPE_LINKLOCAL]) {
91 		splx(s);
92 		return;
93 	}
94 
95 	/*
96 	 * XXX: IPV6_ADDR_SCOPE_xxx macros are not standard.
97 	 * Should we rather hardcode here?
98 	 */
99 	SID.s6id_list[IPV6_ADDR_SCOPE_LINKLOCAL] = ifp->if_index;
100 #ifdef MULTI_SCOPE
101 	/* by default, we don't care about scope boundary for these scopes. */
102 	SID.s6id_list[IPV6_ADDR_SCOPE_SITELOCAL] = 1;
103 	SID.s6id_list[IPV6_ADDR_SCOPE_ORGLOCAL] = 1;
104 #endif
105 #undef SID
106 
107 	splx(s);
108 }
109 
110 int
111 scope6_set(struct ifnet *ifp, u_int32_t *idlist)
112 {
113 	int i, s;
114 	int error = 0;
115 
116 	if (scope6_ids == NULL)	/* paranoid? */
117 		return(EINVAL);
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 	s = splnet();
130 
131 	for (i = 0; i < 16; i++) {
132 		if (idlist[i] &&
133 		    idlist[i] != scope6_ids[ifp->if_index].s6id_list[i]) {
134 			if (i == IPV6_ADDR_SCOPE_LINKLOCAL &&
135 			    idlist[i] > if_index) {
136 				/*
137 				 * XXX: theoretically, there should be no
138 				 * relationship between link IDs and interface
139 				 * IDs, but we check the consistency for
140 				 * safety in later use.
141 				 */
142 				splx(s);
143 				return(EINVAL);
144 			}
145 
146 			/*
147 			 * XXX: we must need lots of work in this case,
148 			 * but we simply set the new value in this initial
149 			 * implementation.
150 			 */
151 			scope6_ids[ifp->if_index].s6id_list[i] = idlist[i];
152 		}
153 	}
154 	splx(s);
155 
156 	return(error);
157 }
158 
159 int
160 scope6_get(struct ifnet *ifp, u_int32_t *idlist)
161 {
162 	if (scope6_ids == NULL)	/* paranoid? */
163 		return(EINVAL);
164 
165 	bcopy(scope6_ids[ifp->if_index].s6id_list, idlist,
166 	      sizeof(scope6_ids[ifp->if_index].s6id_list));
167 
168 	return(0);
169 }
170 
171 
172 /*
173  * Get a scope of the address. Node-local, link-local, site-local or global.
174  */
175 int
176 in6_addrscope(struct in6_addr *addr)
177 {
178 	int scope;
179 
180 	if (addr->s6_addr8[0] == 0xfe) {
181 		scope = addr->s6_addr8[1] & 0xc0;
182 
183 		switch (scope) {
184 		case 0x80:
185 			return IPV6_ADDR_SCOPE_LINKLOCAL;
186 			break;
187 		case 0xc0:
188 			return IPV6_ADDR_SCOPE_SITELOCAL;
189 			break;
190 		default:
191 			return IPV6_ADDR_SCOPE_GLOBAL; /* just in case */
192 			break;
193 		}
194 	}
195 
196 
197 	if (addr->s6_addr8[0] == 0xff) {
198 		scope = addr->s6_addr8[1] & 0x0f;
199 
200 		/*
201 		 * due to other scope such as reserved,
202 		 * return scope doesn't work.
203 		 */
204 		switch (scope) {
205 		case IPV6_ADDR_SCOPE_NODELOCAL:
206 			return IPV6_ADDR_SCOPE_NODELOCAL;
207 			break;
208 		case IPV6_ADDR_SCOPE_LINKLOCAL:
209 			return IPV6_ADDR_SCOPE_LINKLOCAL;
210 			break;
211 		case IPV6_ADDR_SCOPE_SITELOCAL:
212 			return IPV6_ADDR_SCOPE_SITELOCAL;
213 			break;
214 		default:
215 			return IPV6_ADDR_SCOPE_GLOBAL;
216 			break;
217 		}
218 	}
219 
220 	if (bcmp(&in6addr_loopback, addr, sizeof(*addr) - 1) == 0) {
221 		if (addr->s6_addr8[15] == 1) /* loopback */
222 			return IPV6_ADDR_SCOPE_NODELOCAL;
223 		if (addr->s6_addr8[15] == 0) /* unspecified */
224 			return IPV6_ADDR_SCOPE_LINKLOCAL;
225 	}
226 
227 	return IPV6_ADDR_SCOPE_GLOBAL;
228 }
229 
230 int
231 in6_addr2scopeid(struct ifnet *ifp,	/* must not be NULL */
232 		 struct in6_addr *addr)	/* must not be NULL */
233 {
234 	int scope = in6_addrscope(addr);
235 
236 	if (scope6_ids == NULL)	/* paranoid? */
237 		return(0);	/* XXX */
238 	if (ifp->if_index >= if_indexlim)
239 		return(0);	/* XXX */
240 
241 #define SID scope6_ids[ifp->if_index]
242 	switch(scope) {
243 	case IPV6_ADDR_SCOPE_NODELOCAL:
244 		return(-1);	/* XXX: is this an appropriate value? */
245 
246 	case IPV6_ADDR_SCOPE_LINKLOCAL:
247 		return(SID.s6id_list[IPV6_ADDR_SCOPE_LINKLOCAL]);
248 
249 	case IPV6_ADDR_SCOPE_SITELOCAL:
250 		return(SID.s6id_list[IPV6_ADDR_SCOPE_SITELOCAL]);
251 
252 	case IPV6_ADDR_SCOPE_ORGLOCAL:
253 		return(SID.s6id_list[IPV6_ADDR_SCOPE_ORGLOCAL]);
254 
255 	default:
256 		return(0);	/* XXX: treat as global. */
257 	}
258 #undef SID
259 }
260 
261 void
262 scope6_setdefault(struct ifnet *ifp)	/* note that this might be NULL */
263 {
264 	/*
265 	 * Currently, this function just set the default "link" according to
266 	 * the given interface.
267 	 * We might eventually have to separate the notion of "link" from
268 	 * "interface" and provide a user interface to set the default.
269 	 */
270 	if (ifp) {
271 		scope6_ids[0].s6id_list[IPV6_ADDR_SCOPE_LINKLOCAL] =
272 			ifp->if_index;
273 	}
274 	else
275 		scope6_ids[0].s6id_list[IPV6_ADDR_SCOPE_LINKLOCAL] = 0;
276 }
277 
278 int
279 scope6_get_default(u_int32_t *idlist)
280 {
281 	if (scope6_ids == NULL)	/* paranoid? */
282 		return(EINVAL);
283 
284 	bcopy(scope6_ids[0].s6id_list, idlist,
285 	      sizeof(scope6_ids[0].s6id_list));
286 
287 	return(0);
288 }
289 
290 u_int32_t
291 scope6_addr2default(struct in6_addr *addr)
292 {
293 	return(scope6_ids[0].s6id_list[in6_addrscope(addr)]);
294 }
295