xref: /dragonfly/sys/netinet6/scope6.c (revision cb3deea5cd19ac4360b74cda077013653c041709)
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.11 2008/01/05 14:02:40 swildner 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 #include <sys/thread2.h>
41 
42 #include <net/route.h>
43 #include <net/if.h>
44 
45 #include <netinet/in.h>
46 
47 #include <netinet6/in6_var.h>
48 #include <netinet6/scope6_var.h>
49 
50 static struct scope6_id sid_default;
51 
52 static __inline
53 struct scope6_id *
SID(struct ifnet * ifp)54 SID(struct ifnet *ifp)
55 {
56           struct in6_ifextra *xtra;
57 
58           xtra = ifp->if_afdata[AF_INET6];
59           if (xtra)
60                     return (xtra->scope6_id);
61           return(NULL);
62 }
63 
64 void
scope6_init(void)65 scope6_init(void)
66 {
67           bzero(&sid_default, sizeof(sid_default));
68 }
69 
70 struct scope6_id *
scope6_ifattach(struct ifnet * ifp)71 scope6_ifattach(struct ifnet *ifp)
72 {
73           struct scope6_id *sid;
74 
75           sid = (struct scope6_id *)kmalloc(sizeof(*sid), M_IFADDR,
76               M_WAITOK | M_ZERO);
77 
78           crit_enter();
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           crit_exit();
93           return sid;
94 }
95 
96 void
scope6_ifdetach(struct scope6_id * sid)97 scope6_ifdetach(struct scope6_id *sid)
98 {
99           kfree(sid, M_IFADDR);
100 }
101 
102 int
scope6_set(struct ifnet * ifp,struct scope6_id * idlist)103 scope6_set(struct ifnet *ifp, struct scope6_id *idlist)
104 {
105           int i;
106           int error = 0;
107           struct scope6_id *sid = SID(ifp);
108 
109           if (!sid) /* paranoid? */
110                     return (EINVAL);
111 
112           /*
113            * XXX: We need more consistency checks of the relationship among
114            * scopes (e.g. an organization should be larger than a site).
115            */
116 
117           /*
118            * TODO(XXX): after setting, we should reflect the changes to
119            * interface addresses, routing table entries, PCB entries...
120            */
121 
122           crit_enter();
123 
124           for (i = 0; i < 16; i++) {
125                     if (idlist->s6id_list[i] &&
126                         idlist->s6id_list[i] != sid->s6id_list[i]) {
127                               /*
128                                * An interface zone ID must be the corresponding
129                                * interface index by definition.
130                                */
131                               if (i == IPV6_ADDR_SCOPE_INTFACELOCAL &&
132                                   idlist->s6id_list[i] != ifp->if_index) {
133                                         crit_exit();
134                                         return (EINVAL);
135                               }
136 
137                               if (i == IPV6_ADDR_SCOPE_LINKLOCAL &&
138                                   idlist->s6id_list[i] > if_index) {
139                                         /*
140                                          * XXX: theoretically, there should be no
141                                          * relationship between link IDs and interface
142                                          * IDs, but we check the consistency for
143                                          * safety in later use.
144                                          */
145                                         crit_exit();
146                                         return (EINVAL);
147                               }
148 
149                               /*
150                                * XXX: we must need lots of work in this case,
151                                * but we simply set the new value in this initial
152                                * implementation.
153                                */
154                               sid->s6id_list[i] = idlist->s6id_list[i];
155                     }
156           }
157           crit_exit();
158 
159           return (error);
160 }
161 
162 int
scope6_get(struct ifnet * ifp,struct scope6_id * idlist)163 scope6_get(struct ifnet *ifp, struct scope6_id *idlist)
164 {
165           struct scope6_id *sid = SID(ifp);
166 
167           if (sid == NULL)    /* paranoid? */
168                     return (EINVAL);
169 
170           *idlist = *sid;
171 
172           return (0);
173 }
174 
175 
176 /*
177  * Get a scope of the address. Node-local, link-local, site-local or global.
178  */
179 int
in6_addrscope(struct in6_addr * addr)180 in6_addrscope(struct in6_addr *addr)
181 {
182           int scope;
183 
184           if (addr->s6_addr[0] == 0xfe) {
185                     scope = addr->s6_addr[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_addr[0] == 0xff) {
202                     scope = addr->s6_addr[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_INTFACELOCAL:
210                               return IPV6_ADDR_SCOPE_INTFACELOCAL;
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           /*
225            * Regard loopback and unspecified addresses as global, since
226            * they have no ambiguity.
227            */
228           if (bcmp(&kin6addr_loopback, addr, sizeof(*addr) - 1) == 0) {
229                     if (addr->s6_addr[15] == 1) /* loopback */
230                               return IPV6_ADDR_SCOPE_LINKLOCAL;
231                     if (addr->s6_addr[15] == 0) /* unspecified */
232                               return IPV6_ADDR_SCOPE_GLOBAL; /* XXX: correct? */
233           }
234 
235           return IPV6_ADDR_SCOPE_GLOBAL;
236 }
237 
238 /*
239  * When we introduce the "4+28" split semantics in sin6_scope_id,
240  * a 32bit integer is not enough to tell a large ID from an error (-1).
241  * So, we intentionally use a large type as the return value.
242  */
243 int
in6_addr2zoneid(struct ifnet * ifp,struct in6_addr * addr,u_int32_t * ret_id)244 in6_addr2zoneid(struct ifnet *ifp,      /* must not be NULL */
245                     struct in6_addr *addr,        /* must not be NULL */
246                     u_int32_t *ret_id)  /* must not be NULL */
247 {
248           int scope;
249           u_int32_t zoneid = 0;
250           struct scope6_id *sid = SID(ifp);
251 
252 #ifdef DIAGNOSTIC
253           if (sid == NULL) { /* should not happen */
254                     panic("in6_addr2zoneid: scope array is NULL");
255                     /* NOTREACHED */
256           }
257           if (ret_id == NULL) {
258                     panic("in6_addr2zoneid: return ID is null");
259                     /* NOTREACHED */
260           }
261 #endif
262 
263           /*
264            * special case: the loopback address can only belong to a loopback
265            * interface.
266            */
267           if (IN6_IS_ADDR_LOOPBACK(addr)) {
268                     if (!(ifp->if_flags & IFF_LOOPBACK)) {
269                               return (-1);
270                     } else {
271                               *ret_id = 0; /* there's no ambiguity */
272                               return (0);
273                     }
274           }
275 
276           scope = in6_addrscope(addr);
277 
278           switch (scope) {
279           case IPV6_ADDR_SCOPE_INTFACELOCAL: /* should be interface index */
280                     zoneid = sid->s6id_list[IPV6_ADDR_SCOPE_INTFACELOCAL];
281                     break;
282 
283           case IPV6_ADDR_SCOPE_LINKLOCAL:
284                     zoneid = sid->s6id_list[IPV6_ADDR_SCOPE_LINKLOCAL];
285                     break;
286 
287           case IPV6_ADDR_SCOPE_SITELOCAL:
288                     zoneid = sid->s6id_list[IPV6_ADDR_SCOPE_SITELOCAL];
289                     break;
290 
291           case IPV6_ADDR_SCOPE_ORGLOCAL:
292                     zoneid = sid->s6id_list[IPV6_ADDR_SCOPE_ORGLOCAL];
293                     break;
294 
295           default:
296                     zoneid = 0;     /* XXX: treat as global. */
297                     break;
298           }
299           *ret_id = zoneid;
300           return (0);
301 }
302 
303 void
scope6_setdefault(struct ifnet * ifp)304 scope6_setdefault(struct ifnet *ifp)    /* note that this might be NULL */
305 {
306           /*
307            * Currently, this function just set the default "interfaces"
308            * and "links" according to the given interface.
309            * We might eventually have to separate the notion of "link" from
310            * "interface" and provide a user interface to set the default.
311            */
312           if (ifp) {
313                     sid_default.s6id_list[IPV6_ADDR_SCOPE_INTFACELOCAL] =
314                               ifp->if_index;
315                     sid_default.s6id_list[IPV6_ADDR_SCOPE_LINKLOCAL] =
316                               ifp->if_index;
317           } else {
318                     sid_default.s6id_list[IPV6_ADDR_SCOPE_INTFACELOCAL] = 0;
319                     sid_default.s6id_list[IPV6_ADDR_SCOPE_LINKLOCAL] = 0;
320           }
321 }
322 
323 int
scope6_get_default(struct scope6_id * idlist)324 scope6_get_default(struct scope6_id *idlist)
325 {
326           *idlist = sid_default;
327 
328           return (0);
329 }
330 
331 u_int32_t
scope6_addr2default(struct in6_addr * addr)332 scope6_addr2default(struct in6_addr *addr)
333 {
334           /*
335            * special case: The loopback address should be considered as
336            * link-local, but there's no ambiguity in the syntax.
337            */
338           if (IN6_IS_ADDR_LOOPBACK(addr))
339                     return (0);
340 
341           return (sid_default.s6id_list[in6_addrscope(addr)]);
342 }
343 
344 /*
345  * Determine the appropriate scope zone ID for in6 and ifp.  If ret_id is
346  * non NULL, it is set to the zone ID.  If the zone ID needs to be embedded
347   * in the in6_addr structure, in6 will be modified.
348  */
349 int
in6_setscope(struct in6_addr * in6,struct ifnet * ifp,u_int32_t * ret_id)350 in6_setscope(struct in6_addr *in6, struct ifnet *ifp, u_int32_t *ret_id)
351 {
352           int scope;
353           u_int32_t zoneid = 0;
354           struct scope6_id *sid;
355 
356           ifnet_serialize_all(ifp);
357 
358           sid = SID(ifp);
359 
360 #ifdef DIAGNOSTIC
361           if (sid == NULL) { /* should not happen */
362                     panic("in6_setscope: scope array is NULL");
363                     /* NOTREACHED */
364           }
365 #endif
366 
367           /*
368            * special case: the loopback address can only belong to a loopback
369            * interface.
370            */
371           if (IN6_IS_ADDR_LOOPBACK(in6)) {
372                     if (!(ifp->if_flags & IFF_LOOPBACK)) {
373                               ifnet_deserialize_all(ifp);
374                               return (EINVAL);
375                     } else {
376                               if (ret_id != NULL)
377                                         *ret_id = 0; /* there's no ambiguity */
378                               ifnet_deserialize_all(ifp);
379                               return (0);
380                     }
381           }
382 
383           scope = in6_addrscope(in6);
384 
385           switch (scope) {
386           case IPV6_ADDR_SCOPE_NODELOCAL: /* should be interface index */
387                     zoneid = sid->s6id_list[IPV6_ADDR_SCOPE_NODELOCAL];
388                     break;
389 
390           case IPV6_ADDR_SCOPE_LINKLOCAL:
391                     zoneid = sid->s6id_list[IPV6_ADDR_SCOPE_LINKLOCAL];
392                     break;
393 
394           case IPV6_ADDR_SCOPE_SITELOCAL:
395                     zoneid = sid->s6id_list[IPV6_ADDR_SCOPE_SITELOCAL];
396                     break;
397 
398           case IPV6_ADDR_SCOPE_ORGLOCAL:
399                     zoneid = sid->s6id_list[IPV6_ADDR_SCOPE_ORGLOCAL];
400                     break;
401 
402           default:
403                     zoneid = 0;     /* XXX: treat as global. */
404                     break;
405           }
406 
407           ifnet_deserialize_all(ifp);
408 
409           if (ret_id != NULL)
410                     *ret_id = zoneid;
411 
412           if (IN6_IS_SCOPE_LINKLOCAL(in6) || IN6_IS_ADDR_MC_NODELOCAL(in6) )
413                     in6->s6_addr16[1] = htons(zoneid & 0xffff); /* XXX */
414 
415           return (0);
416 }
417