xref: /freebsd/sys/netinet6/scope6.c (revision 77a0943ded95b9e6438f7db70c4a28e4d93946d4)
1 /*	$FreeBSD$	*/
2 /*	$KAME: scope6.c,v 1.9 2000/05/18 15:03:26 jinmei 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 
39 #include <net/route.h>
40 #include <net/if.h>
41 
42 #include <netinet/in.h>
43 
44 #include <netinet6/in6_var.h>
45 #include <netinet6/scope6_var.h>
46 
47 struct scope6_id {
48 	/*
49 	 * 16 is correspondent to 4bit multicast scope field.
50 	 * i.e. from node-local to global with some reserved/unassigned types.
51 	 */
52 	u_int32_t s6id_list[16];
53 };
54 static size_t if_indexlim = 8;
55 struct scope6_id *scope6_ids = NULL;
56 
57 void
58 scope6_ifattach(ifp)
59 	struct ifnet *ifp;
60 {
61 	int s = splnet();
62 
63 	/*
64 	 * We have some arrays that should be indexed by if_index.
65 	 * since if_index will grow dynamically, they should grow too.
66 	 */
67 	if (scope6_ids == NULL || if_index >= if_indexlim) {
68 		size_t n;
69 		caddr_t q;
70 
71 		while (if_index >= if_indexlim)
72 			if_indexlim <<= 1;
73 
74 		/* grow scope index array */
75 		n = if_indexlim * sizeof(struct scope6_id);
76 		/* XXX: need new malloc type? */
77 		q = (caddr_t)malloc(n, M_IFADDR, M_WAITOK);
78 		bzero(q, n);
79 		if (scope6_ids) {
80 			bcopy((caddr_t)scope6_ids, q, n/2);
81 			free((caddr_t)scope6_ids, M_IFADDR);
82 		}
83 		scope6_ids = (struct scope6_id *)q;
84 	}
85 
86 #define SID scope6_ids[ifp->if_index]
87 
88 	/* don't initialize if called twice */
89 	if (SID.s6id_list[IPV6_ADDR_SCOPE_LINKLOCAL]) {
90 		splx(s);
91 		return;
92 	}
93 
94 	/*
95 	 * XXX: IPV6_ADDR_SCOPE_xxx macros are not standard.
96 	 * Should we rather hardcode here?
97 	 */
98 	SID.s6id_list[IPV6_ADDR_SCOPE_LINKLOCAL] = ifp->if_index;
99 #ifdef MULTI_SCOPE
100 	/* by default, we don't care about scope boundary for these scopes. */
101 	SID.s6id_list[IPV6_ADDR_SCOPE_SITELOCAL] = 1;
102 	SID.s6id_list[IPV6_ADDR_SCOPE_ORGLOCAL] = 1;
103 #endif
104 #undef SID
105 
106 	splx(s);
107 }
108 
109 int
110 scope6_set(ifp, idlist)
111 	struct ifnet *ifp;
112 	u_int32_t *idlist;
113 {
114 	int i, s;
115 	int error = 0;
116 
117 	if (scope6_ids == NULL)	/* paranoid? */
118 		return(EINVAL);
119 
120 	/*
121 	 * XXX: We need more consistency checks of the relationship among
122 	 * scopes (e.g. an organization should be larger than a site).
123 	 */
124 
125 	/*
126 	 * TODO(XXX): after setting, we should reflect the changes to
127 	 * interface addresses, routing table entries, PCB entries...
128 	 */
129 
130 	s = splnet();
131 
132 	for (i = 0; i < 16; i++) {
133 		if (idlist[i] &&
134 		    idlist[i] != scope6_ids[ifp->if_index].s6id_list[i]) {
135 			if (i == IPV6_ADDR_SCOPE_LINKLOCAL &&
136 			    idlist[i] > if_index) {
137 				/*
138 				 * XXX: theoretically, there should be no
139 				 * relationship between link IDs and interface
140 				 * IDs, but we check the consistency for
141 				 * safety in later use.
142 				 */
143 				splx(s);
144 				return(EINVAL);
145 			}
146 
147 			/*
148 			 * XXX: we must need lots of work in this case,
149 			 * but we simply set the new value in this initial
150 			 * implementation.
151 			 */
152 			scope6_ids[ifp->if_index].s6id_list[i] = idlist[i];
153 		}
154 	}
155 	splx(s);
156 
157 	return(error);
158 }
159 
160 int
161 scope6_get(ifp, idlist)
162 	struct ifnet *ifp;
163 	u_int32_t *idlist;
164 {
165 	if (scope6_ids == NULL)	/* paranoid? */
166 		return(EINVAL);
167 
168 	bcopy(scope6_ids[ifp->if_index].s6id_list, idlist,
169 	      sizeof(scope6_ids[ifp->if_index].s6id_list));
170 
171 	return(0);
172 }
173 
174 
175 /*
176  * Get a scope of the address. Node-local, link-local, site-local or global.
177  */
178 int
179 in6_addrscope(addr)
180 struct in6_addr *addr;
181 {
182 	int scope;
183 
184 	if (addr->s6_addr8[0] == 0xfe) {
185 		scope = addr->s6_addr8[1] & 0xc0;
186 
187 		switch (scope) {
188 		case 0x80:
189 			return IPV6_ADDR_SCOPE_LINKLOCAL;
190 			break;
191 		case 0xc0:
192 			return IPV6_ADDR_SCOPE_SITELOCAL;
193 			break;
194 		default:
195 			return IPV6_ADDR_SCOPE_GLOBAL; /* just in case */
196 			break;
197 		}
198 	}
199 
200 
201 	if (addr->s6_addr8[0] == 0xff) {
202 		scope = addr->s6_addr8[1] & 0x0f;
203 
204 		/*
205 		 * due to other scope such as reserved,
206 		 * return scope doesn't work.
207 		 */
208 		switch (scope) {
209 		case IPV6_ADDR_SCOPE_NODELOCAL:
210 			return IPV6_ADDR_SCOPE_NODELOCAL;
211 			break;
212 		case IPV6_ADDR_SCOPE_LINKLOCAL:
213 			return IPV6_ADDR_SCOPE_LINKLOCAL;
214 			break;
215 		case IPV6_ADDR_SCOPE_SITELOCAL:
216 			return IPV6_ADDR_SCOPE_SITELOCAL;
217 			break;
218 		default:
219 			return IPV6_ADDR_SCOPE_GLOBAL;
220 			break;
221 		}
222 	}
223 
224 	if (bcmp(&in6addr_loopback, addr, sizeof(addr) - 1) == 0) {
225 		if (addr->s6_addr8[15] == 1) /* loopback */
226 			return IPV6_ADDR_SCOPE_NODELOCAL;
227 		if (addr->s6_addr8[15] == 0) /* unspecified */
228 			return IPV6_ADDR_SCOPE_LINKLOCAL;
229 	}
230 
231 	return IPV6_ADDR_SCOPE_GLOBAL;
232 }
233 
234 int
235 in6_addr2scopeid(ifp, addr)
236 	struct ifnet *ifp;	/* must not be NULL */
237 	struct in6_addr *addr;	/* must not be NULL */
238 {
239 	int scope = in6_addrscope(addr);
240 
241 	if (scope6_ids == NULL)	/* paranoid? */
242 		return(0);	/* XXX */
243 	if (ifp->if_index >= if_indexlim)
244 		return(0);	/* XXX */
245 
246 #define SID scope6_ids[ifp->if_index]
247 	switch(scope) {
248 	case IPV6_ADDR_SCOPE_NODELOCAL:
249 		return(-1);	/* XXX: is this an appropriate value? */
250 
251 	case IPV6_ADDR_SCOPE_LINKLOCAL:
252 		return(SID.s6id_list[IPV6_ADDR_SCOPE_LINKLOCAL]);
253 
254 	case IPV6_ADDR_SCOPE_SITELOCAL:
255 		return(SID.s6id_list[IPV6_ADDR_SCOPE_SITELOCAL]);
256 
257 	case IPV6_ADDR_SCOPE_ORGLOCAL:
258 		return(SID.s6id_list[IPV6_ADDR_SCOPE_ORGLOCAL]);
259 
260 	default:
261 		return(0);	/* XXX: treat as global. */
262 	}
263 #undef SID
264 }
265 
266 void
267 scope6_setdefault(ifp)
268 	struct ifnet *ifp;	/* note that this might be NULL */
269 {
270 	/*
271 	 * Currently, this function just set the default "link" according to
272 	 * the given interface.
273 	 * We might eventually have to separate the notion of "link" from
274 	 * "interface" and provide a user interface to set the default.
275 	 */
276 	if (ifp) {
277 		scope6_ids[0].s6id_list[IPV6_ADDR_SCOPE_LINKLOCAL] =
278 			ifp->if_index;
279 	}
280 	else
281 		scope6_ids[0].s6id_list[IPV6_ADDR_SCOPE_LINKLOCAL] = 0;
282 }
283 
284 int
285 scope6_get_default(idlist)
286 	u_int32_t *idlist;
287 {
288 	if (scope6_ids == NULL)	/* paranoid? */
289 		return(EINVAL);
290 
291 	bcopy(scope6_ids[0].s6id_list, idlist,
292 	      sizeof(scope6_ids[0].s6id_list));
293 
294 	return(0);
295 }
296 
297 u_int32_t
298 scope6_addr2default(addr)
299 	struct in6_addr *addr;
300 {
301 	return(scope6_ids[0].s6id_list[in6_addrscope(addr)]);
302 }
303