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.3 2004/05/20 18:30:36 cpressey 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(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(struct ifnet *ifp, u_int32_t *idlist) 112 { 113 int i, s; 114 int error = 0; 115 116 if (scope6_ids == NULL) /* paranoid? */ 117 return(EINVAL); 118 119 /* 120 * XXX: We need more consistency checks of the relationship among 121 * scopes (e.g. an organization should be larger than a site). 122 */ 123 124 /* 125 * TODO(XXX): after setting, we should reflect the changes to 126 * interface addresses, routing table entries, PCB entries... 127 */ 128 129 s = splnet(); 130 131 for (i = 0; i < 16; i++) { 132 if (idlist[i] && 133 idlist[i] != scope6_ids[ifp->if_index].s6id_list[i]) { 134 if (i == IPV6_ADDR_SCOPE_LINKLOCAL && 135 idlist[i] > if_index) { 136 /* 137 * XXX: theoretically, there should be no 138 * relationship between link IDs and interface 139 * IDs, but we check the consistency for 140 * safety in later use. 141 */ 142 splx(s); 143 return(EINVAL); 144 } 145 146 /* 147 * XXX: we must need lots of work in this case, 148 * but we simply set the new value in this initial 149 * implementation. 150 */ 151 scope6_ids[ifp->if_index].s6id_list[i] = idlist[i]; 152 } 153 } 154 splx(s); 155 156 return(error); 157 } 158 159 int 160 scope6_get(struct ifnet *ifp, u_int32_t *idlist) 161 { 162 if (scope6_ids == NULL) /* paranoid? */ 163 return(EINVAL); 164 165 bcopy(scope6_ids[ifp->if_index].s6id_list, idlist, 166 sizeof(scope6_ids[ifp->if_index].s6id_list)); 167 168 return(0); 169 } 170 171 172 /* 173 * Get a scope of the address. Node-local, link-local, site-local or global. 174 */ 175 int 176 in6_addrscope(struct in6_addr *addr) 177 { 178 int scope; 179 180 if (addr->s6_addr8[0] == 0xfe) { 181 scope = addr->s6_addr8[1] & 0xc0; 182 183 switch (scope) { 184 case 0x80: 185 return IPV6_ADDR_SCOPE_LINKLOCAL; 186 break; 187 case 0xc0: 188 return IPV6_ADDR_SCOPE_SITELOCAL; 189 break; 190 default: 191 return IPV6_ADDR_SCOPE_GLOBAL; /* just in case */ 192 break; 193 } 194 } 195 196 197 if (addr->s6_addr8[0] == 0xff) { 198 scope = addr->s6_addr8[1] & 0x0f; 199 200 /* 201 * due to other scope such as reserved, 202 * return scope doesn't work. 203 */ 204 switch (scope) { 205 case IPV6_ADDR_SCOPE_NODELOCAL: 206 return IPV6_ADDR_SCOPE_NODELOCAL; 207 break; 208 case IPV6_ADDR_SCOPE_LINKLOCAL: 209 return IPV6_ADDR_SCOPE_LINKLOCAL; 210 break; 211 case IPV6_ADDR_SCOPE_SITELOCAL: 212 return IPV6_ADDR_SCOPE_SITELOCAL; 213 break; 214 default: 215 return IPV6_ADDR_SCOPE_GLOBAL; 216 break; 217 } 218 } 219 220 if (bcmp(&in6addr_loopback, addr, sizeof(*addr) - 1) == 0) { 221 if (addr->s6_addr8[15] == 1) /* loopback */ 222 return IPV6_ADDR_SCOPE_NODELOCAL; 223 if (addr->s6_addr8[15] == 0) /* unspecified */ 224 return IPV6_ADDR_SCOPE_LINKLOCAL; 225 } 226 227 return IPV6_ADDR_SCOPE_GLOBAL; 228 } 229 230 int 231 in6_addr2scopeid(struct ifnet *ifp, /* must not be NULL */ 232 struct in6_addr *addr) /* must not be NULL */ 233 { 234 int scope = in6_addrscope(addr); 235 236 if (scope6_ids == NULL) /* paranoid? */ 237 return(0); /* XXX */ 238 if (ifp->if_index >= if_indexlim) 239 return(0); /* XXX */ 240 241 #define SID scope6_ids[ifp->if_index] 242 switch(scope) { 243 case IPV6_ADDR_SCOPE_NODELOCAL: 244 return(-1); /* XXX: is this an appropriate value? */ 245 246 case IPV6_ADDR_SCOPE_LINKLOCAL: 247 return(SID.s6id_list[IPV6_ADDR_SCOPE_LINKLOCAL]); 248 249 case IPV6_ADDR_SCOPE_SITELOCAL: 250 return(SID.s6id_list[IPV6_ADDR_SCOPE_SITELOCAL]); 251 252 case IPV6_ADDR_SCOPE_ORGLOCAL: 253 return(SID.s6id_list[IPV6_ADDR_SCOPE_ORGLOCAL]); 254 255 default: 256 return(0); /* XXX: treat as global. */ 257 } 258 #undef SID 259 } 260 261 void 262 scope6_setdefault(struct ifnet *ifp) /* note that this might be NULL */ 263 { 264 /* 265 * Currently, this function just set the default "link" according to 266 * the given interface. 267 * We might eventually have to separate the notion of "link" from 268 * "interface" and provide a user interface to set the default. 269 */ 270 if (ifp) { 271 scope6_ids[0].s6id_list[IPV6_ADDR_SCOPE_LINKLOCAL] = 272 ifp->if_index; 273 } 274 else 275 scope6_ids[0].s6id_list[IPV6_ADDR_SCOPE_LINKLOCAL] = 0; 276 } 277 278 int 279 scope6_get_default(u_int32_t *idlist) 280 { 281 if (scope6_ids == NULL) /* paranoid? */ 282 return(EINVAL); 283 284 bcopy(scope6_ids[0].s6id_list, idlist, 285 sizeof(scope6_ids[0].s6id_list)); 286 287 return(0); 288 } 289 290 u_int32_t 291 scope6_addr2default(struct in6_addr *addr) 292 { 293 return(scope6_ids[0].s6id_list[in6_addrscope(addr)]); 294 } 295