Annotation of sys/netinet6/in6_src.c, Revision 1.1
1.1 ! nbrk 1: /* $OpenBSD: in6_src.c,v 1.22 2006/12/11 11:26:05 itojun Exp $ */
! 2: /* $KAME: in6_src.c,v 1.36 2001/02/06 04:08:17 itojun Exp $ */
! 3:
! 4: /*
! 5: * Copyright (C) 1995, 1996, 1997, and 1998 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: /*
! 34: * Copyright (c) 1982, 1986, 1991, 1993
! 35: * The Regents of the University of California. All rights reserved.
! 36: *
! 37: * Redistribution and use in source and binary forms, with or without
! 38: * modification, are permitted provided that the following conditions
! 39: * are met:
! 40: * 1. Redistributions of source code must retain the above copyright
! 41: * notice, this list of conditions and the following disclaimer.
! 42: * 2. Redistributions in binary form must reproduce the above copyright
! 43: * notice, this list of conditions and the following disclaimer in the
! 44: * documentation and/or other materials provided with the distribution.
! 45: * 3. Neither the name of the University nor the names of its contributors
! 46: * may be used to endorse or promote products derived from this software
! 47: * without specific prior written permission.
! 48: *
! 49: * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
! 50: * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
! 51: * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
! 52: * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
! 53: * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
! 54: * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
! 55: * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
! 56: * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
! 57: * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
! 58: * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
! 59: * SUCH DAMAGE.
! 60: *
! 61: * @(#)in_pcb.c 8.2 (Berkeley) 1/4/94
! 62: */
! 63:
! 64: #include <sys/param.h>
! 65: #include <sys/systm.h>
! 66: #include <sys/malloc.h>
! 67: #include <sys/mbuf.h>
! 68: #include <sys/protosw.h>
! 69: #include <sys/socket.h>
! 70: #include <sys/socketvar.h>
! 71: #include <sys/ioctl.h>
! 72: #include <sys/errno.h>
! 73: #include <sys/time.h>
! 74: #include <sys/proc.h>
! 75:
! 76: #include <net/if.h>
! 77: #include <net/route.h>
! 78:
! 79: #include <netinet/in.h>
! 80: #include <netinet/in_var.h>
! 81: #include <netinet/in_systm.h>
! 82: #include <netinet/ip.h>
! 83: #include <netinet/in_pcb.h>
! 84: #include <netinet6/in6_var.h>
! 85: #include <netinet/ip6.h>
! 86: #include <netinet6/ip6_var.h>
! 87: #include <netinet6/nd6.h>
! 88:
! 89: static int selectroute(struct sockaddr_in6 *, struct ip6_pktopts *,
! 90: struct ip6_moptions *, struct route_in6 *, struct ifnet **,
! 91: struct rtentry **, int);
! 92:
! 93: /*
! 94: * Return an IPv6 address, which is the most appropriate for a given
! 95: * destination and user specified options.
! 96: * If necessary, this function lookups the routing table and returns
! 97: * an entry to the caller for later use.
! 98: */
! 99: struct in6_addr *
! 100: in6_selectsrc(dstsock, opts, mopts, ro, laddr, errorp)
! 101: struct sockaddr_in6 *dstsock;
! 102: struct ip6_pktopts *opts;
! 103: struct ip6_moptions *mopts;
! 104: struct route_in6 *ro;
! 105: struct in6_addr *laddr;
! 106: int *errorp;
! 107: {
! 108: struct in6_addr *dst;
! 109: struct in6_ifaddr *ia6 = 0;
! 110: struct in6_pktinfo *pi = NULL;
! 111:
! 112: dst = &dstsock->sin6_addr;
! 113: *errorp = 0;
! 114:
! 115: /*
! 116: * If the source address is explicitly specified by the caller,
! 117: * use it.
! 118: */
! 119: if (opts && (pi = opts->ip6po_pktinfo) &&
! 120: !IN6_IS_ADDR_UNSPECIFIED(&pi->ipi6_addr))
! 121: return (&pi->ipi6_addr);
! 122:
! 123: /*
! 124: * If the source address is not specified but the socket(if any)
! 125: * is already bound, use the bound address.
! 126: */
! 127: if (laddr && !IN6_IS_ADDR_UNSPECIFIED(laddr))
! 128: return (laddr);
! 129:
! 130: /*
! 131: * If the caller doesn't specify the source address but
! 132: * the outgoing interface, use an address associated with
! 133: * the interface.
! 134: */
! 135: if (pi && pi->ipi6_ifindex) {
! 136: /* XXX boundary check is assumed to be already done. */
! 137: ia6 = in6_ifawithscope(ifindex2ifnet[pi->ipi6_ifindex],
! 138: dst);
! 139: if (ia6 == 0) {
! 140: *errorp = EADDRNOTAVAIL;
! 141: return (0);
! 142: }
! 143: return (&satosin6(&ia6->ia_addr)->sin6_addr);
! 144: }
! 145:
! 146: /*
! 147: * If the destination address is a link-local unicast address or
! 148: * a link/interface-local multicast address, and if the outgoing
! 149: * interface is specified by the sin6_scope_id filed, use an address
! 150: * associated with the interface.
! 151: * XXX: We're now trying to define more specific semantics of
! 152: * sin6_scope_id field, so this part will be rewritten in
! 153: * the near future.
! 154: */
! 155: if ((IN6_IS_ADDR_LINKLOCAL(dst) || IN6_IS_ADDR_MC_LINKLOCAL(dst) ||
! 156: IN6_IS_ADDR_MC_INTFACELOCAL(dst)) && dstsock->sin6_scope_id) {
! 157: /*
! 158: * I'm not sure if boundary check for scope_id is done
! 159: * somewhere...
! 160: */
! 161: if (dstsock->sin6_scope_id < 0 ||
! 162: if_indexlim <= dstsock->sin6_scope_id ||
! 163: !ifindex2ifnet[dstsock->sin6_scope_id]) {
! 164: *errorp = ENXIO; /* XXX: better error? */
! 165: return (0);
! 166: }
! 167: ia6 = in6_ifawithscope(ifindex2ifnet[dstsock->sin6_scope_id],
! 168: dst);
! 169: if (ia6 == 0) {
! 170: *errorp = EADDRNOTAVAIL;
! 171: return (0);
! 172: }
! 173: return (&satosin6(&ia6->ia_addr)->sin6_addr);
! 174: }
! 175:
! 176: /*
! 177: * If the destination address is a multicast address and
! 178: * the outgoing interface for the address is specified
! 179: * by the caller, use an address associated with the interface.
! 180: * Even if the outgoing interface is not specified, we also
! 181: * choose a loopback interface as the outgoing interface.
! 182: */
! 183: if (IN6_IS_ADDR_MULTICAST(dst)) {
! 184: struct ifnet *ifp = mopts ? mopts->im6o_multicast_ifp : NULL;
! 185:
! 186: if (!ifp && dstsock->sin6_scope_id)
! 187: ifp = ifindex2ifnet[htons(dstsock->sin6_scope_id)];
! 188:
! 189: if (ifp) {
! 190: ia6 = in6_ifawithscope(ifp, dst);
! 191: if (ia6 == 0) {
! 192: *errorp = EADDRNOTAVAIL;
! 193: return (0);
! 194: }
! 195: return (&satosin6(&ia6->ia_addr)->sin6_addr);
! 196: }
! 197: }
! 198:
! 199: /*
! 200: * If the next hop address for the packet is specified
! 201: * by caller, use an address associated with the route
! 202: * to the next hop.
! 203: */
! 204: {
! 205: struct sockaddr_in6 *sin6_next;
! 206: struct rtentry *rt;
! 207:
! 208: if (opts && opts->ip6po_nexthop) {
! 209: sin6_next = satosin6(opts->ip6po_nexthop);
! 210: rt = nd6_lookup(&sin6_next->sin6_addr, 1, NULL);
! 211: if (rt) {
! 212: ia6 = in6_ifawithscope(rt->rt_ifp, dst);
! 213: if (ia6 == 0)
! 214: ia6 = ifatoia6(rt->rt_ifa);
! 215: }
! 216: if (ia6 == 0) {
! 217: *errorp = EADDRNOTAVAIL;
! 218: return (0);
! 219: }
! 220: return (&satosin6(&ia6->ia_addr)->sin6_addr);
! 221: }
! 222: }
! 223:
! 224: /*
! 225: * If route is known or can be allocated now,
! 226: * our src addr is taken from the i/f, else punt.
! 227: */
! 228: if (ro) {
! 229: if (ro->ro_rt &&
! 230: !IN6_ARE_ADDR_EQUAL(&satosin6(&ro->ro_dst)->sin6_addr, dst)) {
! 231: RTFREE(ro->ro_rt);
! 232: ro->ro_rt = (struct rtentry *)0;
! 233: }
! 234: if (ro->ro_rt == (struct rtentry *)0 ||
! 235: ro->ro_rt->rt_ifp == (struct ifnet *)0) {
! 236: struct sockaddr_in6 *sa6;
! 237:
! 238: /* No route yet, so try to acquire one */
! 239: bzero(&ro->ro_dst, sizeof(struct sockaddr_in6));
! 240: sa6 = (struct sockaddr_in6 *)&ro->ro_dst;
! 241: sa6->sin6_family = AF_INET6;
! 242: sa6->sin6_len = sizeof(struct sockaddr_in6);
! 243: sa6->sin6_addr = *dst;
! 244: sa6->sin6_scope_id = dstsock->sin6_scope_id;
! 245: if (IN6_IS_ADDR_MULTICAST(dst)) {
! 246: ro->ro_rt = rtalloc1(&((struct route *)ro)
! 247: ->ro_dst, 0, 0);
! 248: } else {
! 249: rtalloc_mpath((struct route *)ro, NULL, 0);
! 250: }
! 251: }
! 252:
! 253: /*
! 254: * in_pcbconnect() checks out IFF_LOOPBACK to skip using
! 255: * the address. But we don't know why it does so.
! 256: * It is necessary to ensure the scope even for lo0
! 257: * so doesn't check out IFF_LOOPBACK.
! 258: */
! 259:
! 260: if (ro->ro_rt) {
! 261: ia6 = in6_ifawithscope(ro->ro_rt->rt_ifa->ifa_ifp, dst);
! 262: if (ia6 == 0) /* xxx scope error ?*/
! 263: ia6 = ifatoia6(ro->ro_rt->rt_ifa);
! 264: }
! 265: #if 0
! 266: /*
! 267: * xxx The followings are necessary? (kazu)
! 268: * I don't think so.
! 269: * It's for SO_DONTROUTE option in IPv4.(jinmei)
! 270: */
! 271: if (ia6 == 0) {
! 272: struct sockaddr_in6 sin6 = {sizeof(sin6), AF_INET6, 0};
! 273:
! 274: sin6->sin6_addr = *dst;
! 275:
! 276: ia6 = ifatoia6(ifa_ifwithdstaddr(sin6tosa(&sin6)));
! 277: if (ia6 == 0)
! 278: ia6 = ifatoia6(ifa_ifwithnet(sin6tosa(&sin6)));
! 279: if (ia6 == 0)
! 280: return (0);
! 281: return (&satosin6(&ia6->ia_addr)->sin6_addr);
! 282: }
! 283: #endif /* 0 */
! 284: if (ia6 == 0) {
! 285: *errorp = EHOSTUNREACH; /* no route */
! 286: return (0);
! 287: }
! 288: return (&satosin6(&ia6->ia_addr)->sin6_addr);
! 289: }
! 290:
! 291: *errorp = EADDRNOTAVAIL;
! 292: return (0);
! 293: }
! 294:
! 295: static int
! 296: selectroute(dstsock, opts, mopts, ro, retifp, retrt, norouteok)
! 297: struct sockaddr_in6 *dstsock;
! 298: struct ip6_pktopts *opts;
! 299: struct ip6_moptions *mopts;
! 300: struct route_in6 *ro;
! 301: struct ifnet **retifp;
! 302: struct rtentry **retrt;
! 303: int norouteok;
! 304: {
! 305: int error = 0;
! 306: struct ifnet *ifp = NULL;
! 307: struct rtentry *rt = NULL;
! 308: struct sockaddr_in6 *sin6_next;
! 309: struct in6_pktinfo *pi = NULL;
! 310: struct in6_addr *dst;
! 311:
! 312: dst = &dstsock->sin6_addr;
! 313:
! 314: #if 0
! 315: if (dstsock->sin6_addr.s6_addr32[0] == 0 &&
! 316: dstsock->sin6_addr.s6_addr32[1] == 0 &&
! 317: !IN6_IS_ADDR_LOOPBACK(&dstsock->sin6_addr)) {
! 318: printf("in6_selectroute: strange destination %s\n",
! 319: ip6_sprintf(&dstsock->sin6_addr));
! 320: } else {
! 321: printf("in6_selectroute: destination = %s%%%d\n",
! 322: ip6_sprintf(&dstsock->sin6_addr),
! 323: dstsock->sin6_scope_id); /* for debug */
! 324: }
! 325: #endif
! 326:
! 327: /* If the caller specify the outgoing interface explicitly, use it. */
! 328: if (opts && (pi = opts->ip6po_pktinfo) != NULL && pi->ipi6_ifindex) {
! 329: /* XXX boundary check is assumed to be already done. */
! 330: ifp = ifindex2ifnet[pi->ipi6_ifindex];
! 331: if (ifp != NULL &&
! 332: (norouteok || retrt == NULL ||
! 333: IN6_IS_ADDR_MULTICAST(dst))) {
! 334: /*
! 335: * we do not have to check or get the route for
! 336: * multicast.
! 337: */
! 338: goto done;
! 339: } else
! 340: goto getroute;
! 341: }
! 342:
! 343: /*
! 344: * If the destination address is a multicast address and the outgoing
! 345: * interface for the address is specified by the caller, use it.
! 346: */
! 347: if (IN6_IS_ADDR_MULTICAST(dst) &&
! 348: mopts != NULL && (ifp = mopts->im6o_multicast_ifp) != NULL) {
! 349: goto done; /* we do not need a route for multicast. */
! 350: }
! 351:
! 352: getroute:
! 353: /*
! 354: * If the next hop address for the packet is specified by the caller,
! 355: * use it as the gateway.
! 356: */
! 357: if (opts && opts->ip6po_nexthop) {
! 358: struct route_in6 *ron;
! 359:
! 360: sin6_next = satosin6(opts->ip6po_nexthop);
! 361:
! 362: /* at this moment, we only support AF_INET6 next hops */
! 363: if (sin6_next->sin6_family != AF_INET6) {
! 364: error = EAFNOSUPPORT; /* or should we proceed? */
! 365: goto done;
! 366: }
! 367:
! 368: /*
! 369: * If the next hop is an IPv6 address, then the node identified
! 370: * by that address must be a neighbor of the sending host.
! 371: */
! 372: ron = &opts->ip6po_nextroute;
! 373: if ((ron->ro_rt &&
! 374: (ron->ro_rt->rt_flags & (RTF_UP | RTF_GATEWAY)) !=
! 375: RTF_UP) ||
! 376: !IN6_ARE_ADDR_EQUAL(&satosin6(&ron->ro_dst)->sin6_addr,
! 377: &sin6_next->sin6_addr)) {
! 378: if (ron->ro_rt) {
! 379: RTFREE(ron->ro_rt);
! 380: ron->ro_rt = NULL;
! 381: }
! 382: *satosin6(&ron->ro_dst) = *sin6_next;
! 383: }
! 384: if (ron->ro_rt == NULL) {
! 385: rtalloc((struct route *)ron); /* multi path case? */
! 386: if (ron->ro_rt == NULL ||
! 387: (ron->ro_rt->rt_flags & RTF_GATEWAY)) {
! 388: if (ron->ro_rt) {
! 389: RTFREE(ron->ro_rt);
! 390: ron->ro_rt = NULL;
! 391: }
! 392: error = EHOSTUNREACH;
! 393: goto done;
! 394: }
! 395: }
! 396: if (!nd6_is_addr_neighbor(sin6_next, ron->ro_rt->rt_ifp)) {
! 397: RTFREE(ron->ro_rt);
! 398: ron->ro_rt = NULL;
! 399: error = EHOSTUNREACH;
! 400: goto done;
! 401: }
! 402: rt = ron->ro_rt;
! 403: ifp = rt->rt_ifp;
! 404:
! 405: /*
! 406: * When cloning is required, try to allocate a route to the
! 407: * destination so that the caller can store path MTU
! 408: * information.
! 409: */
! 410: goto done;
! 411: }
! 412:
! 413: /*
! 414: * Use a cached route if it exists and is valid, else try to allocate
! 415: * a new one. Note that we should check the address family of the
! 416: * cached destination, in case of sharing the cache with IPv4.
! 417: */
! 418: if (ro) {
! 419: if (ro->ro_rt &&
! 420: (!(ro->ro_rt->rt_flags & RTF_UP) ||
! 421: ((struct sockaddr *)(&ro->ro_dst))->sa_family != AF_INET6 ||
! 422: !IN6_ARE_ADDR_EQUAL(&satosin6(&ro->ro_dst)->sin6_addr,
! 423: dst))) {
! 424: RTFREE(ro->ro_rt);
! 425: ro->ro_rt = (struct rtentry *)NULL;
! 426: }
! 427: if (ro->ro_rt == (struct rtentry *)NULL) {
! 428: struct sockaddr_in6 *sa6;
! 429:
! 430: /* No route yet, so try to acquire one */
! 431: bzero(&ro->ro_dst, sizeof(struct sockaddr_in6));
! 432: sa6 = (struct sockaddr_in6 *)&ro->ro_dst;
! 433: *sa6 = *dstsock;
! 434: sa6->sin6_scope_id = 0;
! 435: rtalloc_mpath((struct route *)ro, NULL, 0);
! 436: }
! 437:
! 438: /*
! 439: * do not care about the result if we have the nexthop
! 440: * explicitly specified.
! 441: */
! 442: if (opts && opts->ip6po_nexthop)
! 443: goto done;
! 444:
! 445: if (ro->ro_rt) {
! 446: ifp = ro->ro_rt->rt_ifp;
! 447:
! 448: if (ifp == NULL) { /* can this really happen? */
! 449: RTFREE(ro->ro_rt);
! 450: ro->ro_rt = NULL;
! 451: }
! 452: }
! 453: if (ro->ro_rt == NULL)
! 454: error = EHOSTUNREACH;
! 455: rt = ro->ro_rt;
! 456:
! 457: /*
! 458: * Check if the outgoing interface conflicts with
! 459: * the interface specified by ipi6_ifindex (if specified).
! 460: * Note that loopback interface is always okay.
! 461: * (this may happen when we are sending a packet to one of
! 462: * our own addresses.)
! 463: */
! 464: if (opts && opts->ip6po_pktinfo &&
! 465: opts->ip6po_pktinfo->ipi6_ifindex) {
! 466: if (!(ifp->if_flags & IFF_LOOPBACK) &&
! 467: ifp->if_index !=
! 468: opts->ip6po_pktinfo->ipi6_ifindex) {
! 469: error = EHOSTUNREACH;
! 470: goto done;
! 471: }
! 472: }
! 473: }
! 474:
! 475: done:
! 476: if (ifp == NULL && rt == NULL) {
! 477: /*
! 478: * This can happen if the caller did not pass a cached route
! 479: * nor any other hints. We treat this case an error.
! 480: */
! 481: error = EHOSTUNREACH;
! 482: }
! 483: if (error == EHOSTUNREACH)
! 484: ip6stat.ip6s_noroute++;
! 485:
! 486: if (retifp != NULL)
! 487: *retifp = ifp;
! 488: if (retrt != NULL)
! 489: *retrt = rt; /* rt may be NULL */
! 490:
! 491: return (error);
! 492: }
! 493:
! 494: int
! 495: in6_selectroute(dstsock, opts, mopts, ro, retifp, retrt)
! 496: struct sockaddr_in6 *dstsock;
! 497: struct ip6_pktopts *opts;
! 498: struct ip6_moptions *mopts;
! 499: struct route_in6 *ro;
! 500: struct ifnet **retifp;
! 501: struct rtentry **retrt;
! 502: {
! 503:
! 504: return (selectroute(dstsock, opts, mopts, ro, retifp, retrt, 0));
! 505: }
! 506:
! 507: /*
! 508: * Default hop limit selection. The precedence is as follows:
! 509: * 1. Hoplimit value specified via ioctl.
! 510: * 2. (If the outgoing interface is detected) the current
! 511: * hop limit of the interface specified by router advertisement.
! 512: * 3. The system default hoplimit.
! 513: */
! 514: #define in6pcb inpcb
! 515: #define in6p_hops inp_hops
! 516: int
! 517: in6_selecthlim(in6p, ifp)
! 518: struct in6pcb *in6p;
! 519: struct ifnet *ifp;
! 520: {
! 521: if (in6p && in6p->in6p_hops >= 0)
! 522: return (in6p->in6p_hops);
! 523: else if (ifp)
! 524: return (ND_IFINFO(ifp)->chlim);
! 525: else
! 526: return (ip6_defhlim);
! 527: }
! 528: #undef in6pcb
! 529: #undef in6p_hops
! 530:
! 531: /*
! 532: * generate kernel-internal form (scopeid embedded into s6_addr16[1]).
! 533: * If the address scope of is link-local, embed the interface index in the
! 534: * address. The routine determines our precedence
! 535: * between advanced API scope/interface specification and basic API
! 536: * specification.
! 537: *
! 538: * this function should be nuked in the future, when we get rid of
! 539: * embedded scopeid thing.
! 540: *
! 541: * XXX actually, it is over-specification to return ifp against sin6_scope_id.
! 542: * there can be multiple interfaces that belong to a particular scope zone
! 543: * (in specification, we have 1:N mapping between a scope zone and interfaces).
! 544: * we may want to change the function to return something other than ifp.
! 545: */
! 546: int
! 547: in6_embedscope(in6, sin6, in6p, ifpp)
! 548: struct in6_addr *in6;
! 549: const struct sockaddr_in6 *sin6;
! 550: struct inpcb *in6p;
! 551: #define in6p_outputopts inp_outputopts6
! 552: #define in6p_moptions inp_moptions6
! 553: struct ifnet **ifpp;
! 554: {
! 555: struct ifnet *ifp = NULL;
! 556: u_int32_t scopeid;
! 557:
! 558: *in6 = sin6->sin6_addr;
! 559: scopeid = sin6->sin6_scope_id;
! 560: if (ifpp)
! 561: *ifpp = NULL;
! 562:
! 563: /*
! 564: * don't try to read sin6->sin6_addr beyond here, since the caller may
! 565: * ask us to overwrite existing sockaddr_in6
! 566: */
! 567:
! 568: if (IN6_IS_SCOPE_EMBED(in6)) {
! 569: struct in6_pktinfo *pi;
! 570:
! 571: /*
! 572: * KAME assumption: link id == interface id
! 573: */
! 574:
! 575: if (in6p && in6p->in6p_outputopts &&
! 576: (pi = in6p->in6p_outputopts->ip6po_pktinfo) &&
! 577: pi->ipi6_ifindex) {
! 578: ifp = ifindex2ifnet[pi->ipi6_ifindex];
! 579: in6->s6_addr16[1] = htons(pi->ipi6_ifindex);
! 580: } else if (in6p && IN6_IS_ADDR_MULTICAST(in6) &&
! 581: in6p->in6p_moptions &&
! 582: in6p->in6p_moptions->im6o_multicast_ifp) {
! 583: ifp = in6p->in6p_moptions->im6o_multicast_ifp;
! 584: in6->s6_addr16[1] = htons(ifp->if_index);
! 585: } else if (scopeid) {
! 586: /* boundary check */
! 587: if (scopeid < 0 || if_indexlim <= scopeid ||
! 588: !ifindex2ifnet[scopeid])
! 589: return ENXIO; /* XXX EINVAL? */
! 590: ifp = ifindex2ifnet[scopeid];
! 591: /*XXX assignment to 16bit from 32bit variable */
! 592: in6->s6_addr16[1] = htons(scopeid & 0xffff);
! 593: }
! 594:
! 595: if (ifpp)
! 596: *ifpp = ifp;
! 597: }
! 598:
! 599: return 0;
! 600: }
! 601: #undef in6p_outputopts
! 602: #undef in6p_moptions
! 603:
! 604: /*
! 605: * generate standard sockaddr_in6 from embedded form.
! 606: * touches sin6_addr and sin6_scope_id only.
! 607: *
! 608: * this function should be nuked in the future, when we get rid of
! 609: * embedded scopeid thing.
! 610: */
! 611: int
! 612: in6_recoverscope(sin6, in6, ifp)
! 613: struct sockaddr_in6 *sin6;
! 614: const struct in6_addr *in6;
! 615: struct ifnet *ifp;
! 616: {
! 617: u_int32_t scopeid;
! 618:
! 619: sin6->sin6_addr = *in6;
! 620:
! 621: /*
! 622: * don't try to read *in6 beyond here, since the caller may
! 623: * ask us to overwrite existing sockaddr_in6
! 624: */
! 625:
! 626: sin6->sin6_scope_id = 0;
! 627: if (IN6_IS_SCOPE_EMBED(in6)) {
! 628: /*
! 629: * KAME assumption: link id == interface id
! 630: */
! 631: scopeid = ntohs(sin6->sin6_addr.s6_addr16[1]);
! 632: if (scopeid) {
! 633: /* sanity check */
! 634: if (scopeid < 0 || if_indexlim <= scopeid ||
! 635: !ifindex2ifnet[scopeid])
! 636: return ENXIO;
! 637: if (ifp && ifp->if_index != scopeid)
! 638: return ENXIO;
! 639: sin6->sin6_addr.s6_addr16[1] = 0;
! 640: sin6->sin6_scope_id = scopeid;
! 641: }
! 642: }
! 643:
! 644: return 0;
! 645: }
! 646:
! 647: /*
! 648: * just clear the embedded scope identifer.
! 649: */
! 650: void
! 651: in6_clearscope(addr)
! 652: struct in6_addr *addr;
! 653: {
! 654: if (IN6_IS_SCOPE_EMBED(addr))
! 655: addr->s6_addr16[1] = 0;
! 656: }
CVSweb