in6_src.c revision 1.91
159243Sobrien/* $OpenBSD: in6_src.c,v 1.91 2024/01/09 19:57:01 bluhm Exp $ */ 2100616Smp/* $KAME: in6_src.c,v 1.36 2001/02/06 04:08:17 itojun Exp $ */ 359243Sobrien 459243Sobrien/* 559243Sobrien * Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project. 659243Sobrien * All rights reserved. 759243Sobrien * 859243Sobrien * Redistribution and use in source and binary forms, with or without 959243Sobrien * modification, are permitted provided that the following conditions 1059243Sobrien * are met: 1159243Sobrien * 1. Redistributions of source code must retain the above copyright 1259243Sobrien * notice, this list of conditions and the following disclaimer. 1359243Sobrien * 2. Redistributions in binary form must reproduce the above copyright 1459243Sobrien * notice, this list of conditions and the following disclaimer in the 1559243Sobrien * documentation and/or other materials provided with the distribution. 1659243Sobrien * 3. Neither the name of the project nor the names of its contributors 1759243Sobrien * may be used to endorse or promote products derived from this software 18100616Smp * without specific prior written permission. 1959243Sobrien * 2059243Sobrien * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND 2159243Sobrien * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 2259243Sobrien * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 2359243Sobrien * ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE 2459243Sobrien * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 2559243Sobrien * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 2659243Sobrien * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 2759243Sobrien * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 2859243Sobrien * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 2959243Sobrien * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 3059243Sobrien * SUCH DAMAGE. 3159243Sobrien */ 3259243Sobrien 3359243Sobrien/* 3459243Sobrien * Copyright (c) 1982, 1986, 1991, 1993 3559243Sobrien * The Regents of the University of California. All rights reserved. 36100616Smp * 3759243Sobrien * Redistribution and use in source and binary forms, with or without 3859243Sobrien * modification, are permitted provided that the following conditions 3959243Sobrien * are met: 4059243Sobrien * 1. Redistributions of source code must retain the above copyright 4159243Sobrien * notice, this list of conditions and the following disclaimer. 4259243Sobrien * 2. Redistributions in binary form must reproduce the above copyright 4359243Sobrien * notice, this list of conditions and the following disclaimer in the 4459243Sobrien * documentation and/or other materials provided with the distribution. 4559243Sobrien * 3. Neither the name of the University nor the names of its contributors 4659243Sobrien * may be used to endorse or promote products derived from this software 4759243Sobrien * without specific prior written permission. 4859243Sobrien * 4959243Sobrien * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 5059243Sobrien * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 5159243Sobrien * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 5259243Sobrien * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 5359243Sobrien * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 5459243Sobrien * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 5559243Sobrien * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 5659243Sobrien * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 5759243Sobrien * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 5859243Sobrien * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 5959243Sobrien * SUCH DAMAGE. 6059243Sobrien * 6159243Sobrien * @(#)in_pcb.c 8.2 (Berkeley) 1/4/94 6259243Sobrien */ 6359243Sobrien 6459243Sobrien#include <sys/param.h> 6559243Sobrien#include <sys/systm.h> 6659243Sobrien#include <sys/mbuf.h> 6759243Sobrien#include <sys/socket.h> 6859243Sobrien#include <sys/socketvar.h> 6959243Sobrien#include <sys/ioctl.h> 7059243Sobrien#include <sys/errno.h> 7159243Sobrien#include <sys/time.h> 7259243Sobrien 7359243Sobrien#include <net/if.h> 7459243Sobrien#include <net/if_var.h> 7559243Sobrien#include <net/route.h> 7659243Sobrien 7759243Sobrien#include <netinet/in.h> 7859243Sobrien#include <netinet/ip.h> 7959243Sobrien#include <netinet/in_pcb.h> 8059243Sobrien#include <netinet6/in6_var.h> 8159243Sobrien#include <netinet/ip6.h> 8259243Sobrien#include <netinet6/ip6_var.h> 8359243Sobrien#include <netinet6/nd6.h> 8459243Sobrien 8559243Sobrienint in6_selectif(struct sockaddr_in6 *, struct ip6_pktopts *, 8659243Sobrien struct ip6_moptions *, struct route_in6 *, struct ifnet **, u_int); 8759243Sobrien 8859243Sobrien/* 8959243Sobrien * Return an IPv6 address, which is the most appropriate for a given 9059243Sobrien * destination and pcb. We need the additional opt parameter because 9159243Sobrien * the values set at pcb level can be overridden via cmsg. 9259243Sobrien */ 9359243Sobrienint 9459243Sobrienin6_pcbselsrc(const struct in6_addr **in6src, struct sockaddr_in6 *dstsock, 9559243Sobrien struct inpcb *inp, struct ip6_pktopts *opts) 9659243Sobrien{ 9759243Sobrien struct ip6_moptions *mopts = inp->inp_moptions6; 9859243Sobrien struct route_in6 *ro = &inp->inp_route6; 9959243Sobrien const struct in6_addr *laddr = &inp->inp_laddr6; 10059243Sobrien u_int rtableid = inp->inp_rtableid; 10159243Sobrien struct ifnet *ifp = NULL; 10259243Sobrien struct sockaddr *ip6_source = NULL; 10359243Sobrien struct in6_addr *dst; 10459243Sobrien struct in6_ifaddr *ia6 = NULL; 10559243Sobrien struct in6_pktinfo *pi = NULL; 10659243Sobrien int error; 10759243Sobrien 10859243Sobrien dst = &dstsock->sin6_addr; 10959243Sobrien 11059243Sobrien /* 11159243Sobrien * If the source address is explicitly specified by the caller, 11259243Sobrien * check if the requested source address is indeed a unicast address 11359243Sobrien * assigned to the node, and can be used as the packet's source 11459243Sobrien * address. If everything is okay, use the address as source. 11559243Sobrien */ 11659243Sobrien if (opts && (pi = opts->ip6po_pktinfo) && 11759243Sobrien !IN6_IS_ADDR_UNSPECIFIED(&pi->ipi6_addr)) { 11859243Sobrien struct sockaddr_in6 sa6; 11959243Sobrien 12059243Sobrien /* get the outgoing interface */ 12159243Sobrien error = in6_selectif(dstsock, opts, mopts, ro, &ifp, rtableid); 12259243Sobrien if (error) 12359243Sobrien return (error); 12459243Sobrien 12559243Sobrien bzero(&sa6, sizeof(sa6)); 12659243Sobrien sa6.sin6_family = AF_INET6; 12759243Sobrien sa6.sin6_len = sizeof(sa6); 12859243Sobrien sa6.sin6_addr = pi->ipi6_addr; 12959243Sobrien 13059243Sobrien if (ifp && IN6_IS_SCOPE_EMBED(&sa6.sin6_addr)) 13159243Sobrien sa6.sin6_addr.s6_addr16[1] = htons(ifp->if_index); 13259243Sobrien if_put(ifp); /* put reference from in6_selectif */ 13359243Sobrien 13459243Sobrien ia6 = ifatoia6(ifa_ifwithaddr(sin6tosa(&sa6), rtableid)); 13559243Sobrien if (ia6 == NULL || (ia6->ia6_flags & 13659243Sobrien (IN6_IFF_ANYCAST|IN6_IFF_TENTATIVE|IN6_IFF_DUPLICATED))) 13759243Sobrien return (EADDRNOTAVAIL); 13859243Sobrien 13959243Sobrien pi->ipi6_addr = sa6.sin6_addr; /* XXX: this overrides pi */ 14059243Sobrien 14159243Sobrien *in6src = &pi->ipi6_addr; 14259243Sobrien return (0); 14359243Sobrien } 14459243Sobrien 14559243Sobrien /* 14659243Sobrien * If the source address is not specified but the socket(if any) 14759243Sobrien * is already bound, use the bound address. 14859243Sobrien */ 14959243Sobrien if (laddr && !IN6_IS_ADDR_UNSPECIFIED(laddr)) { 15059243Sobrien *in6src = laddr; 15159243Sobrien return (0); 15259243Sobrien } 15359243Sobrien 15459243Sobrien /* 15559243Sobrien * If the caller doesn't specify the source address but 15659243Sobrien * the outgoing interface, use an address associated with 15759243Sobrien * the interface. 15859243Sobrien */ 15959243Sobrien if (pi && pi->ipi6_ifindex) { 16059243Sobrien ifp = if_get(pi->ipi6_ifindex); 16159243Sobrien if (ifp == NULL) 16259243Sobrien return (ENXIO); /* XXX: better error? */ 16359243Sobrien 16459243Sobrien ia6 = in6_ifawithscope(ifp, dst, rtableid); 16559243Sobrien if_put(ifp); 16659243Sobrien 16759243Sobrien if (ia6 == NULL) 16859243Sobrien return (EADDRNOTAVAIL); 16959243Sobrien 17059243Sobrien *in6src = &ia6->ia_addr.sin6_addr; 17159243Sobrien return (0); 17259243Sobrien } 17359243Sobrien 17459243Sobrien error = in6_selectsrc(in6src, dstsock, mopts, rtableid); 17559243Sobrien if (error != EADDRNOTAVAIL) 17659243Sobrien return (error); 17759243Sobrien 17859243Sobrien /* 17959243Sobrien * If route is known or can be allocated now, 18059243Sobrien * our src addr is taken from the i/f, else punt. 18159243Sobrien */ 18259243Sobrien if (!rtisvalid(ro->ro_rt) || (ro->ro_tableid != rtableid) || 18361515Sobrien !IN6_ARE_ADDR_EQUAL(&ro->ro_dst.sin6_addr, dst)) { 18461515Sobrien rtfree(ro->ro_rt); 18561515Sobrien ro->ro_rt = NULL; 18661515Sobrien } 18761515Sobrien if (ro->ro_rt == NULL) { 18859243Sobrien struct sockaddr_in6 *sa6; 18959243Sobrien 19059243Sobrien /* No route yet, so try to acquire one */ 19159243Sobrien bzero(&ro->ro_dst, sizeof(struct sockaddr_in6)); 19259243Sobrien ro->ro_tableid = rtableid; 19359243Sobrien sa6 = &ro->ro_dst; 19459243Sobrien sa6->sin6_family = AF_INET6; 19559243Sobrien sa6->sin6_len = sizeof(struct sockaddr_in6); 19659243Sobrien sa6->sin6_addr = *dst; 19759243Sobrien sa6->sin6_scope_id = dstsock->sin6_scope_id; 19859243Sobrien ro->ro_rt = rtalloc(sin6tosa(&ro->ro_dst), 19959243Sobrien RT_RESOLVE, ro->ro_tableid); 20059243Sobrien } 20159243Sobrien 20259243Sobrien /* 20359243Sobrien * in_pcbconnect() checks out IFF_LOOPBACK to skip using 20459243Sobrien * the address. But we don't know why it does so. 20559243Sobrien * It is necessary to ensure the scope even for lo0 20659243Sobrien * so doesn't check out IFF_LOOPBACK. 20759243Sobrien */ 20859243Sobrien 20959243Sobrien if (ro->ro_rt) { 21059243Sobrien ifp = if_get(ro->ro_rt->rt_ifidx); 21159243Sobrien if (ifp != NULL) { 21259243Sobrien ia6 = in6_ifawithscope(ifp, dst, rtableid); 21359243Sobrien if_put(ifp); 21459243Sobrien } 21559243Sobrien if (ia6 == NULL) /* xxx scope error ?*/ 21659243Sobrien ia6 = ifatoia6(ro->ro_rt->rt_ifa); 21759243Sobrien } 21859243Sobrien 21959243Sobrien /* 22059243Sobrien * Use preferred source address if : 22159243Sobrien * - destination is not onlink 22259243Sobrien * - preferred source address is set 22359243Sobrien * - output interface is UP 22459243Sobrien */ 22559243Sobrien if (ro->ro_rt && !(ro->ro_rt->rt_flags & RTF_LLINFO) && 22659243Sobrien !(ro->ro_rt->rt_flags & RTF_HOST)) { 22759243Sobrien ip6_source = rtable_getsource(rtableid, AF_INET6); 22859243Sobrien if (ip6_source != NULL) { 22959243Sobrien struct ifaddr *ifa; 23059243Sobrien if ((ifa = ifa_ifwithaddr(ip6_source, rtableid)) != 23159243Sobrien NULL && ISSET(ifa->ifa_ifp->if_flags, IFF_UP)) { 23259243Sobrien *in6src = &satosin6(ip6_source)->sin6_addr; 23359243Sobrien return (0); 23459243Sobrien } 23559243Sobrien } 23659243Sobrien } 23759243Sobrien 23859243Sobrien if (ia6 == NULL) 23959243Sobrien return (EHOSTUNREACH); /* no route */ 24059243Sobrien 24159243Sobrien *in6src = &ia6->ia_addr.sin6_addr; 24259243Sobrien return (0); 24359243Sobrien} 24459243Sobrien 24559243Sobrien/* 24659243Sobrien * Return an IPv6 address, which is the most appropriate for a given 24759243Sobrien * destination and multicast options. 24859243Sobrien * If necessary, this function lookups the routing table and returns 24959243Sobrien * an entry to the caller for later use. 25059243Sobrien */ 25159243Sobrienint 25259243Sobrienin6_selectsrc(const struct in6_addr **in6src, struct sockaddr_in6 *dstsock, 25359243Sobrien struct ip6_moptions *mopts, unsigned int rtableid) 25459243Sobrien{ 25559243Sobrien struct ifnet *ifp = NULL; 25659243Sobrien struct in6_addr *dst; 25759243Sobrien struct in6_ifaddr *ia6 = NULL; 25859243Sobrien 25959243Sobrien dst = &dstsock->sin6_addr; 26059243Sobrien 26159243Sobrien /* 26259243Sobrien * If the destination address is a link-local unicast address or 26359243Sobrien * a link/interface-local multicast address, and if the outgoing 26459243Sobrien * interface is specified by the sin6_scope_id filed, use an address 26559243Sobrien * associated with the interface. 26659243Sobrien * XXX: We're now trying to define more specific semantics of 26759243Sobrien * sin6_scope_id field, so this part will be rewritten in 26859243Sobrien * the near future. 26959243Sobrien */ 27059243Sobrien if ((IN6_IS_ADDR_LINKLOCAL(dst) || IN6_IS_ADDR_MC_LINKLOCAL(dst) || 27159243Sobrien IN6_IS_ADDR_MC_INTFACELOCAL(dst)) && dstsock->sin6_scope_id) { 27259243Sobrien ifp = if_get(dstsock->sin6_scope_id); 27359243Sobrien if (ifp == NULL) 27459243Sobrien return (ENXIO); /* XXX: better error? */ 27559243Sobrien 27659243Sobrien ia6 = in6_ifawithscope(ifp, dst, rtableid); 27759243Sobrien if_put(ifp); 27859243Sobrien 27959243Sobrien if (ia6 == NULL) 28059243Sobrien return (EADDRNOTAVAIL); 28159243Sobrien 28259243Sobrien *in6src = &ia6->ia_addr.sin6_addr; 28359243Sobrien return (0); 28459243Sobrien } 28559243Sobrien 28659243Sobrien /* 28759243Sobrien * If the destination address is a multicast address and 28859243Sobrien * the outgoing interface for the address is specified 28959243Sobrien * by the caller, use an address associated with the interface. 29059243Sobrien * Even if the outgoing interface is not specified, we also 29159243Sobrien * choose a loopback interface as the outgoing interface. 29259243Sobrien */ 29359243Sobrien if (IN6_IS_ADDR_MULTICAST(dst)) { 29459243Sobrien ifp = mopts ? if_get(mopts->im6o_ifidx) : NULL; 29559243Sobrien 29659243Sobrien if (!ifp && dstsock->sin6_scope_id) 29759243Sobrien ifp = if_get(htons(dstsock->sin6_scope_id)); 29859243Sobrien 29959243Sobrien if (ifp) { 30059243Sobrien ia6 = in6_ifawithscope(ifp, dst, rtableid); 30159243Sobrien if_put(ifp); 30259243Sobrien 30359243Sobrien if (ia6 == NULL) 30459243Sobrien return (EADDRNOTAVAIL); 30559243Sobrien 30659243Sobrien *in6src = &ia6->ia_addr.sin6_addr; 30759243Sobrien return (0); 30859243Sobrien } 30959243Sobrien } 31059243Sobrien 31159243Sobrien return (EADDRNOTAVAIL); 31259243Sobrien} 31359243Sobrien 31459243Sobrienstruct rtentry * 31559243Sobrienin6_selectroute(struct sockaddr_in6 *dstsock, struct ip6_pktopts *opts, 31659243Sobrien struct route_in6 *ro, unsigned int rtableid) 31759243Sobrien{ 31859243Sobrien struct in6_addr *dst; 31959243Sobrien 32059243Sobrien dst = &dstsock->sin6_addr; 32159243Sobrien 32259243Sobrien /* 32359243Sobrien * Use a cached route if it exists and is valid, else try to allocate 32459243Sobrien * a new one. 32559243Sobrien */ 32659243Sobrien if (ro) { 32759243Sobrien if (rtisvalid(ro->ro_rt)) 32859243Sobrien KASSERT(sin6tosa(&ro->ro_dst)->sa_family == AF_INET6); 32959243Sobrien if (!rtisvalid(ro->ro_rt) || 33059243Sobrien !IN6_ARE_ADDR_EQUAL(&ro->ro_dst.sin6_addr, dst)) { 33159243Sobrien rtfree(ro->ro_rt); 33259243Sobrien ro->ro_rt = NULL; 33359243Sobrien } 33459243Sobrien if (ro->ro_rt == NULL) { 33559243Sobrien struct sockaddr_in6 *sa6; 33659243Sobrien 33759243Sobrien /* No route yet, so try to acquire one */ 33859243Sobrien bzero(&ro->ro_dst, sizeof(struct sockaddr_in6)); 33959243Sobrien ro->ro_tableid = rtableid; 34059243Sobrien sa6 = &ro->ro_dst; 34159243Sobrien *sa6 = *dstsock; 34259243Sobrien sa6->sin6_scope_id = 0; 34359243Sobrien ro->ro_tableid = rtableid; 34459243Sobrien ro->ro_rt = rtalloc_mpath(sin6tosa(&ro->ro_dst), 34559243Sobrien NULL, ro->ro_tableid); 34659243Sobrien } 34759243Sobrien 34859243Sobrien /* 34959243Sobrien * Check if the outgoing interface conflicts with 35059243Sobrien * the interface specified by ipi6_ifindex (if specified). 35159243Sobrien * Note that loopback interface is always okay. 35259243Sobrien * (this may happen when we are sending a packet to one of 35359243Sobrien * our own addresses.) 35459243Sobrien */ 35559243Sobrien if (opts && opts->ip6po_pktinfo && 35659243Sobrien opts->ip6po_pktinfo->ipi6_ifindex) { 35759243Sobrien if (ro->ro_rt != NULL && 35859243Sobrien !ISSET(ro->ro_rt->rt_flags, RTF_LOCAL) && 35959243Sobrien ro->ro_rt->rt_ifidx != 36059243Sobrien opts->ip6po_pktinfo->ipi6_ifindex) { 36159243Sobrien return (NULL); 36259243Sobrien } 36359243Sobrien } 36459243Sobrien 36559243Sobrien return (ro->ro_rt); 36659243Sobrien } 36759243Sobrien 36859243Sobrien return (NULL); 36959243Sobrien} 37059243Sobrien 37159243Sobrienint 37259243Sobrienin6_selectif(struct sockaddr_in6 *dstsock, struct ip6_pktopts *opts, 37359243Sobrien struct ip6_moptions *mopts, struct route_in6 *ro, struct ifnet **retifp, 37459243Sobrien u_int rtableid) 37559243Sobrien{ 37659243Sobrien struct rtentry *rt = NULL; 37759243Sobrien struct in6_pktinfo *pi = NULL; 37859243Sobrien 37959243Sobrien /* If the caller specify the outgoing interface explicitly, use it. */ 38059243Sobrien if (opts && (pi = opts->ip6po_pktinfo) != NULL && pi->ipi6_ifindex) { 38159243Sobrien *retifp = if_get(pi->ipi6_ifindex); 38259243Sobrien if (*retifp != NULL) 38359243Sobrien return (0); 38459243Sobrien } 38559243Sobrien 38659243Sobrien /* 38759243Sobrien * If the destination address is a multicast address and the outgoing 38859243Sobrien * interface for the address is specified by the caller, use it. 38959243Sobrien */ 39059243Sobrien if (IN6_IS_ADDR_MULTICAST(&dstsock->sin6_addr) && 39159243Sobrien mopts != NULL && (*retifp = if_get(mopts->im6o_ifidx)) != NULL) 39259243Sobrien return (0); 39359243Sobrien 39459243Sobrien rt = in6_selectroute(dstsock, opts, ro, rtableid); 39559243Sobrien if (rt == NULL) 39659243Sobrien return (EHOSTUNREACH); 39759243Sobrien 39859243Sobrien /* 39959243Sobrien * do not use a rejected or black hole route. 40059243Sobrien * XXX: this check should be done in the L2 output routine. 40159243Sobrien * However, if we skipped this check here, we'd see the following 40259243Sobrien * scenario: 40359243Sobrien * - install a rejected route for a scoped address prefix 40459243Sobrien * (like fe80::/10) 40559243Sobrien * - send a packet to a destination that matches the scoped prefix, 40659243Sobrien * with ambiguity about the scope zone. 40759243Sobrien * - pick the outgoing interface from the route, and disambiguate the 40859243Sobrien * scope zone with the interface. 40959243Sobrien * - ip6_output() would try to get another route with the "new" 41059243Sobrien * destination, which may be valid. 41159243Sobrien * - we'd see no error on output. 41259243Sobrien * Although this may not be very harmful, it should still be confusing. 41359243Sobrien * We thus reject the case here. 41459243Sobrien */ 41559243Sobrien if (rt && (rt->rt_flags & (RTF_REJECT | RTF_BLACKHOLE))) 41659243Sobrien return (rt->rt_flags & RTF_HOST ? EHOSTUNREACH : ENETUNREACH); 41759243Sobrien 41859243Sobrien if (rt != NULL) 41959243Sobrien *retifp = if_get(rt->rt_ifidx); 42059243Sobrien 42159243Sobrien return (0); 42259243Sobrien} 42359243Sobrien 42459243Sobrienint 42559243Sobrienin6_selecthlim(const struct inpcb *inp) 42659243Sobrien{ 42759243Sobrien if (inp && inp->inp_hops >= 0) 42859243Sobrien return (inp->inp_hops); 42959243Sobrien 43059243Sobrien return (ip6_defhlim); 43159243Sobrien} 43259243Sobrien 43359243Sobrien/* 43459243Sobrien * generate kernel-internal form (scopeid embedded into s6_addr16[1]). 43559243Sobrien * If the address scope of is link-local, embed the interface index in the 43659243Sobrien * address. The routine determines our precedence 43759243Sobrien * between advanced API scope/interface specification and basic API 43859243Sobrien * specification. 43959243Sobrien * 44059243Sobrien * this function should be nuked in the future, when we get rid of 44159243Sobrien * embedded scopeid thing. 44259243Sobrien * 44359243Sobrien * XXX actually, it is over-specification to return ifp against sin6_scope_id. 44459243Sobrien * there can be multiple interfaces that belong to a particular scope zone 44559243Sobrien * (in specification, we have 1:N mapping between a scope zone and interfaces). 44659243Sobrien * we may want to change the function to return something other than ifp. 44759243Sobrien */ 44859243Sobrienint 44959243Sobrienin6_embedscope(struct in6_addr *in6, const struct sockaddr_in6 *sin6, 45059243Sobrien const struct ip6_pktopts *outputopts6, const struct ip6_moptions *moptions6) 45159243Sobrien{ 45259243Sobrien u_int32_t scopeid; 45359243Sobrien 45459243Sobrien *in6 = sin6->sin6_addr; 45559243Sobrien 45659243Sobrien /* 45759243Sobrien * don't try to read sin6->sin6_addr beyond here, since the caller may 45859243Sobrien * ask us to overwrite existing sockaddr_in6 45959243Sobrien */ 46059243Sobrien 46159243Sobrien if (IN6_IS_SCOPE_EMBED(in6)) { 46259243Sobrien struct in6_pktinfo *pi; 46359243Sobrien 46459243Sobrien /* 46559243Sobrien * KAME assumption: link id == interface id 46659243Sobrien */ 467100616Smp 46859243Sobrien if (outputopts6 && (pi = outputopts6->ip6po_pktinfo) && 46959243Sobrien pi->ipi6_ifindex) 47059243Sobrien scopeid = pi->ipi6_ifindex; 47159243Sobrien else if (moptions6 && IN6_IS_ADDR_MULTICAST(in6) && 47259243Sobrien moptions6->im6o_ifidx) 47359243Sobrien scopeid = moptions6->im6o_ifidx; 47459243Sobrien else 47559243Sobrien scopeid = sin6->sin6_scope_id; 47683098Smp 47783098Smp if (scopeid) { 47859243Sobrien struct ifnet *ifp; 47959243Sobrien 48059243Sobrien ifp = if_get(scopeid); 48159243Sobrien if (ifp == NULL) 48259243Sobrien return ENXIO; /* XXX EINVAL? */ 483100616Smp /*XXX assignment to 16bit from 32bit variable */ 484100616Smp in6->s6_addr16[1] = htons(scopeid & 0xffff); 485100616Smp if_put(ifp); 48659243Sobrien } 48759243Sobrien } 48859243Sobrien 48959243Sobrien return 0; 49059243Sobrien} 49159243Sobrien 49259243Sobrien/* 49359243Sobrien * generate standard sockaddr_in6 from embedded form. 49459243Sobrien * touches sin6_addr and sin6_scope_id only. 49559243Sobrien * 49659243Sobrien * this function should be nuked in the future, when we get rid of 49759243Sobrien * embedded scopeid thing. 49859243Sobrien */ 49959243Sobrienvoid 50083098Smpin6_recoverscope(struct sockaddr_in6 *sin6, const struct in6_addr *in6) 50159243Sobrien{ 50259243Sobrien u_int32_t scopeid; 50359243Sobrien 50483098Smp sin6->sin6_addr = *in6; 50583098Smp 50659243Sobrien /* 50759243Sobrien * don't try to read *in6 beyond here, since the caller may 50859243Sobrien * ask us to overwrite existing sockaddr_in6 50959243Sobrien */ 51059243Sobrien 51159243Sobrien sin6->sin6_scope_id = 0; 51259243Sobrien if (IN6_IS_SCOPE_EMBED(in6)) { 51359243Sobrien /* 51459243Sobrien * KAME assumption: link id == interface id 51559243Sobrien */ 51659243Sobrien scopeid = ntohs(sin6->sin6_addr.s6_addr16[1]); 51759243Sobrien if (scopeid) { 51859243Sobrien sin6->sin6_addr.s6_addr16[1] = 0; 51959243Sobrien sin6->sin6_scope_id = scopeid; 52059243Sobrien } 52159243Sobrien } 52259243Sobrien} 52359243Sobrien 52459243Sobrien/* 52559243Sobrien * just clear the embedded scope identifier. 52659243Sobrien */ 52759243Sobrienvoid 52859243Sobrienin6_clearscope(struct in6_addr *addr) 52959243Sobrien{ 53059243Sobrien if (IN6_IS_SCOPE_EMBED(addr)) 53159243Sobrien addr->s6_addr16[1] = 0; 53259243Sobrien} 53359243Sobrien