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