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.2 2003/06/17 04:28:53 dillon 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 struct scope6_id { 50 /* 51 * 16 is correspondent to 4bit multicast scope field. 52 * i.e. from node-local to global with some reserved/unassigned types. 53 */ 54 u_int32_t s6id_list[16]; 55 }; 56 static size_t if_indexlim = 8; 57 struct scope6_id *scope6_ids = NULL; 58 59 void 60 scope6_ifattach(ifp) 61 struct ifnet *ifp; 62 { 63 int s = splnet(); 64 65 /* 66 * We have some arrays that should be indexed by if_index. 67 * since if_index will grow dynamically, they should grow too. 68 */ 69 if (scope6_ids == NULL || if_index >= if_indexlim) { 70 size_t n; 71 caddr_t q; 72 73 while (if_index >= if_indexlim) 74 if_indexlim <<= 1; 75 76 /* grow scope index array */ 77 n = if_indexlim * sizeof(struct scope6_id); 78 /* XXX: need new malloc type? */ 79 q = (caddr_t)malloc(n, M_IFADDR, M_WAITOK); 80 bzero(q, n); 81 if (scope6_ids) { 82 bcopy((caddr_t)scope6_ids, q, n/2); 83 free((caddr_t)scope6_ids, M_IFADDR); 84 } 85 scope6_ids = (struct scope6_id *)q; 86 } 87 88 #define SID scope6_ids[ifp->if_index] 89 90 /* don't initialize if called twice */ 91 if (SID.s6id_list[IPV6_ADDR_SCOPE_LINKLOCAL]) { 92 splx(s); 93 return; 94 } 95 96 /* 97 * XXX: IPV6_ADDR_SCOPE_xxx macros are not standard. 98 * Should we rather hardcode here? 99 */ 100 SID.s6id_list[IPV6_ADDR_SCOPE_LINKLOCAL] = ifp->if_index; 101 #ifdef MULTI_SCOPE 102 /* by default, we don't care about scope boundary for these scopes. */ 103 SID.s6id_list[IPV6_ADDR_SCOPE_SITELOCAL] = 1; 104 SID.s6id_list[IPV6_ADDR_SCOPE_ORGLOCAL] = 1; 105 #endif 106 #undef SID 107 108 splx(s); 109 } 110 111 int 112 scope6_set(ifp, idlist) 113 struct ifnet *ifp; 114 u_int32_t *idlist; 115 { 116 int i, s; 117 int error = 0; 118 119 if (scope6_ids == NULL) /* paranoid? */ 120 return(EINVAL); 121 122 /* 123 * XXX: We need more consistency checks of the relationship among 124 * scopes (e.g. an organization should be larger than a site). 125 */ 126 127 /* 128 * TODO(XXX): after setting, we should reflect the changes to 129 * interface addresses, routing table entries, PCB entries... 130 */ 131 132 s = splnet(); 133 134 for (i = 0; i < 16; i++) { 135 if (idlist[i] && 136 idlist[i] != scope6_ids[ifp->if_index].s6id_list[i]) { 137 if (i == IPV6_ADDR_SCOPE_LINKLOCAL && 138 idlist[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 splx(s); 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 scope6_ids[ifp->if_index].s6id_list[i] = idlist[i]; 155 } 156 } 157 splx(s); 158 159 return(error); 160 } 161 162 int 163 scope6_get(ifp, idlist) 164 struct ifnet *ifp; 165 u_int32_t *idlist; 166 { 167 if (scope6_ids == NULL) /* paranoid? */ 168 return(EINVAL); 169 170 bcopy(scope6_ids[ifp->if_index].s6id_list, idlist, 171 sizeof(scope6_ids[ifp->if_index].s6id_list)); 172 173 return(0); 174 } 175 176 177 /* 178 * Get a scope of the address. Node-local, link-local, site-local or global. 179 */ 180 int 181 in6_addrscope(addr) 182 struct in6_addr *addr; 183 { 184 int scope; 185 186 if (addr->s6_addr8[0] == 0xfe) { 187 scope = addr->s6_addr8[1] & 0xc0; 188 189 switch (scope) { 190 case 0x80: 191 return IPV6_ADDR_SCOPE_LINKLOCAL; 192 break; 193 case 0xc0: 194 return IPV6_ADDR_SCOPE_SITELOCAL; 195 break; 196 default: 197 return IPV6_ADDR_SCOPE_GLOBAL; /* just in case */ 198 break; 199 } 200 } 201 202 203 if (addr->s6_addr8[0] == 0xff) { 204 scope = addr->s6_addr8[1] & 0x0f; 205 206 /* 207 * due to other scope such as reserved, 208 * return scope doesn't work. 209 */ 210 switch (scope) { 211 case IPV6_ADDR_SCOPE_NODELOCAL: 212 return IPV6_ADDR_SCOPE_NODELOCAL; 213 break; 214 case IPV6_ADDR_SCOPE_LINKLOCAL: 215 return IPV6_ADDR_SCOPE_LINKLOCAL; 216 break; 217 case IPV6_ADDR_SCOPE_SITELOCAL: 218 return IPV6_ADDR_SCOPE_SITELOCAL; 219 break; 220 default: 221 return IPV6_ADDR_SCOPE_GLOBAL; 222 break; 223 } 224 } 225 226 if (bcmp(&in6addr_loopback, addr, sizeof(*addr) - 1) == 0) { 227 if (addr->s6_addr8[15] == 1) /* loopback */ 228 return IPV6_ADDR_SCOPE_NODELOCAL; 229 if (addr->s6_addr8[15] == 0) /* unspecified */ 230 return IPV6_ADDR_SCOPE_LINKLOCAL; 231 } 232 233 return IPV6_ADDR_SCOPE_GLOBAL; 234 } 235 236 int 237 in6_addr2scopeid(ifp, addr) 238 struct ifnet *ifp; /* must not be NULL */ 239 struct in6_addr *addr; /* must not be NULL */ 240 { 241 int scope = in6_addrscope(addr); 242 243 if (scope6_ids == NULL) /* paranoid? */ 244 return(0); /* XXX */ 245 if (ifp->if_index >= if_indexlim) 246 return(0); /* XXX */ 247 248 #define SID scope6_ids[ifp->if_index] 249 switch(scope) { 250 case IPV6_ADDR_SCOPE_NODELOCAL: 251 return(-1); /* XXX: is this an appropriate value? */ 252 253 case IPV6_ADDR_SCOPE_LINKLOCAL: 254 return(SID.s6id_list[IPV6_ADDR_SCOPE_LINKLOCAL]); 255 256 case IPV6_ADDR_SCOPE_SITELOCAL: 257 return(SID.s6id_list[IPV6_ADDR_SCOPE_SITELOCAL]); 258 259 case IPV6_ADDR_SCOPE_ORGLOCAL: 260 return(SID.s6id_list[IPV6_ADDR_SCOPE_ORGLOCAL]); 261 262 default: 263 return(0); /* XXX: treat as global. */ 264 } 265 #undef SID 266 } 267 268 void 269 scope6_setdefault(ifp) 270 struct ifnet *ifp; /* note that this might be NULL */ 271 { 272 /* 273 * Currently, this function just set the default "link" according to 274 * 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 scope6_ids[0].s6id_list[IPV6_ADDR_SCOPE_LINKLOCAL] = 280 ifp->if_index; 281 } 282 else 283 scope6_ids[0].s6id_list[IPV6_ADDR_SCOPE_LINKLOCAL] = 0; 284 } 285 286 int 287 scope6_get_default(idlist) 288 u_int32_t *idlist; 289 { 290 if (scope6_ids == NULL) /* paranoid? */ 291 return(EINVAL); 292 293 bcopy(scope6_ids[0].s6id_list, idlist, 294 sizeof(scope6_ids[0].s6id_list)); 295 296 return(0); 297 } 298 299 u_int32_t 300 scope6_addr2default(addr) 301 struct in6_addr *addr; 302 { 303 return(scope6_ids[0].s6id_list[in6_addrscope(addr)]); 304 } 305