1 /* $FreeBSD$ */ 2 /* $KAME: scope6.c,v 1.10 2000/07/24 13:29:31 itojun Exp $ */ 3 4 /* 5 * Copyright (C) 2000 WIDE Project. 6 * All rights reserved. 7 * 8 * Redistribution and use in source and binary forms, with or without 9 * modification, are permitted provided that the following conditions 10 * are met: 11 * 1. Redistributions of source code must retain the above copyright 12 * notice, this list of conditions and the following disclaimer. 13 * 2. Redistributions in binary form must reproduce the above copyright 14 * notice, this list of conditions and the following disclaimer in the 15 * documentation and/or other materials provided with the distribution. 16 * 3. Neither the name of the project nor the names of its contributors 17 * may be used to endorse or promote products derived from this software 18 * without specific prior written permission. 19 * 20 * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND 21 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 22 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 23 * ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE 24 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 25 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 26 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 27 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 28 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 29 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 30 * SUCH DAMAGE. 31 */ 32 33 #include <sys/param.h> 34 #include <sys/malloc.h> 35 #include <sys/mbuf.h> 36 #include <sys/socket.h> 37 #include <sys/systm.h> 38 #include <sys/queue.h> 39 40 #include <net/route.h> 41 #include <net/if.h> 42 43 #include <netinet/in.h> 44 45 #include <netinet6/in6_var.h> 46 #include <netinet6/scope6_var.h> 47 48 struct scope6_id { 49 /* 50 * 16 is correspondent to 4bit multicast scope field. 51 * i.e. from node-local to global with some reserved/unassigned types. 52 */ 53 u_int32_t s6id_list[16]; 54 }; 55 static size_t if_indexlim = 8; 56 struct scope6_id *scope6_ids = NULL; 57 58 void 59 scope6_ifattach(ifp) 60 struct ifnet *ifp; 61 { 62 int s = splnet(); 63 64 /* 65 * We have some arrays that should be indexed by if_index. 66 * since if_index will grow dynamically, they should grow too. 67 */ 68 if (scope6_ids == NULL || if_index >= if_indexlim) { 69 size_t n; 70 caddr_t q; 71 72 while (if_index >= if_indexlim) 73 if_indexlim <<= 1; 74 75 /* grow scope index array */ 76 n = if_indexlim * sizeof(struct scope6_id); 77 /* XXX: need new malloc type? */ 78 q = (caddr_t)malloc(n, M_IFADDR, M_WAITOK); 79 bzero(q, n); 80 if (scope6_ids) { 81 bcopy((caddr_t)scope6_ids, q, n/2); 82 free((caddr_t)scope6_ids, M_IFADDR); 83 } 84 scope6_ids = (struct scope6_id *)q; 85 } 86 87 #define SID scope6_ids[ifp->if_index] 88 89 /* don't initialize if called twice */ 90 if (SID.s6id_list[IPV6_ADDR_SCOPE_LINKLOCAL]) { 91 splx(s); 92 return; 93 } 94 95 /* 96 * XXX: IPV6_ADDR_SCOPE_xxx macros are not standard. 97 * Should we rather hardcode here? 98 */ 99 SID.s6id_list[IPV6_ADDR_SCOPE_LINKLOCAL] = ifp->if_index; 100 #ifdef MULTI_SCOPE 101 /* by default, we don't care about scope boundary for these scopes. */ 102 SID.s6id_list[IPV6_ADDR_SCOPE_SITELOCAL] = 1; 103 SID.s6id_list[IPV6_ADDR_SCOPE_ORGLOCAL] = 1; 104 #endif 105 #undef SID 106 107 splx(s); 108 } 109 110 int 111 scope6_set(ifp, idlist) 112 struct ifnet *ifp; 113 u_int32_t *idlist; 114 { 115 int i, s; 116 int error = 0; 117 118 if (scope6_ids == NULL) /* paranoid? */ 119 return(EINVAL); 120 121 /* 122 * XXX: We need more consistency checks of the relationship among 123 * scopes (e.g. an organization should be larger than a site). 124 */ 125 126 /* 127 * TODO(XXX): after setting, we should reflect the changes to 128 * interface addresses, routing table entries, PCB entries... 129 */ 130 131 s = splnet(); 132 133 for (i = 0; i < 16; i++) { 134 if (idlist[i] && 135 idlist[i] != scope6_ids[ifp->if_index].s6id_list[i]) { 136 if (i == IPV6_ADDR_SCOPE_LINKLOCAL && 137 idlist[i] > if_index) { 138 /* 139 * XXX: theoretically, there should be no 140 * relationship between link IDs and interface 141 * IDs, but we check the consistency for 142 * safety in later use. 143 */ 144 splx(s); 145 return(EINVAL); 146 } 147 148 /* 149 * XXX: we must need lots of work in this case, 150 * but we simply set the new value in this initial 151 * implementation. 152 */ 153 scope6_ids[ifp->if_index].s6id_list[i] = idlist[i]; 154 } 155 } 156 splx(s); 157 158 return(error); 159 } 160 161 int 162 scope6_get(ifp, idlist) 163 struct ifnet *ifp; 164 u_int32_t *idlist; 165 { 166 if (scope6_ids == NULL) /* paranoid? */ 167 return(EINVAL); 168 169 bcopy(scope6_ids[ifp->if_index].s6id_list, idlist, 170 sizeof(scope6_ids[ifp->if_index].s6id_list)); 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(addr) 181 struct in6_addr *addr; 182 { 183 int scope; 184 185 if (addr->s6_addr8[0] == 0xfe) { 186 scope = addr->s6_addr8[1] & 0xc0; 187 188 switch (scope) { 189 case 0x80: 190 return IPV6_ADDR_SCOPE_LINKLOCAL; 191 break; 192 case 0xc0: 193 return IPV6_ADDR_SCOPE_SITELOCAL; 194 break; 195 default: 196 return IPV6_ADDR_SCOPE_GLOBAL; /* just in case */ 197 break; 198 } 199 } 200 201 202 if (addr->s6_addr8[0] == 0xff) { 203 scope = addr->s6_addr8[1] & 0x0f; 204 205 /* 206 * due to other scope such as reserved, 207 * return scope doesn't work. 208 */ 209 switch (scope) { 210 case IPV6_ADDR_SCOPE_NODELOCAL: 211 return IPV6_ADDR_SCOPE_NODELOCAL; 212 break; 213 case IPV6_ADDR_SCOPE_LINKLOCAL: 214 return IPV6_ADDR_SCOPE_LINKLOCAL; 215 break; 216 case IPV6_ADDR_SCOPE_SITELOCAL: 217 return IPV6_ADDR_SCOPE_SITELOCAL; 218 break; 219 default: 220 return IPV6_ADDR_SCOPE_GLOBAL; 221 break; 222 } 223 } 224 225 if (bcmp(&in6addr_loopback, addr, sizeof(*addr) - 1) == 0) { 226 if (addr->s6_addr8[15] == 1) /* loopback */ 227 return IPV6_ADDR_SCOPE_NODELOCAL; 228 if (addr->s6_addr8[15] == 0) /* unspecified */ 229 return IPV6_ADDR_SCOPE_LINKLOCAL; 230 } 231 232 return IPV6_ADDR_SCOPE_GLOBAL; 233 } 234 235 int 236 in6_addr2scopeid(ifp, addr) 237 struct ifnet *ifp; /* must not be NULL */ 238 struct in6_addr *addr; /* must not be NULL */ 239 { 240 int scope = in6_addrscope(addr); 241 242 if (scope6_ids == NULL) /* paranoid? */ 243 return(0); /* XXX */ 244 if (ifp->if_index >= if_indexlim) 245 return(0); /* XXX */ 246 247 #define SID scope6_ids[ifp->if_index] 248 switch(scope) { 249 case IPV6_ADDR_SCOPE_NODELOCAL: 250 return(-1); /* XXX: is this an appropriate value? */ 251 252 case IPV6_ADDR_SCOPE_LINKLOCAL: 253 return(SID.s6id_list[IPV6_ADDR_SCOPE_LINKLOCAL]); 254 255 case IPV6_ADDR_SCOPE_SITELOCAL: 256 return(SID.s6id_list[IPV6_ADDR_SCOPE_SITELOCAL]); 257 258 case IPV6_ADDR_SCOPE_ORGLOCAL: 259 return(SID.s6id_list[IPV6_ADDR_SCOPE_ORGLOCAL]); 260 261 default: 262 return(0); /* XXX: treat as global. */ 263 } 264 #undef SID 265 } 266 267 void 268 scope6_setdefault(ifp) 269 struct ifnet *ifp; /* note that this might be NULL */ 270 { 271 /* 272 * Currently, this function just set the default "link" according to 273 * the given interface. 274 * We might eventually have to separate the notion of "link" from 275 * "interface" and provide a user interface to set the default. 276 */ 277 if (ifp) { 278 scope6_ids[0].s6id_list[IPV6_ADDR_SCOPE_LINKLOCAL] = 279 ifp->if_index; 280 } 281 else 282 scope6_ids[0].s6id_list[IPV6_ADDR_SCOPE_LINKLOCAL] = 0; 283 } 284 285 int 286 scope6_get_default(idlist) 287 u_int32_t *idlist; 288 { 289 if (scope6_ids == NULL) /* paranoid? */ 290 return(EINVAL); 291 292 bcopy(scope6_ids[0].s6id_list, idlist, 293 sizeof(scope6_ids[0].s6id_list)); 294 295 return(0); 296 } 297 298 u_int32_t 299 scope6_addr2default(addr) 300 struct in6_addr *addr; 301 { 302 return(scope6_ids[0].s6id_list[in6_addrscope(addr)]); 303 } 304