Merge from vendor branch HEIMDAL:
[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.4 2005/02/01 16:09:37 hrs 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 static struct scope6_id sid_default;
50 #define SID(ifp) \
51         (((struct in6_ifextra *)(ifp)->if_afdata[AF_INET6])->scope6_id)
52
53 void
54 scope6_init()
55 {
56         bzero(&sid_default, sizeof(sid_default));
57 }
58
59 struct scope6_id *
60 scope6_ifattach(struct ifnet *ifp)
61 {
62         int s = splnet();
63         struct scope6_id *sid;
64
65         sid = (struct scope6_id *)malloc(sizeof(*sid), M_IFADDR, M_WAITOK);
66         bzero(sid, sizeof(*sid));
67
68         /*
69          * XXX: IPV6_ADDR_SCOPE_xxx macros are not standard.
70          * Should we rather hardcode here?
71          */
72         sid->s6id_list[IPV6_ADDR_SCOPE_NODELOCAL] = ifp->if_index;
73         sid->s6id_list[IPV6_ADDR_SCOPE_LINKLOCAL] = ifp->if_index;
74 #ifdef MULTI_SCOPE
75         /* by default, we don't care about scope boundary for these scopes. */
76         sid->s6id_list[IPV6_ADDR_SCOPE_SITELOCAL] = 1;
77         sid->s6id_list[IPV6_ADDR_SCOPE_ORGLOCAL] = 1;
78 #endif
79
80         splx(s);
81         return sid;
82 }
83
84 void
85 scope6_ifdetach(struct scope6_id *sid)
86 {
87         free(sid, M_IFADDR);
88 }
89
90 int
91 scope6_set(struct ifnet *ifp, struct scope6_id *idlist)
92 {
93         int i, s;
94         int error = 0;
95         struct scope6_id *sid = SID(ifp);
96
97         if (!sid)       /* paranoid? */
98                 return(EINVAL);
99
100         /*
101          * XXX: We need more consistency checks of the relationship among
102          * scopes (e.g. an organization should be larger than a site).
103          */
104
105         /*
106          * TODO(XXX): after setting, we should reflect the changes to
107          * interface addresses, routing table entries, PCB entries... 
108          */
109
110         s = splnet();
111
112         for (i = 0; i < 16; i++) {
113                 if (idlist->s6id_list[i] &&
114                     idlist->s6id_list[i] != sid->s6id_list[i]) {
115                         if (i == IPV6_ADDR_SCOPE_LINKLOCAL &&
116                             idlist->s6id_list[i] > if_index) {
117                                 /*
118                                  * XXX: theoretically, there should be no
119                                  * relationship between link IDs and interface
120                                  * IDs, but we check the consistency for
121                                  * safety in later use.
122                                  */
123                                 splx(s);
124                                 return(EINVAL);
125                         }
126
127                         /*
128                          * XXX: we must need lots of work in this case,
129                          * but we simply set the new value in this initial
130                          * implementation.
131                          */
132                         sid->s6id_list[i] = idlist->s6id_list[i];
133                 }
134         }
135         splx(s);
136
137         return(error);
138 }
139
140 int
141 scope6_get(struct ifnet *ifp, struct scope6_id *idlist)
142 {
143         struct scope6_id *sid = SID(ifp);
144
145         if (sid == NULL)        /* paranoid? */
146                 return(EINVAL);
147
148         *idlist = *sid;
149
150         return(0);
151 }
152
153
154 /*
155  * Get a scope of the address. Node-local, link-local, site-local or global.
156  */
157 int
158 in6_addrscope(struct in6_addr *addr)
159 {
160         int scope;
161
162         if (addr->s6_addr8[0] == 0xfe) {
163                 scope = addr->s6_addr8[1] & 0xc0;
164
165                 switch (scope) {
166                 case 0x80:
167                         return IPV6_ADDR_SCOPE_LINKLOCAL;
168                         break;
169                 case 0xc0:
170                         return IPV6_ADDR_SCOPE_SITELOCAL;
171                         break;
172                 default:
173                         return IPV6_ADDR_SCOPE_GLOBAL; /* just in case */
174                         break;
175                 }
176         }
177
178
179         if (addr->s6_addr8[0] == 0xff) {
180                 scope = addr->s6_addr8[1] & 0x0f;
181
182                 /*
183                  * due to other scope such as reserved,
184                  * return scope doesn't work.
185                  */
186                 switch (scope) {
187                 case IPV6_ADDR_SCOPE_NODELOCAL:
188                         return IPV6_ADDR_SCOPE_NODELOCAL;
189                         break;
190                 case IPV6_ADDR_SCOPE_LINKLOCAL:
191                         return IPV6_ADDR_SCOPE_LINKLOCAL;
192                         break;
193                 case IPV6_ADDR_SCOPE_SITELOCAL:
194                         return IPV6_ADDR_SCOPE_SITELOCAL;
195                         break;
196                 default:
197                         return IPV6_ADDR_SCOPE_GLOBAL;
198                         break;
199                 }
200         }
201
202         if (bcmp(&in6addr_loopback, addr, sizeof(*addr) - 1) == 0) {
203                 if (addr->s6_addr8[15] == 1) /* loopback */
204                         return IPV6_ADDR_SCOPE_NODELOCAL;
205                 if (addr->s6_addr8[15] == 0) /* unspecified */
206                         return IPV6_ADDR_SCOPE_LINKLOCAL;
207         }
208
209         return IPV6_ADDR_SCOPE_GLOBAL;
210 }
211
212 int
213 in6_addr2scopeid(struct ifnet *ifp,     /* must not be NULL */
214                  struct in6_addr *addr) /* must not be NULL */
215 {
216         int scope;
217         struct scope6_id *sid = SID(ifp);
218
219 #ifdef DIAGNOSTIC
220         if (sid == NULL) { /* should not happen */
221                 panic("in6_addr2zoneid: scope array is NULL");
222                 /* NOTREACHED */
223         }
224 #endif
225
226         /*
227          * special case: the loopback address can only belong to a loopback
228          * interface.
229          */
230         if (IN6_IS_ADDR_LOOPBACK(addr)) {
231                 if (!(ifp->if_flags & IFF_LOOPBACK))
232                         return (-1);
233                 else
234                         return (0); /* there's no ambiguity */
235         }
236
237         scope = in6_addrscope(addr);
238
239         switch(scope) {
240         case IPV6_ADDR_SCOPE_NODELOCAL:
241                 return(-1);     /* XXX: is this an appropriate value? */
242
243         case IPV6_ADDR_SCOPE_LINKLOCAL:
244                 return (sid->s6id_list[IPV6_ADDR_SCOPE_LINKLOCAL]);
245
246         case IPV6_ADDR_SCOPE_SITELOCAL:
247                 return (sid->s6id_list[IPV6_ADDR_SCOPE_SITELOCAL]);
248
249         case IPV6_ADDR_SCOPE_ORGLOCAL:
250                 return (sid->s6id_list[IPV6_ADDR_SCOPE_ORGLOCAL]);
251
252         default:
253                 return(0);      /* XXX: treat as global. */
254         }
255 }
256
257 void
258 scope6_setdefault(struct ifnet *ifp)    /* note that this might be NULL */
259 {
260         /*
261          * Currently, this function just set the default "interfaces"
262          * and "links" according to the given interface.
263          * We might eventually have to separate the notion of "link" from
264          * "interface" and provide a user interface to set the default.
265          */
266         if (ifp) {
267                 sid_default.s6id_list[IPV6_ADDR_SCOPE_NODELOCAL] =
268                         ifp->if_index;
269                 sid_default.s6id_list[IPV6_ADDR_SCOPE_LINKLOCAL] =
270                         ifp->if_index;
271         } else {
272                 sid_default.s6id_list[IPV6_ADDR_SCOPE_NODELOCAL] = 0;
273                 sid_default.s6id_list[IPV6_ADDR_SCOPE_LINKLOCAL] = 0;
274         }
275 }
276
277 int
278 scope6_get_default(struct scope6_id *idlist)
279 {
280         *idlist = sid_default;
281
282         return(0);
283 }
284
285 u_int32_t
286 scope6_addr2default(struct in6_addr *addr)
287 {
288         /*
289          * special case: The loopback address should be considered as
290          * link-local, but there's no ambiguity in the syntax.
291          */
292         if (IN6_IS_ADDR_LOOPBACK(addr))
293                 return (0);
294
295         return (sid_default.s6id_list[in6_addrscope(addr)]);
296 }