xref: /dragonfly/sys/netinet6/scope6.c (revision f9a239ec)
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 }
297