inet6: emit RTM_NEWADDR messages on address flag changes.
[dragonfly.git] / sys / netinet6 / scope6.c
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 *
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
65 scope6_init(void)
66 {
67         bzero(&sid_default, sizeof(sid_default));
68 }
69
70 struct scope6_id *
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
97 scope6_ifdetach(struct scope6_id *sid)
98 {
99         kfree(sid, M_IFADDR);
100 }
101
102 int
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
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
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
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
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
324 scope6_get_default(struct scope6_id *idlist)
325 {
326         *idlist = sid_default;
327
328         return (0);
329 }
330
331 u_int32_t
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
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 }