xref: /openbsd-src/sys/netinet6/ip6_forward.c (revision 84d9c64aba152a33fde7c9c690969428d4f7efb9)
1*84d9c64aSbluhm /*	$OpenBSD: ip6_forward.c,v 1.125 2025/01/03 21:27:40 bluhm Exp $	*/
29fd6ccffSitojun /*	$KAME: ip6_forward.c,v 1.75 2001/06/29 12:42:13 jinmei 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 
330f2babb4Sjasoni #include "pf.h"
340f2babb4Sjasoni 
35287546eaSitojun #include <sys/param.h>
36287546eaSitojun #include <sys/systm.h>
37287546eaSitojun #include <sys/mbuf.h>
38287546eaSitojun #include <sys/socket.h>
39287546eaSitojun #include <sys/errno.h>
40287546eaSitojun #include <sys/time.h>
41287546eaSitojun #include <sys/kernel.h>
42287546eaSitojun #include <sys/syslog.h>
43287546eaSitojun 
44287546eaSitojun #include <net/if.h>
450deb6685Smpi #include <net/if_var.h>
46ba8b7f53Smarkus #include <net/if_enc.h>
47287546eaSitojun #include <net/route.h>
482ab93df7Sbluhm #if NPF > 0
492ab93df7Sbluhm #include <net/pfvar.h>
502ab93df7Sbluhm #endif
51287546eaSitojun 
52287546eaSitojun #include <netinet/in.h>
53f4f4d166Sitojun #include <netinet/ip_var.h>
54dc572864Sbluhm #include <netinet6/in6_var.h>
55fa86ee14Sitojun #include <netinet/ip6.h>
56287546eaSitojun #include <netinet6/ip6_var.h>
57fa86ee14Sitojun #include <netinet/icmp6.h>
58287546eaSitojun #include <netinet6/nd6.h>
592ab93df7Sbluhm #include <netinet/udp.h>
602ab93df7Sbluhm #include <netinet/tcp.h>
612ab93df7Sbluhm #include <netinet/tcp_timer.h>
622ab93df7Sbluhm #include <netinet/tcp_var.h>
636f8ff7e4Stodd #ifdef IPSEC
64537b0e2dScedric #include <netinet/ip_ipsp.h>
656f8ff7e4Stodd #include <netinet/ip_ah.h>
666f8ff7e4Stodd #include <netinet/ip_esp.h>
676f8ff7e4Stodd #endif
68287546eaSitojun 
69287546eaSitojun /*
70287546eaSitojun  * Forward a packet.  If some error occurs return the sender
71287546eaSitojun  * an icmp packet.  Note we can't always generate a meaningful
72287546eaSitojun  * icmp message because icmp doesn't have a large enough repertoire
73287546eaSitojun  * of codes and types.
74287546eaSitojun  *
75287546eaSitojun  * If not forwarding, just drop the packet.  This could be confusing
7684b2c343Sbluhm  * if ip6_forwarding was zero but some routing protocol was advancing
77287546eaSitojun  * us as a gateway to somewhere.  However, we must let the routing
78287546eaSitojun  * protocol deal with that.
79287546eaSitojun  *
80287546eaSitojun  */
81287546eaSitojun 
82287546eaSitojun void
83ab457133Sbluhm ip6_forward(struct mbuf *m, struct route *ro, int flags)
84287546eaSitojun {
85287546eaSitojun 	struct ip6_hdr *ip6 = mtod(m, struct ip6_hdr *);
8600dd3069Sbluhm 	struct route iproute;
8700dd3069Sbluhm 	struct rtentry *rt;
8822723314Sbluhm 	struct sockaddr *dst;
89c4e3ba2aSmpi 	struct ifnet *ifp = NULL;
90dd7e0821Sbluhm 	u_int rtableid = m->m_pkthdr.ph_rtableid;
91dd7e0821Sbluhm 	u_int ifidx = m->m_pkthdr.ph_ifidx;
92dd7e0821Sbluhm 	u_int8_t loopcnt = m->m_pkthdr.ph_loopcnt;
93dd7e0821Sbluhm 	u_int icmp_len;
94dd7e0821Sbluhm 	char icmp_buf[MHLEN];
95dd7e0821Sbluhm 	CTASSERT(sizeof(struct ip6_hdr) + sizeof(struct tcphdr) +
96dd7e0821Sbluhm 	    MAX_TCPOPTLEN <= sizeof(icmp_buf));
97dd7e0821Sbluhm 	u_short mflags, pfflags;
982136a888Sbluhm 	struct mbuf *mcopy;
99dd7e0821Sbluhm 	int error = 0, type = 0, code = 0, destmtu = 0;
1006f8ff7e4Stodd #ifdef IPSEC
1014a931851Smarkus 	struct tdb *tdb = NULL;
1026f8ff7e4Stodd #endif /* IPSEC */
103bbcf0337Smpi 	char src6[INET6_ADDRSTRLEN], dst6[INET6_ADDRSTRLEN];
104287546eaSitojun 
1055a4be39aSitojun 	/*
1065a4be39aSitojun 	 * Do not forward packets to multicast destination (should be handled
1075a4be39aSitojun 	 * by ip6_mforward().
1085a4be39aSitojun 	 * Do not forward packets with unspecified source.  It was discussed
1095a4be39aSitojun 	 * in July 2000, on ipngwg mailing list.
1105a4be39aSitojun 	 */
111f4f4d166Sitojun 	if ((m->m_flags & (M_BCAST|M_MCAST)) != 0 ||
1125a4be39aSitojun 	    IN6_IS_ADDR_MULTICAST(&ip6->ip6_dst) ||
1135a4be39aSitojun 	    IN6_IS_ADDR_UNSPECIFIED(&ip6->ip6_src)) {
114d0968898Skn 		time_t uptime;
115d0968898Skn 
11631e14cacSjca 		ip6stat_inc(ip6s_cantforward);
117d0968898Skn 		uptime = getuptime();
118d0968898Skn 
119d0968898Skn 		if (ip6_log_time + ip6_log_interval < uptime) {
120d0968898Skn 			ip6_log_time = uptime;
121bbcf0337Smpi 			inet_ntop(AF_INET6, &ip6->ip6_src, src6, sizeof(src6));
122bbcf0337Smpi 			inet_ntop(AF_INET6, &ip6->ip6_dst, dst6, sizeof(dst6));
123287546eaSitojun 			log(LOG_DEBUG,
124287546eaSitojun 			    "cannot forward "
125ef0d94f6Stb 			    "from %s to %s nxt %d received on interface %u\n",
126dd7e0821Sbluhm 			    src6, dst6, ip6->ip6_nxt, ifidx);
127287546eaSitojun 		}
128287546eaSitojun 		m_freem(m);
1292136a888Sbluhm 		goto done;
130287546eaSitojun 	}
131287546eaSitojun 
132287546eaSitojun 	if (ip6->ip6_hlim <= IPV6_HLIMDEC) {
133287546eaSitojun 		icmp6_error(m, ICMP6_TIME_EXCEEDED,
134287546eaSitojun 				ICMP6_TIME_EXCEED_TRANSIT, 0);
1352136a888Sbluhm 		goto done;
136287546eaSitojun 	}
137287546eaSitojun 	ip6->ip6_hlim -= IPV6_HLIMDEC;
138287546eaSitojun 
139e38fb131Sdlg 	/*
140e38fb131Sdlg 	 * Save at most ICMPV6_PLD_MAXLEN (= the min IPv6 MTU -
141e38fb131Sdlg 	 * size of IPv6 + ICMPv6 headers) bytes of the packet in case
142e38fb131Sdlg 	 * we need to generate an ICMP6 message to the src.
143e38fb131Sdlg 	 * Thanks to M_EXT, in most cases copy will not occur.
144dd7e0821Sbluhm 	 * For small packets copy original onto stack instead of mbuf.
145e38fb131Sdlg 	 *
146cf045b76Sbluhm 	 * For final protocol header like TCP or UDP, full header chain in
147cf045b76Sbluhm 	 * ICMP6 packet is not necessary.  In this case only copy small
148cf045b76Sbluhm 	 * part of original packet and save it on stack instead of mbuf.
149cf045b76Sbluhm 	 * Although this violates RFC 4443 2.4. (c), it avoids additional
150cf045b76Sbluhm 	 * mbuf allocations.  Also pf nat and rdr do not affect the shared
151cf045b76Sbluhm 	 * mbuf cluster.
152cf045b76Sbluhm 	 *
153e38fb131Sdlg 	 * It is important to save it before IPsec processing as IPsec
154e38fb131Sdlg 	 * processing may modify the mbuf.
155e38fb131Sdlg 	 */
156cf045b76Sbluhm 	switch (ip6->ip6_nxt) {
157cf045b76Sbluhm 	case IPPROTO_TCP:
158cf045b76Sbluhm 		icmp_len = sizeof(struct ip6_hdr) + sizeof(struct tcphdr) +
159cf045b76Sbluhm 		    MAX_TCPOPTLEN;
160cf045b76Sbluhm 		break;
161cf045b76Sbluhm 	case IPPROTO_UDP:
162cf045b76Sbluhm 		icmp_len = sizeof(struct ip6_hdr) + sizeof(struct udphdr);
163cf045b76Sbluhm 		break;
164cf045b76Sbluhm 	case IPPROTO_ESP:
165cf045b76Sbluhm 		icmp_len = sizeof(struct ip6_hdr) + 2 * sizeof(u_int32_t);
166cf045b76Sbluhm 		break;
167cf045b76Sbluhm 	default:
168cf045b76Sbluhm 		icmp_len = ICMPV6_PLD_MAXLEN;
169cf045b76Sbluhm 		break;
170cf045b76Sbluhm 	}
171cf045b76Sbluhm 	if (icmp_len > m->m_pkthdr.len)
172cf045b76Sbluhm 		icmp_len = m->m_pkthdr.len;
173dd7e0821Sbluhm 	if (icmp_len <= sizeof(icmp_buf)) {
174dd7e0821Sbluhm 		mflags = m->m_flags;
175dd7e0821Sbluhm 		pfflags = m->m_pkthdr.pf.flags;
176dd7e0821Sbluhm 		m_copydata(m, 0, icmp_len, icmp_buf);
177dd7e0821Sbluhm 		mcopy = NULL;
178dd7e0821Sbluhm 	} else {
179dd7e0821Sbluhm 		mcopy = m_copym(m, 0, icmp_len, M_NOWAIT);
180dd7e0821Sbluhm 		icmp_len = 0;
181dd7e0821Sbluhm 	}
182e38fb131Sdlg 
183f1638404Sclaudio #if NPF > 0
184f1638404Sclaudio reroute:
185f1638404Sclaudio #endif
186f1638404Sclaudio 
1876f8ff7e4Stodd #ifdef IPSEC
1884a931851Smarkus 	if (ipsec_in_use) {
189e7111b64Sbluhm 		error = ip6_output_ipsec_lookup(m, NULL, &tdb);
190e7111b64Sbluhm 		if (error) {
1916f8ff7e4Stodd 			/*
1926f8ff7e4Stodd 			 * -EINVAL is used to indicate that the packet should
1936f8ff7e4Stodd 			 * be silently dropped, typically because we've asked
1946f8ff7e4Stodd 			 * key management for an SA.
1956f8ff7e4Stodd 			 */
1966f8ff7e4Stodd 			if (error == -EINVAL) /* Should silently drop packet */
1976f8ff7e4Stodd 				error = 0;
1986f8ff7e4Stodd 
1998e48d76aSgerhard 			m_freem(m);
2006f8ff7e4Stodd 			goto freecopy;
2016f8ff7e4Stodd 		}
2026f8ff7e4Stodd 	}
2036f8ff7e4Stodd #endif /* IPSEC */
2046f8ff7e4Stodd 
20500dd3069Sbluhm 	if (ro == NULL) {
20600dd3069Sbluhm 		ro = &iproute;
20700dd3069Sbluhm 		ro->ro_rt = NULL;
20800dd3069Sbluhm 	}
20900dd3069Sbluhm 	rt = route6_mpath(ro, &ip6->ip6_dst, &ip6->ip6_src,
210d1e0275eSmpi 	    m->m_pkthdr.ph_rtableid);
211d1e0275eSmpi 	if (rt == NULL) {
21231e14cacSjca 		ip6stat_inc(ip6s_noroute);
213dd7e0821Sbluhm 		type = ICMP6_DST_UNREACH;
214dd7e0821Sbluhm 		code = ICMP6_DST_UNREACH_NOROUTE;
215287546eaSitojun 		m_freem(m);
216dd7e0821Sbluhm 		goto icmperror;
217287546eaSitojun 	}
21800dd3069Sbluhm 	dst = &ro->ro_dstsa;
219f4f4d166Sitojun 
220f4f4d166Sitojun 	/*
221f4f4d166Sitojun 	 * Scope check: if a packet can't be delivered to its destination
222f4f4d166Sitojun 	 * for the reason that the destination is beyond the scope of the
223f4f4d166Sitojun 	 * source address, discard the packet and return an icmp6 destination
224f4f4d166Sitojun 	 * unreachable error with Code 2 (beyond scope of source address).
225f4f4d166Sitojun 	 * [draft-ietf-ipngwg-icmp-v3-00.txt, Section 3.1]
226f4f4d166Sitojun 	 */
227dd7e0821Sbluhm 	if (in6_addr2scopeid(ifidx, &ip6->ip6_src) !=
22828112c65Smpi 	    in6_addr2scopeid(rt->rt_ifidx, &ip6->ip6_src)) {
229d0968898Skn 		time_t uptime;
230d0968898Skn 
23131e14cacSjca 		ip6stat_inc(ip6s_cantforward);
23231e14cacSjca 		ip6stat_inc(ip6s_badscope);
233d0968898Skn 		uptime = getuptime();
234f4f4d166Sitojun 
235d0968898Skn 		if (ip6_log_time + ip6_log_interval < uptime) {
236d0968898Skn 			ip6_log_time = uptime;
237bbcf0337Smpi 			inet_ntop(AF_INET6, &ip6->ip6_src, src6, sizeof(src6));
238bbcf0337Smpi 			inet_ntop(AF_INET6, &ip6->ip6_dst, dst6, sizeof(dst6));
239f4f4d166Sitojun 			log(LOG_DEBUG,
240f4f4d166Sitojun 			    "cannot forward "
241fb492c37Smpi 			    "src %s, dst %s, nxt %d, rcvif %u, outif %u\n",
242dd7e0821Sbluhm 			    src6, dst6, ip6->ip6_nxt, ifidx, rt->rt_ifidx);
243f4f4d166Sitojun 		}
244dd7e0821Sbluhm 		type = ICMP6_DST_UNREACH;
245dd7e0821Sbluhm 		code = ICMP6_DST_UNREACH_BEYONDSCOPE;
246f4f4d166Sitojun 		m_freem(m);
247dd7e0821Sbluhm 		goto icmperror;
248f4f4d166Sitojun 	}
249f4f4d166Sitojun 
2506f8ff7e4Stodd #ifdef IPSEC
2516f8ff7e4Stodd 	/*
2526f8ff7e4Stodd 	 * Check if the packet needs encapsulation.
2536f8ff7e4Stodd 	 * ipsp_process_packet will never come back to here.
2546f8ff7e4Stodd 	 */
2554a931851Smarkus 	if (tdb != NULL) {
2566f8ff7e4Stodd 		/* Callee frees mbuf */
25700dd3069Sbluhm 		error = ip6_output_ipsec_send(tdb, m, ro, 0, 1);
25800dd3069Sbluhm 		rt = ro->ro_rt;
2594a931851Smarkus 		if (error)
2604a931851Smarkus 			goto senderr;
2614a931851Smarkus 		goto freecopy;
2626f8ff7e4Stodd 	}
2636f8ff7e4Stodd #endif /* IPSEC */
2646f8ff7e4Stodd 
265287546eaSitojun 	if (rt->rt_flags & RTF_GATEWAY)
26622723314Sbluhm 		dst = rt->rt_gateway;
267287546eaSitojun 
268287546eaSitojun 	/*
269287546eaSitojun 	 * If we are to forward the packet using the same interface
270287546eaSitojun 	 * as one we got the packet from, perhaps we should send a redirect
271287546eaSitojun 	 * to sender to shortcut a hop.
272287546eaSitojun 	 * Only send redirect if source is sending directly to us,
273287546eaSitojun 	 * and if packet was not source routed (or has any options).
274287546eaSitojun 	 * Also, don't send redirect if forwarding using a route
275287546eaSitojun 	 * modified by a redirect.
276287546eaSitojun 	 */
277c4e3ba2aSmpi 	ifp = if_get(rt->rt_ifidx);
2782e310992Smpi 	if (ifp == NULL) {
2792e310992Smpi 		m_freem(m);
280c0384984Smpi 		goto freecopy;
2812e310992Smpi 	}
282dd7e0821Sbluhm 	if (rt->rt_ifidx == ifidx &&
2839c4c1a6fSbluhm 	    !ISSET(rt->rt_flags, RTF_DYNAMIC|RTF_MODIFIED) &&
2849c4c1a6fSbluhm 	    !ISSET(flags, IPV6_REDIRECT) &&
2859c4c1a6fSbluhm 	    atomic_load_int(&ip6_sendredirects)) {
286c4e3ba2aSmpi 		if ((ifp->if_flags & IFF_POINTOPOINT) &&
28700dd3069Sbluhm 		    nd6_is_addr_neighbor(&ro->ro_dstsin6, ifp)) {
2886b28ab66Sitojun 			/*
2896b28ab66Sitojun 			 * If the incoming interface is equal to the outgoing
2909fd6ccffSitojun 			 * one, the link attached to the interface is
2919fd6ccffSitojun 			 * point-to-point, and the IPv6 destination is
2929fd6ccffSitojun 			 * regarded as on-link on the link, then it will be
2939fd6ccffSitojun 			 * highly probable that the destination address does
2949fd6ccffSitojun 			 * not exist on the link and that the packet is going
2959fd6ccffSitojun 			 * to loop.  Thus, we immediately drop the packet and
2969fd6ccffSitojun 			 * send an ICMPv6 error message.
2979fd6ccffSitojun 			 * For other routing loops, we dare to let the packet
2989fd6ccffSitojun 			 * go to the loop, so that a remote diagnosing host
2999fd6ccffSitojun 			 * can detect the loop by traceroute.
3006b28ab66Sitojun 			 * type/code is based on suggestion by Rich Draves.
3016b28ab66Sitojun 			 * not sure if it is the best pick.
3026b28ab66Sitojun 			 */
303dd7e0821Sbluhm 			type = ICMP6_DST_UNREACH;
304dd7e0821Sbluhm 			code = ICMP6_DST_UNREACH_ADDR;
3056b28ab66Sitojun 			m_freem(m);
306dd7e0821Sbluhm 			goto icmperror;
3076b28ab66Sitojun 		}
308287546eaSitojun 		type = ND_REDIRECT;
3096b28ab66Sitojun 	}
310287546eaSitojun 
3116afad192Sitojun 	/*
3126afad192Sitojun 	 * Fake scoped addresses. Note that even link-local source or
313df8d9afdSjsg 	 * destination can appear, if the originating node just sends the
3146afad192Sitojun 	 * packet to us (without address resolution for the destination).
3156afad192Sitojun 	 * Since both icmp6_error and icmp6_redirect_output fill the embedded
316841d7adbSitojun 	 * link identifiers, we can do this stuff after making a copy for
317841d7adbSitojun 	 * returning an error.
3186afad192Sitojun 	 */
319d4070096Sitojun 	if (IN6_IS_SCOPE_EMBED(&ip6->ip6_src))
3206afad192Sitojun 		ip6->ip6_src.s6_addr16[1] = 0;
321d4070096Sitojun 	if (IN6_IS_SCOPE_EMBED(&ip6->ip6_dst))
3226afad192Sitojun 		ip6->ip6_dst.s6_addr16[1] = 0;
3236afad192Sitojun 
3240f2babb4Sjasoni #if NPF > 0
325c4e3ba2aSmpi 	if (pf_test(AF_INET6, PF_FWD, ifp, &m) != PF_PASS) {
3260f2babb4Sjasoni 		m_freem(m);
3270f2babb4Sjasoni 		goto senderr;
3280f2babb4Sjasoni 	}
329cb3a4e31Sjasoni 	if (m == NULL)
330cb3a4e31Sjasoni 		goto senderr;
331261474c2Sdhartmei 	ip6 = mtod(m, struct ip6_hdr *);
332f1638404Sclaudio 	if ((m->m_pkthdr.pf.flags & (PF_TAG_REROUTE | PF_TAG_GENERATED)) ==
333f1638404Sclaudio 	    (PF_TAG_REROUTE | PF_TAG_GENERATED)) {
334f1638404Sclaudio 		/* already rerun the route lookup, go on */
335f1638404Sclaudio 		m->m_pkthdr.pf.flags &= ~(PF_TAG_GENERATED | PF_TAG_REROUTE);
336f1638404Sclaudio 	} else if (m->m_pkthdr.pf.flags & PF_TAG_REROUTE) {
337f1638404Sclaudio 		/* tag as generated to skip over pf_test on rerun */
338f1638404Sclaudio 		m->m_pkthdr.pf.flags |= PF_TAG_GENERATED;
339ab457133Sbluhm 		SET(flags, IPV6_REDIRECT);
34000dd3069Sbluhm 		if (ro == &iproute)
34100dd3069Sbluhm 			rtfree(ro->ro_rt);
34200dd3069Sbluhm 		ro = NULL;
343c4e3ba2aSmpi 		if_put(ifp);
344c4e3ba2aSmpi 		ifp = NULL;
345f1638404Sclaudio 		goto reroute;
346f1638404Sclaudio 	}
3470f2babb4Sjasoni #endif
348f3378d78Sjan 
34928c60e63Sbluhm #ifdef IPSEC
35028c60e63Sbluhm 	if (ISSET(flags, IPV6_FORWARDING) &&
35128c60e63Sbluhm 	    ISSET(flags, IPV6_FORWARDING_IPSEC) &&
35228c60e63Sbluhm 	    !ISSET(m->m_pkthdr.ph_tagsset, PACKET_TAG_IPSEC_IN_DONE)) {
35328c60e63Sbluhm 		error = EHOSTUNREACH;
35428c60e63Sbluhm 		goto senderr;
35528c60e63Sbluhm 	}
35628c60e63Sbluhm #endif
35728c60e63Sbluhm 
35822723314Sbluhm 	error = if_output_tso(ifp, &m, dst, rt, ifp->if_mtu);
3594d7caa4bSbluhm 	if (error)
3604d7caa4bSbluhm 		ip6stat_inc(ip6s_cantforward);
3614d7caa4bSbluhm 	else if (m == NULL)
3624d7caa4bSbluhm 		ip6stat_inc(ip6s_forward);
363f3378d78Sjan 	if (error || m == NULL)
3644d7caa4bSbluhm 		goto senderr;
3650f2babb4Sjasoni 
366dd7e0821Sbluhm 	type = ICMP6_PACKET_TOO_BIG;
367dd7e0821Sbluhm 	destmtu = ifp->if_mtu;
3684d7caa4bSbluhm 	m_freem(m);
369dd7e0821Sbluhm 	goto icmperror;
3704d7caa4bSbluhm 
3710f2babb4Sjasoni senderr:
372dd7e0821Sbluhm 	if (mcopy == NULL && icmp_len == 0)
3732136a888Sbluhm 		goto done;
374a380976cSbluhm 
375287546eaSitojun 	switch (error) {
376287546eaSitojun 	case 0:
377287546eaSitojun 		if (type == ND_REDIRECT) {
378dd7e0821Sbluhm 			if (icmp_len != 0) {
379dd7e0821Sbluhm 				mcopy = m_gethdr(M_DONTWAIT, MT_DATA);
380dd7e0821Sbluhm 				if (mcopy == NULL)
381dd7e0821Sbluhm 					goto done;
382dd7e0821Sbluhm 				mcopy->m_len = mcopy->m_pkthdr.len = icmp_len;
383dd7e0821Sbluhm 				mcopy->m_flags |= (mflags & M_COPYFLAGS);
384dd7e0821Sbluhm 				mcopy->m_pkthdr.ph_rtableid = rtableid;
385dd7e0821Sbluhm 				mcopy->m_pkthdr.ph_ifidx = ifidx;
386dd7e0821Sbluhm 				mcopy->m_pkthdr.ph_loopcnt = loopcnt;
387dd7e0821Sbluhm 				mcopy->m_pkthdr.pf.flags |=
388dd7e0821Sbluhm 				    (pfflags & PF_TAG_GENERATED);
389dd7e0821Sbluhm 				memcpy(mcopy->m_data, icmp_buf, icmp_len);
390dd7e0821Sbluhm 			}
391dd7e0821Sbluhm 			if (mcopy != NULL) {
392287546eaSitojun 				icmp6_redirect_output(mcopy, rt);
3934d7caa4bSbluhm 				ip6stat_inc(ip6s_redirectsent);
394dd7e0821Sbluhm 			}
3952136a888Sbluhm 			goto done;
396287546eaSitojun 		}
397287546eaSitojun 		goto freecopy;
398287546eaSitojun 
399287546eaSitojun 	case EMSGSIZE:
400a380976cSbluhm 		type = ICMP6_PACKET_TOO_BIG;
401a380976cSbluhm 		if (rt != NULL) {
402*84d9c64aSbluhm 			u_int rtmtu;
403*84d9c64aSbluhm 
404*84d9c64aSbluhm 			rtmtu = atomic_load_int(&rt->rt_mtu);
405*84d9c64aSbluhm 			if (rtmtu != 0) {
406*84d9c64aSbluhm 				destmtu = rtmtu;
407a380976cSbluhm 			} else {
408a380976cSbluhm 				struct ifnet *destifp;
409a380976cSbluhm 
410a380976cSbluhm 				destifp = if_get(rt->rt_ifidx);
411a380976cSbluhm 				if (destifp != NULL)
412a380976cSbluhm 					destmtu = destifp->if_mtu;
413a380976cSbluhm 				if_put(destifp);
414a380976cSbluhm 			}
415a380976cSbluhm 		}
416a380976cSbluhm 		ip6stat_inc(ip6s_cantfrag);
417a380976cSbluhm 		if (destmtu == 0)
418a380976cSbluhm 			goto freecopy;
419a380976cSbluhm 		break;
420a380976cSbluhm 
421a380976cSbluhm 	case EACCES:
422a380976cSbluhm 		/*
423a380976cSbluhm 		 * pf(4) blocked the packet. There is no need to send an ICMP
424a380976cSbluhm 		 * packet back since pf(4) takes care of it.
425a380976cSbluhm 		 */
426287546eaSitojun 		goto freecopy;
427287546eaSitojun 
428287546eaSitojun 	case ENOBUFS:
429287546eaSitojun 		/* Tell source to slow down like source quench in IP? */
430287546eaSitojun 		goto freecopy;
431287546eaSitojun 
432287546eaSitojun 	case ENETUNREACH:	/* shouldn't happen, checked above */
433287546eaSitojun 	case EHOSTUNREACH:
434287546eaSitojun 	case ENETDOWN:
435287546eaSitojun 	case EHOSTDOWN:
436287546eaSitojun 	default:
437287546eaSitojun 		type = ICMP6_DST_UNREACH;
438287546eaSitojun 		code = ICMP6_DST_UNREACH_ADDR;
439287546eaSitojun 		break;
440287546eaSitojun 	}
441dd7e0821Sbluhm  icmperror:
442dd7e0821Sbluhm 	if (icmp_len != 0) {
443dd7e0821Sbluhm 		mcopy = m_gethdr(M_DONTWAIT, MT_DATA);
444dd7e0821Sbluhm 		if (mcopy == NULL)
445dd7e0821Sbluhm 			goto done;
446dd7e0821Sbluhm 		mcopy->m_len = mcopy->m_pkthdr.len = icmp_len;
447dd7e0821Sbluhm 		mcopy->m_flags |= (mflags & M_COPYFLAGS);
448dd7e0821Sbluhm 		mcopy->m_pkthdr.ph_rtableid = rtableid;
449dd7e0821Sbluhm 		mcopy->m_pkthdr.ph_ifidx = ifidx;
450dd7e0821Sbluhm 		mcopy->m_pkthdr.ph_loopcnt = loopcnt;
451dd7e0821Sbluhm 		mcopy->m_pkthdr.pf.flags |= (pfflags & PF_TAG_GENERATED);
452dd7e0821Sbluhm 		memcpy(mcopy->m_data, icmp_buf, icmp_len);
453dd7e0821Sbluhm 	}
454dd7e0821Sbluhm 	if (mcopy != NULL)
455a380976cSbluhm 		icmp6_error(mcopy, type, code, destmtu);
4562136a888Sbluhm 	goto done;
457287546eaSitojun 
458287546eaSitojun  freecopy:
459287546eaSitojun 	m_freem(mcopy);
4602136a888Sbluhm  done:
46100dd3069Sbluhm 	if (ro == &iproute)
46200dd3069Sbluhm 		rtfree(ro->ro_rt);
463c4e3ba2aSmpi 	if_put(ifp);
46431a6915fSbluhm #ifdef IPSEC
46531a6915fSbluhm 	tdb_unref(tdb);
46631a6915fSbluhm #endif /* IPSEC */
467287546eaSitojun }
468