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 } 417