1 /* $FreeBSD$ */ 2 /* $KAME: scope6.c,v 1.9 2000/05/18 15:03:26 jinmei 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 39 #include <net/route.h> 40 #include <net/if.h> 41 42 #include <netinet/in.h> 43 44 #include <netinet6/in6_var.h> 45 #include <netinet6/scope6_var.h> 46 47 struct scope6_id { 48 /* 49 * 16 is correspondent to 4bit multicast scope field. 50 * i.e. from node-local to global with some reserved/unassigned types. 51 */ 52 u_int32_t s6id_list[16]; 53 }; 54 static size_t if_indexlim = 8; 55 struct scope6_id *scope6_ids = NULL; 56 57 void 58 scope6_ifattach(ifp) 59 struct ifnet *ifp; 60 { 61 int s = splnet(); 62 63 /* 64 * We have some arrays that should be indexed by if_index. 65 * since if_index will grow dynamically, they should grow too. 66 */ 67 if (scope6_ids == NULL || if_index >= if_indexlim) { 68 size_t n; 69 caddr_t q; 70 71 while (if_index >= if_indexlim) 72 if_indexlim <<= 1; 73 74 /* grow scope index array */ 75 n = if_indexlim * sizeof(struct scope6_id); 76 /* XXX: need new malloc type? */ 77 q = (caddr_t)malloc(n, M_IFADDR, M_WAITOK); 78 bzero(q, n); 79 if (scope6_ids) { 80 bcopy((caddr_t)scope6_ids, q, n/2); 81 free((caddr_t)scope6_ids, M_IFADDR); 82 } 83 scope6_ids = (struct scope6_id *)q; 84 } 85 86 #define SID scope6_ids[ifp->if_index] 87 88 /* don't initialize if called twice */ 89 if (SID.s6id_list[IPV6_ADDR_SCOPE_LINKLOCAL]) { 90 splx(s); 91 return; 92 } 93 94 /* 95 * XXX: IPV6_ADDR_SCOPE_xxx macros are not standard. 96 * Should we rather hardcode here? 97 */ 98 SID.s6id_list[IPV6_ADDR_SCOPE_LINKLOCAL] = ifp->if_index; 99 #ifdef MULTI_SCOPE 100 /* by default, we don't care about scope boundary for these scopes. */ 101 SID.s6id_list[IPV6_ADDR_SCOPE_SITELOCAL] = 1; 102 SID.s6id_list[IPV6_ADDR_SCOPE_ORGLOCAL] = 1; 103 #endif 104 #undef SID 105 106 splx(s); 107 } 108 109 int 110 scope6_set(ifp, idlist) 111 struct ifnet *ifp; 112 u_int32_t *idlist; 113 { 114 int i, s; 115 int error = 0; 116 117 if (scope6_ids == NULL) /* paranoid? */ 118 return(EINVAL); 119 120 /* 121 * XXX: We need more consistency checks of the relationship among 122 * scopes (e.g. an organization should be larger than a site). 123 */ 124 125 /* 126 * TODO(XXX): after setting, we should reflect the changes to 127 * interface addresses, routing table entries, PCB entries... 128 */ 129 130 s = splnet(); 131 132 for (i = 0; i < 16; i++) { 133 if (idlist[i] && 134 idlist[i] != scope6_ids[ifp->if_index].s6id_list[i]) { 135 if (i == IPV6_ADDR_SCOPE_LINKLOCAL && 136 idlist[i] > if_index) { 137 /* 138 * XXX: theoretically, there should be no 139 * relationship between link IDs and interface 140 * IDs, but we check the consistency for 141 * safety in later use. 142 */ 143 splx(s); 144 return(EINVAL); 145 } 146 147 /* 148 * XXX: we must need lots of work in this case, 149 * but we simply set the new value in this initial 150 * implementation. 151 */ 152 scope6_ids[ifp->if_index].s6id_list[i] = idlist[i]; 153 } 154 } 155 splx(s); 156 157 return(error); 158 } 159 160 int 161 scope6_get(ifp, idlist) 162 struct ifnet *ifp; 163 u_int32_t *idlist; 164 { 165 if (scope6_ids == NULL) /* paranoid? */ 166 return(EINVAL); 167 168 bcopy(scope6_ids[ifp->if_index].s6id_list, idlist, 169 sizeof(scope6_ids[ifp->if_index].s6id_list)); 170 171 return(0); 172 } 173 174 175 /* 176 * Get a scope of the address. Node-local, link-local, site-local or global. 177 */ 178 int 179 in6_addrscope(addr) 180 struct in6_addr *addr; 181 { 182 int scope; 183 184 if (addr->s6_addr8[0] == 0xfe) { 185 scope = addr->s6_addr8[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_addr8[0] == 0xff) { 202 scope = addr->s6_addr8[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_NODELOCAL: 210 return IPV6_ADDR_SCOPE_NODELOCAL; 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 if (bcmp(&in6addr_loopback, addr, sizeof(addr) - 1) == 0) { 225 if (addr->s6_addr8[15] == 1) /* loopback */ 226 return IPV6_ADDR_SCOPE_NODELOCAL; 227 if (addr->s6_addr8[15] == 0) /* unspecified */ 228 return IPV6_ADDR_SCOPE_LINKLOCAL; 229 } 230 231 return IPV6_ADDR_SCOPE_GLOBAL; 232 } 233 234 int 235 in6_addr2scopeid(ifp, addr) 236 struct ifnet *ifp; /* must not be NULL */ 237 struct in6_addr *addr; /* must not be NULL */ 238 { 239 int scope = in6_addrscope(addr); 240 241 if (scope6_ids == NULL) /* paranoid? */ 242 return(0); /* XXX */ 243 if (ifp->if_index >= if_indexlim) 244 return(0); /* XXX */ 245 246 #define SID scope6_ids[ifp->if_index] 247 switch(scope) { 248 case IPV6_ADDR_SCOPE_NODELOCAL: 249 return(-1); /* XXX: is this an appropriate value? */ 250 251 case IPV6_ADDR_SCOPE_LINKLOCAL: 252 return(SID.s6id_list[IPV6_ADDR_SCOPE_LINKLOCAL]); 253 254 case IPV6_ADDR_SCOPE_SITELOCAL: 255 return(SID.s6id_list[IPV6_ADDR_SCOPE_SITELOCAL]); 256 257 case IPV6_ADDR_SCOPE_ORGLOCAL: 258 return(SID.s6id_list[IPV6_ADDR_SCOPE_ORGLOCAL]); 259 260 default: 261 return(0); /* XXX: treat as global. */ 262 } 263 #undef SID 264 } 265 266 void 267 scope6_setdefault(ifp) 268 struct ifnet *ifp; /* note that this might be NULL */ 269 { 270 /* 271 * Currently, this function just set the default "link" according to 272 * the given interface. 273 * We might eventually have to separate the notion of "link" from 274 * "interface" and provide a user interface to set the default. 275 */ 276 if (ifp) { 277 scope6_ids[0].s6id_list[IPV6_ADDR_SCOPE_LINKLOCAL] = 278 ifp->if_index; 279 } 280 else 281 scope6_ids[0].s6id_list[IPV6_ADDR_SCOPE_LINKLOCAL] = 0; 282 } 283 284 int 285 scope6_get_default(idlist) 286 u_int32_t *idlist; 287 { 288 if (scope6_ids == NULL) /* paranoid? */ 289 return(EINVAL); 290 291 bcopy(scope6_ids[0].s6id_list, idlist, 292 sizeof(scope6_ids[0].s6id_list)); 293 294 return(0); 295 } 296 297 u_int32_t 298 scope6_addr2default(addr) 299 struct in6_addr *addr; 300 { 301 return(scope6_ids[0].s6id_list[in6_addrscope(addr)]); 302 } 303