xref: /openbsd-src/sys/netinet6/in6_src.c (revision 66570633f456bbd2712f96f3452d64c4aa3115bd)
1*66570633Sbluhm /*	$OpenBSD: in6_src.c,v 1.100 2025/01/01 13:44:22 bluhm Exp $	*/
27d9ba336Sitojun /*	$KAME: in6_src.c,v 1.36 2001/02/06 04:08:17 itojun Exp $	*/
3287546eaSitojun 
4287546eaSitojun /*
5287546eaSitojun  * Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project.
6287546eaSitojun  * All rights reserved.
7287546eaSitojun  *
8287546eaSitojun  * Redistribution and use in source and binary forms, with or without
9287546eaSitojun  * modification, are permitted provided that the following conditions
10287546eaSitojun  * are met:
11287546eaSitojun  * 1. Redistributions of source code must retain the above copyright
12287546eaSitojun  *    notice, this list of conditions and the following disclaimer.
13287546eaSitojun  * 2. Redistributions in binary form must reproduce the above copyright
14287546eaSitojun  *    notice, this list of conditions and the following disclaimer in the
15287546eaSitojun  *    documentation and/or other materials provided with the distribution.
16287546eaSitojun  * 3. Neither the name of the project nor the names of its contributors
17287546eaSitojun  *    may be used to endorse or promote products derived from this software
18287546eaSitojun  *    without specific prior written permission.
19287546eaSitojun  *
20287546eaSitojun  * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND
21287546eaSitojun  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
22287546eaSitojun  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
23287546eaSitojun  * ARE DISCLAIMED.  IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE
24287546eaSitojun  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
25287546eaSitojun  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
26287546eaSitojun  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
27287546eaSitojun  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
28287546eaSitojun  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
29287546eaSitojun  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
30287546eaSitojun  * SUCH DAMAGE.
31287546eaSitojun  */
32287546eaSitojun 
33287546eaSitojun /*
34287546eaSitojun  * Copyright (c) 1982, 1986, 1991, 1993
35287546eaSitojun  *	The Regents of the University of California.  All rights reserved.
36287546eaSitojun  *
37287546eaSitojun  * Redistribution and use in source and binary forms, with or without
38287546eaSitojun  * modification, are permitted provided that the following conditions
39287546eaSitojun  * are met:
40287546eaSitojun  * 1. Redistributions of source code must retain the above copyright
41287546eaSitojun  *    notice, this list of conditions and the following disclaimer.
42287546eaSitojun  * 2. Redistributions in binary form must reproduce the above copyright
43287546eaSitojun  *    notice, this list of conditions and the following disclaimer in the
44287546eaSitojun  *    documentation and/or other materials provided with the distribution.
4529295d1cSmillert  * 3. Neither the name of the University nor the names of its contributors
46287546eaSitojun  *    may be used to endorse or promote products derived from this software
47287546eaSitojun  *    without specific prior written permission.
48287546eaSitojun  *
49287546eaSitojun  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
50287546eaSitojun  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
51287546eaSitojun  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
52287546eaSitojun  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
53287546eaSitojun  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
54287546eaSitojun  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
55287546eaSitojun  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
56287546eaSitojun  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
57287546eaSitojun  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
58287546eaSitojun  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
59287546eaSitojun  * SUCH DAMAGE.
60287546eaSitojun  *
61287546eaSitojun  *	@(#)in_pcb.c	8.2 (Berkeley) 1/4/94
62287546eaSitojun  */
63287546eaSitojun 
64287546eaSitojun #include <sys/param.h>
65287546eaSitojun #include <sys/systm.h>
66287546eaSitojun #include <sys/mbuf.h>
67287546eaSitojun #include <sys/socket.h>
68287546eaSitojun #include <sys/socketvar.h>
69287546eaSitojun #include <sys/ioctl.h>
70287546eaSitojun #include <sys/errno.h>
71287546eaSitojun #include <sys/time.h>
72287546eaSitojun 
73287546eaSitojun #include <net/if.h>
740deb6685Smpi #include <net/if_var.h>
75287546eaSitojun #include <net/route.h>
76287546eaSitojun 
77287546eaSitojun #include <netinet/in.h>
78287546eaSitojun #include <netinet/ip.h>
79287546eaSitojun #include <netinet/in_pcb.h>
80287546eaSitojun #include <netinet6/in6_var.h>
81fa86ee14Sitojun #include <netinet/ip6.h>
82287546eaSitojun #include <netinet6/ip6_var.h>
83287546eaSitojun #include <netinet6/nd6.h>
84287546eaSitojun 
8522723314Sbluhm int in6_selectif(const struct in6_addr *, struct ip6_pktopts *,
8694c0e2bdSbluhm     struct ip6_moptions *, struct route *, struct ifnet **, u_int);
876b532452Sitojun 
88287546eaSitojun /*
897d9ba336Sitojun  * Return an IPv6 address, which is the most appropriate for a given
903e4bd4b4Svgross  * destination and pcb. We need the additional opt parameter because
91678831beSjsg  * the values set at pcb level can be overridden via cmsg.
92287546eaSitojun  */
930039ae51Sjca int
94cff23a6bSbluhm in6_pcbselsrc(const struct in6_addr **in6src, struct sockaddr_in6 *dstsock,
953e4bd4b4Svgross     struct inpcb *inp, struct ip6_pktopts *opts)
96287546eaSitojun {
973e4bd4b4Svgross 	struct ip6_moptions *mopts = inp->inp_moptions6;
98eb7afff9Sbluhm 	struct rtentry *rt;
99990f2b24Sbluhm 	const struct in6_addr *laddr = &inp->inp_laddr6;
1003e4bd4b4Svgross 	u_int rtableid = inp->inp_rtableid;
10173e65003Smpi 	struct ifnet *ifp = NULL;
10272366f00Sdenis 	struct sockaddr	*ip6_source = NULL;
103287546eaSitojun 	struct in6_addr *dst;
1046143cd01Sbluhm 	struct in6_ifaddr *ia6 = NULL;
105287546eaSitojun 	struct in6_pktinfo *pi = NULL;
1060039ae51Sjca 	int	error;
107287546eaSitojun 
108287546eaSitojun 	dst = &dstsock->sin6_addr;
109287546eaSitojun 
110287546eaSitojun 	/*
111287546eaSitojun 	 * If the source address is explicitly specified by the caller,
11292b7cce5Smikeb 	 * check if the requested source address is indeed a unicast address
11392b7cce5Smikeb 	 * assigned to the node, and can be used as the packet's source
11492b7cce5Smikeb 	 * address.  If everything is okay, use the address as source.
115287546eaSitojun 	 */
116287546eaSitojun 	if (opts && (pi = opts->ip6po_pktinfo) &&
11792b7cce5Smikeb 	    !IN6_IS_ADDR_UNSPECIFIED(&pi->ipi6_addr)) {
11892b7cce5Smikeb 		struct sockaddr_in6 sa6;
11992b7cce5Smikeb 
12092b7cce5Smikeb 		/* get the outgoing interface */
121eb7afff9Sbluhm 		error = in6_selectif(dst, opts, mopts, &inp->inp_route, &ifp,
122eb7afff9Sbluhm 		    rtableid);
1230039ae51Sjca 		if (error)
1240039ae51Sjca 			return (error);
12592b7cce5Smikeb 
12692b7cce5Smikeb 		bzero(&sa6, sizeof(sa6));
12792b7cce5Smikeb 		sa6.sin6_family = AF_INET6;
12892b7cce5Smikeb 		sa6.sin6_len = sizeof(sa6);
12992b7cce5Smikeb 		sa6.sin6_addr = pi->ipi6_addr;
13092b7cce5Smikeb 
13192b7cce5Smikeb 		if (ifp && IN6_IS_SCOPE_EMBED(&sa6.sin6_addr))
13292b7cce5Smikeb 			sa6.sin6_addr.s6_addr16[1] = htons(ifp->if_index);
1330e3d2ab2Sclaudio 		if_put(ifp); /* put reference from in6_selectif */
13492b7cce5Smikeb 
135c3c56496Sbluhm 		ia6 = ifatoia6(ifa_ifwithaddr(sin6tosa(&sa6), rtableid));
136b2ea68e0Smpi 		if (ia6 == NULL || (ia6->ia6_flags &
137b2ea68e0Smpi 		     (IN6_IFF_ANYCAST|IN6_IFF_TENTATIVE|IN6_IFF_DUPLICATED)))
1380039ae51Sjca 			return (EADDRNOTAVAIL);
13992b7cce5Smikeb 
14092b7cce5Smikeb 		pi->ipi6_addr = sa6.sin6_addr; /* XXX: this overrides pi */
14192b7cce5Smikeb 
1420039ae51Sjca 		*in6src = &pi->ipi6_addr;
1430039ae51Sjca 		return (0);
14492b7cce5Smikeb 	}
145287546eaSitojun 
146287546eaSitojun 	/*
147287546eaSitojun 	 * If the source address is not specified but the socket(if any)
148287546eaSitojun 	 * is already bound, use the bound address.
149287546eaSitojun 	 */
1500039ae51Sjca 	if (laddr && !IN6_IS_ADDR_UNSPECIFIED(laddr)) {
1510039ae51Sjca 		*in6src = laddr;
1520039ae51Sjca 		return (0);
1530039ae51Sjca 	}
154287546eaSitojun 
155287546eaSitojun 	/*
156287546eaSitojun 	 * If the caller doesn't specify the source address but
157287546eaSitojun 	 * the outgoing interface, use an address associated with
158287546eaSitojun 	 * the interface.
159287546eaSitojun 	 */
160287546eaSitojun 	if (pi && pi->ipi6_ifindex) {
16173e65003Smpi 		ifp = if_get(pi->ipi6_ifindex);
1620039ae51Sjca 		if (ifp == NULL)
1630039ae51Sjca 			return (ENXIO); /* XXX: better error? */
1640039ae51Sjca 
1657186e918Sflorian 		ia6 = in6_ifawithscope(ifp, dst, rtableid, NULL);
1660e3d2ab2Sclaudio 		if_put(ifp);
1670e3d2ab2Sclaudio 
1680039ae51Sjca 		if (ia6 == NULL)
1690039ae51Sjca 			return (EADDRNOTAVAIL);
1700039ae51Sjca 
1710039ae51Sjca 		*in6src = &ia6->ia_addr.sin6_addr;
172287546eaSitojun 		return (0);
173287546eaSitojun 	}
174287546eaSitojun 
17590fa29faSmpi 	error = in6_selectsrc(in6src, dstsock, mopts, rtableid);
17690fa29faSmpi 	if (error != EADDRNOTAVAIL)
17790fa29faSmpi 		return (error);
17890fa29faSmpi 
17990fa29faSmpi 	/*
18090fa29faSmpi 	 * If route is known or can be allocated now,
18190fa29faSmpi 	 * our src addr is taken from the i/f, else punt.
18290fa29faSmpi 	 */
183eb7afff9Sbluhm 	rt = route6_mpath(&inp->inp_route, dst, NULL, rtableid);
18490fa29faSmpi 
18590fa29faSmpi 	/*
18690fa29faSmpi 	 * in_pcbconnect() checks out IFF_LOOPBACK to skip using
18790fa29faSmpi 	 * the address. But we don't know why it does so.
18890fa29faSmpi 	 * It is necessary to ensure the scope even for lo0
18990fa29faSmpi 	 * so doesn't check out IFF_LOOPBACK.
19090fa29faSmpi 	 */
19190fa29faSmpi 
192eb7afff9Sbluhm 	if (rt != NULL) {
193eb7afff9Sbluhm 		ifp = if_get(rt->rt_ifidx);
19490fa29faSmpi 		if (ifp != NULL) {
1957186e918Sflorian 			ia6 = in6_ifawithscope(ifp, dst, rtableid, rt);
19690fa29faSmpi 			if_put(ifp);
19790fa29faSmpi 		}
19890fa29faSmpi 		if (ia6 == NULL) /* xxx scope error ?*/
199eb7afff9Sbluhm 			ia6 = ifatoia6(rt->rt_ifa);
20090fa29faSmpi 	}
20172366f00Sdenis 
20272366f00Sdenis 	/*
20372366f00Sdenis 	 * Use preferred source address if :
20472366f00Sdenis 	 * - destination is not onlink
205678831beSjsg 	 * - preferred source address is set
20672366f00Sdenis 	 * - output interface is UP
20772366f00Sdenis 	 */
208eb7afff9Sbluhm 	if (rt != NULL && !(rt->rt_flags & RTF_LLINFO) &&
209eb7afff9Sbluhm 	    !(rt->rt_flags & RTF_HOST)) {
21072366f00Sdenis 		ip6_source = rtable_getsource(rtableid, AF_INET6);
21172366f00Sdenis 		if (ip6_source != NULL) {
21272366f00Sdenis 			struct ifaddr *ifa;
21372366f00Sdenis 			if ((ifa = ifa_ifwithaddr(ip6_source, rtableid)) !=
21472366f00Sdenis 			    NULL && ISSET(ifa->ifa_ifp->if_flags, IFF_UP)) {
215e547fc4cSdenis 				*in6src = &satosin6(ip6_source)->sin6_addr;
21672366f00Sdenis 				return (0);
21772366f00Sdenis 			}
21872366f00Sdenis 		}
21972366f00Sdenis 	}
22072366f00Sdenis 
22190fa29faSmpi 	if (ia6 == NULL)
22290fa29faSmpi 		return (EHOSTUNREACH);	/* no route */
22390fa29faSmpi 
22490fa29faSmpi 	*in6src = &ia6->ia_addr.sin6_addr;
22590fa29faSmpi 	return (0);
2263e4bd4b4Svgross }
2273e4bd4b4Svgross 
2283e4bd4b4Svgross /*
2293e4bd4b4Svgross  * Return an IPv6 address, which is the most appropriate for a given
2303e4bd4b4Svgross  * destination and multicast options.
2313e4bd4b4Svgross  * If necessary, this function lookups the routing table and returns
2323e4bd4b4Svgross  * an entry to the caller for later use.
2333e4bd4b4Svgross  */
2343e4bd4b4Svgross int
235cff23a6bSbluhm in6_selectsrc(const struct in6_addr **in6src, struct sockaddr_in6 *dstsock,
23690fa29faSmpi     struct ip6_moptions *mopts, unsigned int rtableid)
2373e4bd4b4Svgross {
2383e4bd4b4Svgross 	struct ifnet *ifp = NULL;
2393e4bd4b4Svgross 	struct in6_addr *dst;
2403e4bd4b4Svgross 	struct in6_ifaddr *ia6 = NULL;
2413e4bd4b4Svgross 
2423e4bd4b4Svgross 	dst = &dstsock->sin6_addr;
2433e4bd4b4Svgross 
244287546eaSitojun 	/*
245287546eaSitojun 	 * If the destination address is a link-local unicast address or
246d4070096Sitojun 	 * a link/interface-local multicast address, and if the outgoing
247d4070096Sitojun 	 * interface is specified by the sin6_scope_id filed, use an address
248d4070096Sitojun 	 * associated with the interface.
249287546eaSitojun 	 * XXX: We're now trying to define more specific semantics of
250287546eaSitojun 	 *      sin6_scope_id field, so this part will be rewritten in
251287546eaSitojun 	 *      the near future.
252287546eaSitojun 	 */
253d4070096Sitojun 	if ((IN6_IS_ADDR_LINKLOCAL(dst) || IN6_IS_ADDR_MC_LINKLOCAL(dst) ||
254d4070096Sitojun 	     IN6_IS_ADDR_MC_INTFACELOCAL(dst)) && dstsock->sin6_scope_id) {
25573e65003Smpi 		ifp = if_get(dstsock->sin6_scope_id);
2560039ae51Sjca 		if (ifp == NULL)
2570039ae51Sjca 			return (ENXIO); /* XXX: better error? */
2580039ae51Sjca 
2597186e918Sflorian 		ia6 = in6_ifawithscope(ifp, dst, rtableid, NULL);
2600e3d2ab2Sclaudio 		if_put(ifp);
2610e3d2ab2Sclaudio 
2620039ae51Sjca 		if (ia6 == NULL)
2630039ae51Sjca 			return (EADDRNOTAVAIL);
2640039ae51Sjca 
2650039ae51Sjca 		*in6src = &ia6->ia_addr.sin6_addr;
266287546eaSitojun 		return (0);
267287546eaSitojun 	}
268287546eaSitojun 
269287546eaSitojun 	/*
270287546eaSitojun 	 * If the destination address is a multicast address and
271287546eaSitojun 	 * the outgoing interface for the address is specified
272287546eaSitojun 	 * by the caller, use an address associated with the interface.
273287546eaSitojun 	 * Even if the outgoing interface is not specified, we also
274287546eaSitojun 	 * choose a loopback interface as the outgoing interface.
275287546eaSitojun 	 */
276287546eaSitojun 	if (IN6_IS_ADDR_MULTICAST(dst)) {
277ec0be06bSmpi 		ifp = mopts ? if_get(mopts->im6o_ifidx) : NULL;
278287546eaSitojun 
279d4070096Sitojun 		if (!ifp && dstsock->sin6_scope_id)
28073e65003Smpi 			ifp = if_get(htons(dstsock->sin6_scope_id));
281287546eaSitojun 
282287546eaSitojun 		if (ifp) {
2837186e918Sflorian 			ia6 = in6_ifawithscope(ifp, dst, rtableid, NULL);
2840e3d2ab2Sclaudio 			if_put(ifp);
2850e3d2ab2Sclaudio 
2860039ae51Sjca 			if (ia6 == NULL)
2870039ae51Sjca 				return (EADDRNOTAVAIL);
2880039ae51Sjca 
2890039ae51Sjca 			*in6src = &ia6->ia_addr.sin6_addr;
290287546eaSitojun 			return (0);
291287546eaSitojun 		}
292287546eaSitojun 	}
293287546eaSitojun 
2940039ae51Sjca 	return (EADDRNOTAVAIL);
295287546eaSitojun }
296287546eaSitojun 
297aaf34fadSmpi struct rtentry *
29822723314Sbluhm in6_selectroute(const struct in6_addr *dst, struct ip6_pktopts *opts,
29994c0e2bdSbluhm     struct route *ro, unsigned int rtableid)
3006b532452Sitojun {
3016b532452Sitojun 	/*
3026b532452Sitojun 	 * Use a cached route if it exists and is valid, else try to allocate
30355c24ba0Svgross 	 * a new one.
3046b532452Sitojun 	 */
3056b532452Sitojun 	if (ro) {
306eb7afff9Sbluhm 		struct rtentry *rt;
307eb7afff9Sbluhm 
308eb7afff9Sbluhm 		rt = route6_mpath(ro, dst, NULL, rtableid);
3096b532452Sitojun 
3106b532452Sitojun 		/*
3116b532452Sitojun 		 * Check if the outgoing interface conflicts with
3126b532452Sitojun 		 * the interface specified by ipi6_ifindex (if specified).
3136b532452Sitojun 		 * Note that loopback interface is always okay.
3146b532452Sitojun 		 * (this may happen when we are sending a packet to one of
3156b532452Sitojun 		 *  our own addresses.)
3166b532452Sitojun 		 */
3176b532452Sitojun 		if (opts && opts->ip6po_pktinfo &&
3186b532452Sitojun 		    opts->ip6po_pktinfo->ipi6_ifindex) {
319eb7afff9Sbluhm 			if (rt != NULL && !ISSET(rt->rt_flags, RTF_LOCAL) &&
320eb7afff9Sbluhm 			    rt->rt_ifidx != opts->ip6po_pktinfo->ipi6_ifindex) {
321aaf34fadSmpi 				return (NULL);
3226b532452Sitojun 			}
3236b532452Sitojun 		}
3246b532452Sitojun 
325eb7afff9Sbluhm 		return (rt);
3266b532452Sitojun 	}
3276b532452Sitojun 
328aaf34fadSmpi 	return (NULL);
3296b532452Sitojun }
3306b532452Sitojun 
3316b532452Sitojun int
33222723314Sbluhm in6_selectif(const struct in6_addr *dst, struct ip6_pktopts *opts,
33394c0e2bdSbluhm     struct ip6_moptions *mopts, struct route *ro, struct ifnet **retifp,
334ba79ddd5Ssperreault     u_int rtableid)
33592b7cce5Smikeb {
336eb7afff9Sbluhm 	struct rtentry *rt;
337b9ab5338Smpi 	struct in6_pktinfo *pi = NULL;
33892b7cce5Smikeb 
339b9ab5338Smpi 	/* If the caller specify the outgoing interface explicitly, use it. */
340b9ab5338Smpi 	if (opts && (pi = opts->ip6po_pktinfo) != NULL && pi->ipi6_ifindex) {
341b9ab5338Smpi 		*retifp = if_get(pi->ipi6_ifindex);
342b9ab5338Smpi 		if (*retifp != NULL)
343b9ab5338Smpi 			return (0);
344b9ab5338Smpi 	}
345b9ab5338Smpi 
346b9ab5338Smpi 	/*
347b9ab5338Smpi 	 * If the destination address is a multicast address and the outgoing
348b9ab5338Smpi 	 * interface for the address is specified by the caller, use it.
349b9ab5338Smpi 	 */
35022723314Sbluhm 	if (IN6_IS_ADDR_MULTICAST(dst) &&
351b9ab5338Smpi 	    mopts != NULL && (*retifp = if_get(mopts->im6o_ifidx)) != NULL)
352b9ab5338Smpi 		return (0);
353b9ab5338Smpi 
35422723314Sbluhm 	rt = in6_selectroute(dst, opts, ro, rtableid);
355aaf34fadSmpi 	if (rt == NULL)
356aaf34fadSmpi 		return (EHOSTUNREACH);
35792b7cce5Smikeb 
35892b7cce5Smikeb 	/*
35992b7cce5Smikeb 	 * do not use a rejected or black hole route.
36092b7cce5Smikeb 	 * XXX: this check should be done in the L2 output routine.
36192b7cce5Smikeb 	 * However, if we skipped this check here, we'd see the following
36292b7cce5Smikeb 	 * scenario:
36392b7cce5Smikeb 	 * - install a rejected route for a scoped address prefix
36492b7cce5Smikeb 	 *   (like fe80::/10)
36592b7cce5Smikeb 	 * - send a packet to a destination that matches the scoped prefix,
36692b7cce5Smikeb 	 *   with ambiguity about the scope zone.
36792b7cce5Smikeb 	 * - pick the outgoing interface from the route, and disambiguate the
36892b7cce5Smikeb 	 *   scope zone with the interface.
36992b7cce5Smikeb 	 * - ip6_output() would try to get another route with the "new"
37092b7cce5Smikeb 	 *   destination, which may be valid.
37192b7cce5Smikeb 	 * - we'd see no error on output.
37292b7cce5Smikeb 	 * Although this may not be very harmful, it should still be confusing.
37392b7cce5Smikeb 	 * We thus reject the case here.
37492b7cce5Smikeb 	 */
375eb7afff9Sbluhm 	if (ISSET(rt->rt_flags, RTF_REJECT | RTF_BLACKHOLE))
37692b7cce5Smikeb 		return (rt->rt_flags & RTF_HOST ? EHOSTUNREACH : ENETUNREACH);
37792b7cce5Smikeb 
378824e7fc7Smpi 	*retifp = if_get(rt->rt_ifidx);
37992b7cce5Smikeb 
38092b7cce5Smikeb 	return (0);
38192b7cce5Smikeb }
38292b7cce5Smikeb 
383287546eaSitojun int
384ad6c4bdcSbluhm in6_selecthlim(const struct inpcb *inp)
385287546eaSitojun {
386921ffa12Sbluhm 	if (inp && inp->inp_hops >= 0)
387921ffa12Sbluhm 		return (inp->inp_hops);
38893e31651Smpi 
389287546eaSitojun 	return (ip6_defhlim);
390287546eaSitojun }
391efbbc66dSitojun 
392efbbc66dSitojun /*
393efbbc66dSitojun  * generate kernel-internal form (scopeid embedded into s6_addr16[1]).
394efbbc66dSitojun  * If the address scope of is link-local, embed the interface index in the
395efbbc66dSitojun  * address.  The routine determines our precedence
396efbbc66dSitojun  * between advanced API scope/interface specification and basic API
397efbbc66dSitojun  * specification.
398efbbc66dSitojun  *
399efbbc66dSitojun  * this function should be nuked in the future, when we get rid of
400efbbc66dSitojun  * embedded scopeid thing.
401efbbc66dSitojun  *
402efbbc66dSitojun  * XXX actually, it is over-specification to return ifp against sin6_scope_id.
403efbbc66dSitojun  * there can be multiple interfaces that belong to a particular scope zone
404efbbc66dSitojun  * (in specification, we have 1:N mapping between a scope zone and interfaces).
405efbbc66dSitojun  * we may want to change the function to return something other than ifp.
406efbbc66dSitojun  */
407efbbc66dSitojun int
408eabcb73eSmpi in6_embedscope(struct in6_addr *in6, const struct sockaddr_in6 *sin6,
409952c6363Sbluhm     const struct ip6_pktopts *outputopts6, const struct ip6_moptions *moptions6)
410efbbc66dSitojun {
411efbbc66dSitojun 	u_int32_t scopeid;
412efbbc66dSitojun 
413efbbc66dSitojun 	*in6 = sin6->sin6_addr;
414efbbc66dSitojun 
415efbbc66dSitojun 	/*
416efbbc66dSitojun 	 * don't try to read sin6->sin6_addr beyond here, since the caller may
417efbbc66dSitojun 	 * ask us to overwrite existing sockaddr_in6
418efbbc66dSitojun 	 */
419efbbc66dSitojun 
420d4070096Sitojun 	if (IN6_IS_SCOPE_EMBED(in6)) {
421efbbc66dSitojun 		struct in6_pktinfo *pi;
422efbbc66dSitojun 
423efbbc66dSitojun 		/*
424efbbc66dSitojun 		 * KAME assumption: link id == interface id
425efbbc66dSitojun 		 */
426efbbc66dSitojun 
427952c6363Sbluhm 		if (outputopts6 && (pi = outputopts6->ip6po_pktinfo) &&
428952c6363Sbluhm 		    pi->ipi6_ifindex)
429952c6363Sbluhm 			scopeid = pi->ipi6_ifindex;
430952c6363Sbluhm 		else if (moptions6 && IN6_IS_ADDR_MULTICAST(in6) &&
431952c6363Sbluhm 		    moptions6->im6o_ifidx)
432952c6363Sbluhm 			scopeid = moptions6->im6o_ifidx;
433952c6363Sbluhm 		else
434952c6363Sbluhm 			scopeid = sin6->sin6_scope_id;
435952c6363Sbluhm 
436952c6363Sbluhm 		if (scopeid) {
437952c6363Sbluhm 			struct ifnet *ifp;
438952c6363Sbluhm 
43973e65003Smpi 			ifp = if_get(scopeid);
44073e65003Smpi 			if (ifp == NULL)
441efbbc66dSitojun 				return ENXIO;  /* XXX EINVAL? */
442efbbc66dSitojun 			/*XXX assignment to 16bit from 32bit variable */
443efbbc66dSitojun 			in6->s6_addr16[1] = htons(scopeid & 0xffff);
4442305623bSclaudio 			if_put(ifp);
445efbbc66dSitojun 		}
446952c6363Sbluhm 	}
447efbbc66dSitojun 
448efbbc66dSitojun 	return 0;
449efbbc66dSitojun }
450efbbc66dSitojun 
451efbbc66dSitojun /*
452efbbc66dSitojun  * generate standard sockaddr_in6 from embedded form.
453efbbc66dSitojun  * touches sin6_addr and sin6_scope_id only.
454efbbc66dSitojun  *
455efbbc66dSitojun  * this function should be nuked in the future, when we get rid of
456efbbc66dSitojun  * embedded scopeid thing.
457efbbc66dSitojun  */
4586c8dd0e9Sclaudio void
4596c8dd0e9Sclaudio in6_recoverscope(struct sockaddr_in6 *sin6, const struct in6_addr *in6)
460efbbc66dSitojun {
461efbbc66dSitojun 	u_int32_t scopeid;
462efbbc66dSitojun 
463efbbc66dSitojun 	sin6->sin6_addr = *in6;
464efbbc66dSitojun 
465efbbc66dSitojun 	/*
466efbbc66dSitojun 	 * don't try to read *in6 beyond here, since the caller may
467efbbc66dSitojun 	 * ask us to overwrite existing sockaddr_in6
468efbbc66dSitojun 	 */
469efbbc66dSitojun 
470efbbc66dSitojun 	sin6->sin6_scope_id = 0;
471d4070096Sitojun 	if (IN6_IS_SCOPE_EMBED(in6)) {
472efbbc66dSitojun 		/*
473efbbc66dSitojun 		 * KAME assumption: link id == interface id
474efbbc66dSitojun 		 */
475efbbc66dSitojun 		scopeid = ntohs(sin6->sin6_addr.s6_addr16[1]);
476efbbc66dSitojun 		if (scopeid) {
477efbbc66dSitojun 			sin6->sin6_addr.s6_addr16[1] = 0;
478efbbc66dSitojun 			sin6->sin6_scope_id = scopeid;
479efbbc66dSitojun 		}
480efbbc66dSitojun 	}
481efbbc66dSitojun }
4827d9ba336Sitojun 
4837d9ba336Sitojun /*
484678831beSjsg  * just clear the embedded scope identifier.
4857d9ba336Sitojun  */
4867d9ba336Sitojun void
487525dbc5aSjsing in6_clearscope(struct in6_addr *addr)
4887d9ba336Sitojun {
489d4070096Sitojun 	if (IN6_IS_SCOPE_EMBED(addr))
4907d9ba336Sitojun 		addr->s6_addr16[1] = 0;
4917d9ba336Sitojun }
492