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_NODELOCAL] = 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 if (i == IPV6_ADDR_SCOPE_LINKLOCAL && 128 idlist->s6id_list[i] > if_index) { 129 /* 130 * XXX: theoretically, there should be no 131 * relationship between link IDs and interface 132 * IDs, but we check the consistency for 133 * safety in later use. 134 */ 135 crit_exit(); 136 return (EINVAL); 137 } 138 139 /* 140 * XXX: we must need lots of work in this case, 141 * but we simply set the new value in this initial 142 * implementation. 143 */ 144 sid->s6id_list[i] = idlist->s6id_list[i]; 145 } 146 } 147 crit_exit(); 148 149 return (error); 150 } 151 152 int 153 scope6_get(struct ifnet *ifp, struct scope6_id *idlist) 154 { 155 struct scope6_id *sid = SID(ifp); 156 157 if (sid == NULL) /* paranoid? */ 158 return (EINVAL); 159 160 *idlist = *sid; 161 162 return (0); 163 } 164 165 166 /* 167 * Get a scope of the address. Node-local, link-local, site-local or global. 168 */ 169 int 170 in6_addrscope(struct in6_addr *addr) 171 { 172 int scope; 173 174 if (addr->s6_addr8[0] == 0xfe) { 175 scope = addr->s6_addr8[1] & 0xc0; 176 177 switch (scope) { 178 case 0x80: 179 return IPV6_ADDR_SCOPE_LINKLOCAL; 180 break; 181 case 0xc0: 182 return IPV6_ADDR_SCOPE_SITELOCAL; 183 break; 184 default: 185 return IPV6_ADDR_SCOPE_GLOBAL; /* just in case */ 186 break; 187 } 188 } 189 190 191 if (addr->s6_addr8[0] == 0xff) { 192 scope = addr->s6_addr8[1] & 0x0f; 193 194 /* 195 * due to other scope such as reserved, 196 * return scope doesn't work. 197 */ 198 switch (scope) { 199 case IPV6_ADDR_SCOPE_NODELOCAL: 200 return IPV6_ADDR_SCOPE_NODELOCAL; 201 break; 202 case IPV6_ADDR_SCOPE_LINKLOCAL: 203 return IPV6_ADDR_SCOPE_LINKLOCAL; 204 break; 205 case IPV6_ADDR_SCOPE_SITELOCAL: 206 return IPV6_ADDR_SCOPE_SITELOCAL; 207 break; 208 default: 209 return IPV6_ADDR_SCOPE_GLOBAL; 210 break; 211 } 212 } 213 214 if (bcmp(&kin6addr_loopback, addr, sizeof(*addr) - 1) == 0) { 215 if (addr->s6_addr8[15] == 1) /* loopback */ 216 return IPV6_ADDR_SCOPE_NODELOCAL; 217 if (addr->s6_addr8[15] == 0) /* unspecified */ 218 return IPV6_ADDR_SCOPE_LINKLOCAL; 219 } 220 221 return IPV6_ADDR_SCOPE_GLOBAL; 222 } 223 224 int 225 in6_addr2scopeid(struct ifnet *ifp, /* must not be NULL */ 226 struct in6_addr *addr) /* must not be NULL */ 227 { 228 int scope; 229 struct scope6_id *sid = SID(ifp); 230 231 #ifdef DIAGNOSTIC 232 if (sid == NULL) { /* should not happen */ 233 panic("in6_addr2zoneid: scope array is NULL"); 234 /* NOTREACHED */ 235 } 236 #endif 237 238 /* 239 * special case: the loopback address can only belong to a loopback 240 * interface. 241 */ 242 if (IN6_IS_ADDR_LOOPBACK(addr)) { 243 if (!(ifp->if_flags & IFF_LOOPBACK)) 244 return (-1); 245 else 246 return (0); /* there's no ambiguity */ 247 } 248 249 scope = in6_addrscope(addr); 250 251 switch(scope) { 252 case IPV6_ADDR_SCOPE_NODELOCAL: 253 return (-1); /* XXX: is this an appropriate value? */ 254 255 case IPV6_ADDR_SCOPE_LINKLOCAL: 256 return (sid->s6id_list[IPV6_ADDR_SCOPE_LINKLOCAL]); 257 258 case IPV6_ADDR_SCOPE_SITELOCAL: 259 return (sid->s6id_list[IPV6_ADDR_SCOPE_SITELOCAL]); 260 261 case IPV6_ADDR_SCOPE_ORGLOCAL: 262 return (sid->s6id_list[IPV6_ADDR_SCOPE_ORGLOCAL]); 263 264 default: 265 return (0); /* XXX: treat as global. */ 266 } 267 } 268 269 void 270 scope6_setdefault(struct ifnet *ifp) /* note that this might be NULL */ 271 { 272 /* 273 * Currently, this function just set the default "interfaces" 274 * and "links" according to the given interface. 275 * We might eventually have to separate the notion of "link" from 276 * "interface" and provide a user interface to set the default. 277 */ 278 if (ifp) { 279 sid_default.s6id_list[IPV6_ADDR_SCOPE_NODELOCAL] = 280 ifp->if_index; 281 sid_default.s6id_list[IPV6_ADDR_SCOPE_LINKLOCAL] = 282 ifp->if_index; 283 } else { 284 sid_default.s6id_list[IPV6_ADDR_SCOPE_NODELOCAL] = 0; 285 sid_default.s6id_list[IPV6_ADDR_SCOPE_LINKLOCAL] = 0; 286 } 287 } 288 289 int 290 scope6_get_default(struct scope6_id *idlist) 291 { 292 *idlist = sid_default; 293 294 return (0); 295 } 296 297 u_int32_t 298 scope6_addr2default(struct in6_addr *addr) 299 { 300 /* 301 * special case: The loopback address should be considered as 302 * link-local, but there's no ambiguity in the syntax. 303 */ 304 if (IN6_IS_ADDR_LOOPBACK(addr)) 305 return (0); 306 307 return (sid_default.s6id_list[in6_addrscope(addr)]); 308 } 309 310 /* 311 * Determine the appropriate scope zone ID for in6 and ifp. If ret_id is 312 * non NULL, it is set to the zone ID. If the zone ID needs to be embedded 313 * in the in6_addr structure, in6 will be modified. 314 */ 315 int 316 in6_setscope(struct in6_addr *in6, struct ifnet *ifp, u_int32_t *ret_id) 317 { 318 int scope; 319 u_int32_t zoneid = 0; 320 struct scope6_id *sid; 321 322 ifnet_serialize_all(ifp); 323 324 sid = SID(ifp); 325 326 #ifdef DIAGNOSTIC 327 if (sid == NULL) { /* should not happen */ 328 panic("in6_setscope: scope array is NULL"); 329 /* NOTREACHED */ 330 } 331 #endif 332 333 /* 334 * special case: the loopback address can only belong to a loopback 335 * interface. 336 */ 337 if (IN6_IS_ADDR_LOOPBACK(in6)) { 338 if (!(ifp->if_flags & IFF_LOOPBACK)) { 339 ifnet_deserialize_all(ifp); 340 return (EINVAL); 341 } else { 342 if (ret_id != NULL) 343 *ret_id = 0; /* there's no ambiguity */ 344 ifnet_deserialize_all(ifp); 345 return (0); 346 } 347 } 348 349 scope = in6_addrscope(in6); 350 351 switch (scope) { 352 case IPV6_ADDR_SCOPE_NODELOCAL: /* should be interface index */ 353 zoneid = sid->s6id_list[IPV6_ADDR_SCOPE_NODELOCAL]; 354 break; 355 356 case IPV6_ADDR_SCOPE_LINKLOCAL: 357 zoneid = sid->s6id_list[IPV6_ADDR_SCOPE_LINKLOCAL]; 358 break; 359 360 case IPV6_ADDR_SCOPE_SITELOCAL: 361 zoneid = sid->s6id_list[IPV6_ADDR_SCOPE_SITELOCAL]; 362 break; 363 364 case IPV6_ADDR_SCOPE_ORGLOCAL: 365 zoneid = sid->s6id_list[IPV6_ADDR_SCOPE_ORGLOCAL]; 366 break; 367 368 default: 369 zoneid = 0; /* XXX: treat as global. */ 370 break; 371 } 372 373 ifnet_deserialize_all(ifp); 374 375 if (ret_id != NULL) 376 *ret_id = zoneid; 377 378 if (IN6_IS_SCOPE_LINKLOCAL(in6) || IN6_IS_ADDR_MC_NODELOCAL(in6) ) 379 in6->s6_addr16[1] = htons(zoneid & 0xffff); /* XXX */ 380 381 return (0); 382 } 383