xref: /freebsd/sys/netinet6/ip6_fastfwd.c (revision 40faf87894ff67ffdf8126fce9bb438ddf61a26f)
15a1842a2SAndrey V. Elsukov /*-
25a1842a2SAndrey V. Elsukov  * Copyright (c) 2014-2016 Andrey V. Elsukov <ae@FreeBSD.org>
35a1842a2SAndrey V. Elsukov  * All rights reserved.
45a1842a2SAndrey V. Elsukov  *
55a1842a2SAndrey V. Elsukov  * Redistribution and use in source and binary forms, with or without
65a1842a2SAndrey V. Elsukov  * modification, are permitted provided that the following conditions
75a1842a2SAndrey V. Elsukov  * are met:
85a1842a2SAndrey V. Elsukov  *
95a1842a2SAndrey V. Elsukov  * 1. Redistributions of source code must retain the above copyright
105a1842a2SAndrey V. Elsukov  *    notice, this list of conditions and the following disclaimer.
115a1842a2SAndrey V. Elsukov  * 2. Redistributions in binary form must reproduce the above copyright
125a1842a2SAndrey V. Elsukov  *    notice, this list of conditions and the following disclaimer in the
135a1842a2SAndrey V. Elsukov  *    documentation and/or other materials provided with the distribution.
145a1842a2SAndrey V. Elsukov  *
155a1842a2SAndrey V. Elsukov  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
165a1842a2SAndrey V. Elsukov  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
175a1842a2SAndrey V. Elsukov  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
185a1842a2SAndrey V. Elsukov  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
195a1842a2SAndrey V. Elsukov  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
205a1842a2SAndrey V. Elsukov  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
215a1842a2SAndrey V. Elsukov  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
225a1842a2SAndrey V. Elsukov  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
235a1842a2SAndrey V. Elsukov  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
245a1842a2SAndrey V. Elsukov  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
255a1842a2SAndrey V. Elsukov  */
265a1842a2SAndrey V. Elsukov 
275a1842a2SAndrey V. Elsukov #include <sys/cdefs.h>
285a1842a2SAndrey V. Elsukov #include "opt_inet6.h"
295a1842a2SAndrey V. Elsukov #include "opt_ipstealth.h"
305a1842a2SAndrey V. Elsukov 
315a1842a2SAndrey V. Elsukov #include <sys/param.h>
325a1842a2SAndrey V. Elsukov #include <sys/systm.h>
335a1842a2SAndrey V. Elsukov #include <sys/mbuf.h>
345a1842a2SAndrey V. Elsukov #include <sys/socket.h>
355a1842a2SAndrey V. Elsukov #include <sys/kernel.h>
365a1842a2SAndrey V. Elsukov #include <sys/sysctl.h>
375a1842a2SAndrey V. Elsukov 
385a1842a2SAndrey V. Elsukov #include <net/if.h>
395a1842a2SAndrey V. Elsukov #include <net/if_var.h>
403d0d5b21SJustin Hibbits #include <net/if_private.h>
415a1842a2SAndrey V. Elsukov #include <net/route.h>
429ac7c6cfSAlexander V. Chernikov #include <net/route/nhop.h>
435a1842a2SAndrey V. Elsukov #include <net/pfil.h>
445a1842a2SAndrey V. Elsukov #include <net/vnet.h>
455a1842a2SAndrey V. Elsukov 
465a1842a2SAndrey V. Elsukov #include <netinet/in.h>
475a1842a2SAndrey V. Elsukov #include <netinet/in_kdtrace.h>
485a1842a2SAndrey V. Elsukov #include <netinet/in_var.h>
495a1842a2SAndrey V. Elsukov #include <netinet/ip_var.h>
505a1842a2SAndrey V. Elsukov #include <netinet/ip6.h>
515a1842a2SAndrey V. Elsukov #include <netinet/icmp6.h>
525a1842a2SAndrey V. Elsukov #include <netinet6/in6_var.h>
535a1842a2SAndrey V. Elsukov #include <netinet6/in6_fib.h>
545a1842a2SAndrey V. Elsukov #include <netinet6/ip6_var.h>
555a1842a2SAndrey V. Elsukov #include <netinet6/nd6.h>
565a1842a2SAndrey V. Elsukov 
575a1842a2SAndrey V. Elsukov static int
589ac7c6cfSAlexander V. Chernikov ip6_findroute(struct nhop_object **pnh, const struct sockaddr_in6 *dst,
595a1842a2SAndrey V. Elsukov     struct mbuf *m)
605a1842a2SAndrey V. Elsukov {
619ac7c6cfSAlexander V. Chernikov 	struct nhop_object *nh;
625a1842a2SAndrey V. Elsukov 
639ac7c6cfSAlexander V. Chernikov 	nh = fib6_lookup(M_GETFIB(m), &dst->sin6_addr,
649ac7c6cfSAlexander V. Chernikov 	    dst->sin6_scope_id, NHR_NONE, m->m_pkthdr.flowid);
659ac7c6cfSAlexander V. Chernikov        if (nh == NULL) {
665a1842a2SAndrey V. Elsukov 		IP6STAT_INC(ip6s_noroute);
675a1842a2SAndrey V. Elsukov 		IP6STAT_INC(ip6s_cantforward);
685a1842a2SAndrey V. Elsukov 		icmp6_error(m, ICMP6_DST_UNREACH,
695a1842a2SAndrey V. Elsukov 		    ICMP6_DST_UNREACH_NOROUTE, 0);
705a1842a2SAndrey V. Elsukov 		return (EHOSTUNREACH);
715a1842a2SAndrey V. Elsukov 	}
729ac7c6cfSAlexander V. Chernikov 	if (nh->nh_flags & NHF_BLACKHOLE) {
735a1842a2SAndrey V. Elsukov 		IP6STAT_INC(ip6s_cantforward);
745a1842a2SAndrey V. Elsukov 		m_freem(m);
755a1842a2SAndrey V. Elsukov 		return (EHOSTUNREACH);
765a1842a2SAndrey V. Elsukov 	}
775a1842a2SAndrey V. Elsukov 
789ac7c6cfSAlexander V. Chernikov 	if (nh->nh_flags & NHF_REJECT) {
795a1842a2SAndrey V. Elsukov 		IP6STAT_INC(ip6s_cantforward);
805a1842a2SAndrey V. Elsukov 		icmp6_error(m, ICMP6_DST_UNREACH,
815a1842a2SAndrey V. Elsukov 		    ICMP6_DST_UNREACH_REJECT, 0);
825a1842a2SAndrey V. Elsukov 		return (EHOSTUNREACH);
835a1842a2SAndrey V. Elsukov 	}
849ac7c6cfSAlexander V. Chernikov 
859ac7c6cfSAlexander V. Chernikov 	*pnh = nh;
869ac7c6cfSAlexander V. Chernikov 
875a1842a2SAndrey V. Elsukov 	return (0);
885a1842a2SAndrey V. Elsukov }
895a1842a2SAndrey V. Elsukov 
905a1842a2SAndrey V. Elsukov struct mbuf*
915a1842a2SAndrey V. Elsukov ip6_tryforward(struct mbuf *m)
925a1842a2SAndrey V. Elsukov {
935a1842a2SAndrey V. Elsukov 	struct sockaddr_in6 dst;
949ac7c6cfSAlexander V. Chernikov 	struct nhop_object *nh;
955a1842a2SAndrey V. Elsukov 	struct m_tag *fwd_tag;
965a1842a2SAndrey V. Elsukov 	struct ip6_hdr *ip6;
975a1842a2SAndrey V. Elsukov 	struct ifnet *rcvif;
985a1842a2SAndrey V. Elsukov 	uint32_t plen;
995a1842a2SAndrey V. Elsukov 	int error;
1005a1842a2SAndrey V. Elsukov 
1015a1842a2SAndrey V. Elsukov 	/*
1025a1842a2SAndrey V. Elsukov 	 * Fallback conditions to ip6_input for slow path processing.
1035a1842a2SAndrey V. Elsukov 	 */
1045a1842a2SAndrey V. Elsukov 	ip6 = mtod(m, struct ip6_hdr *);
10556c989dfSAndrey V. Elsukov 	if ((m->m_flags & (M_BCAST | M_MCAST)) != 0 ||
10656c989dfSAndrey V. Elsukov 	    ip6->ip6_nxt == IPPROTO_HOPOPTS ||
1075a1842a2SAndrey V. Elsukov 	    IN6_IS_ADDR_MULTICAST(&ip6->ip6_dst) ||
1085a1842a2SAndrey V. Elsukov 	    IN6_IS_ADDR_LINKLOCAL(&ip6->ip6_dst) ||
1095a1842a2SAndrey V. Elsukov 	    IN6_IS_ADDR_LINKLOCAL(&ip6->ip6_src) ||
110*40faf878SMark Johnston 	    IN6_IS_ADDR_UNSPECIFIED(&ip6->ip6_dst) ||
1115a1842a2SAndrey V. Elsukov 	    IN6_IS_ADDR_UNSPECIFIED(&ip6->ip6_src) ||
1125a1842a2SAndrey V. Elsukov 	    in6_localip(&ip6->ip6_dst))
1135a1842a2SAndrey V. Elsukov 		return (m);
1145a1842a2SAndrey V. Elsukov 	/*
1155a1842a2SAndrey V. Elsukov 	 * Check that the amount of data in the buffers
1165a1842a2SAndrey V. Elsukov 	 * is as at least much as the IPv6 header would have us expect.
1175a1842a2SAndrey V. Elsukov 	 * Trim mbufs if longer than we expect.
1185a1842a2SAndrey V. Elsukov 	 * Drop packet if shorter than we expect.
1195a1842a2SAndrey V. Elsukov 	 */
1205a1842a2SAndrey V. Elsukov 	rcvif = m->m_pkthdr.rcvif;
1215a1842a2SAndrey V. Elsukov 	plen = ntohs(ip6->ip6_plen);
1225a1842a2SAndrey V. Elsukov 	if (plen == 0) {
1235a1842a2SAndrey V. Elsukov 		/*
1245a1842a2SAndrey V. Elsukov 		 * Jumbograms must have hop-by-hop header and go via
1255a1842a2SAndrey V. Elsukov 		 * slow path.
1265a1842a2SAndrey V. Elsukov 		 */
1275a1842a2SAndrey V. Elsukov 		IP6STAT_INC(ip6s_badoptions);
1285a1842a2SAndrey V. Elsukov 		goto dropin;
1295a1842a2SAndrey V. Elsukov 	}
1305a1842a2SAndrey V. Elsukov 	if (m->m_pkthdr.len - sizeof(struct ip6_hdr) < plen) {
1315a1842a2SAndrey V. Elsukov 		IP6STAT_INC(ip6s_tooshort);
1325a1842a2SAndrey V. Elsukov 		in6_ifstat_inc(rcvif, ifs6_in_truncated);
1335a1842a2SAndrey V. Elsukov 		goto dropin;
1345a1842a2SAndrey V. Elsukov 	}
1355a1842a2SAndrey V. Elsukov 	if (m->m_pkthdr.len > sizeof(struct ip6_hdr) + plen) {
1365a1842a2SAndrey V. Elsukov 		if (m->m_len == m->m_pkthdr.len) {
1375a1842a2SAndrey V. Elsukov 			m->m_len = sizeof(struct ip6_hdr) + plen;
1385a1842a2SAndrey V. Elsukov 			m->m_pkthdr.len = sizeof(struct ip6_hdr) + plen;
1395a1842a2SAndrey V. Elsukov 		} else
1405a1842a2SAndrey V. Elsukov 			m_adj(m, sizeof(struct ip6_hdr) + plen -
1415a1842a2SAndrey V. Elsukov 			    m->m_pkthdr.len);
1425a1842a2SAndrey V. Elsukov 	}
1435a1842a2SAndrey V. Elsukov 
1445a1842a2SAndrey V. Elsukov 	/*
1455a1842a2SAndrey V. Elsukov 	 * Hop limit.
1465a1842a2SAndrey V. Elsukov 	 */
1475a1842a2SAndrey V. Elsukov #ifdef IPSTEALTH
1485a1842a2SAndrey V. Elsukov 	if (!V_ip6stealth)
1495a1842a2SAndrey V. Elsukov #endif
1505a1842a2SAndrey V. Elsukov 	if (ip6->ip6_hlim <= IPV6_HLIMDEC) {
1515a1842a2SAndrey V. Elsukov 		icmp6_error(m, ICMP6_TIME_EXCEEDED,
1525a1842a2SAndrey V. Elsukov 		    ICMP6_TIME_EXCEED_TRANSIT, 0);
1535a1842a2SAndrey V. Elsukov 		m = NULL;
1545a1842a2SAndrey V. Elsukov 		goto dropin;
1555a1842a2SAndrey V. Elsukov 	}
1565a1842a2SAndrey V. Elsukov 
1575a1842a2SAndrey V. Elsukov 	bzero(&dst, sizeof(dst));
1585a1842a2SAndrey V. Elsukov 	dst.sin6_family = AF_INET6;
1595a1842a2SAndrey V. Elsukov 	dst.sin6_len = sizeof(dst);
1605a1842a2SAndrey V. Elsukov 	dst.sin6_addr = ip6->ip6_dst;
1615a1842a2SAndrey V. Elsukov 
1625a1842a2SAndrey V. Elsukov 	/*
1635a1842a2SAndrey V. Elsukov 	 * Incoming packet firewall processing.
1645a1842a2SAndrey V. Elsukov 	 */
165b252313fSGleb Smirnoff 	if (!PFIL_HOOKED_IN(V_inet6_pfil_head))
1665a1842a2SAndrey V. Elsukov 		goto passin;
167dda6376bSMateusz Guzik 	if (pfil_mbuf_in(V_inet6_pfil_head, &m, rcvif, NULL) !=
168b252313fSGleb Smirnoff 	    PFIL_PASS)
1695a1842a2SAndrey V. Elsukov 		goto dropin;
1705a1842a2SAndrey V. Elsukov 	/*
1715a1842a2SAndrey V. Elsukov 	 * If packet filter sets the M_FASTFWD_OURS flag, this means
1725a1842a2SAndrey V. Elsukov 	 * that new destination or next hop is our local address.
1735a1842a2SAndrey V. Elsukov 	 * So, we can just go back to ip6_input.
1745a1842a2SAndrey V. Elsukov 	 * XXX: should we decrement ip6_hlim in such case?
1755a1842a2SAndrey V. Elsukov 	 *
1765a1842a2SAndrey V. Elsukov 	 * Also it can forward packet to another destination, e.g.
1775a1842a2SAndrey V. Elsukov 	 * M_IP6_NEXTHOP flag is set and fwd_tag is attached to mbuf.
1785a1842a2SAndrey V. Elsukov 	 */
1795a1842a2SAndrey V. Elsukov 	if (m->m_flags & M_FASTFWD_OURS)
1805a1842a2SAndrey V. Elsukov 		return (m);
1815a1842a2SAndrey V. Elsukov 
1825a1842a2SAndrey V. Elsukov 	ip6 = mtod(m, struct ip6_hdr *);
1835a1842a2SAndrey V. Elsukov 	if ((m->m_flags & M_IP6_NEXTHOP) &&
1845a1842a2SAndrey V. Elsukov 	    (fwd_tag = m_tag_find(m, PACKET_TAG_IPFORWARD, NULL)) != NULL) {
1855a1842a2SAndrey V. Elsukov 		/*
1865a1842a2SAndrey V. Elsukov 		 * Now we will find route to forwarded by pfil destination.
1875a1842a2SAndrey V. Elsukov 		 */
1885a1842a2SAndrey V. Elsukov 		bcopy((fwd_tag + 1), &dst, sizeof(dst));
1895a1842a2SAndrey V. Elsukov 		m->m_flags &= ~M_IP6_NEXTHOP;
1905a1842a2SAndrey V. Elsukov 		m_tag_delete(m, fwd_tag);
1915a1842a2SAndrey V. Elsukov 	} else {
1925a1842a2SAndrey V. Elsukov 		/* Update dst since pfil could change it */
1935a1842a2SAndrey V. Elsukov 		dst.sin6_addr = ip6->ip6_dst;
1945a1842a2SAndrey V. Elsukov 	}
1955a1842a2SAndrey V. Elsukov passin:
1965a1842a2SAndrey V. Elsukov 	/*
1975a1842a2SAndrey V. Elsukov 	 * Find route to destination.
1985a1842a2SAndrey V. Elsukov 	 */
1995a1842a2SAndrey V. Elsukov 	if (ip6_findroute(&nh, &dst, m) != 0) {
2005a1842a2SAndrey V. Elsukov 		m = NULL;
2015a1842a2SAndrey V. Elsukov 		in6_ifstat_inc(rcvif, ifs6_in_noroute);
2025a1842a2SAndrey V. Elsukov 		goto dropin;
2035a1842a2SAndrey V. Elsukov 	}
204b252313fSGleb Smirnoff 	if (!PFIL_HOOKED_OUT(V_inet6_pfil_head)) {
2059ac7c6cfSAlexander V. Chernikov 		if (m->m_pkthdr.len > nh->nh_mtu) {
2069ac7c6cfSAlexander V. Chernikov 			in6_ifstat_inc(nh->nh_ifp, ifs6_in_toobig);
2079ac7c6cfSAlexander V. Chernikov 			icmp6_error(m, ICMP6_PACKET_TOO_BIG, 0, nh->nh_mtu);
208f9656ee6SMichael Tuexen 			m = NULL;
209f9656ee6SMichael Tuexen 			goto dropout;
210f9656ee6SMichael Tuexen 		}
211f9656ee6SMichael Tuexen 		goto passout;
212f9656ee6SMichael Tuexen 	}
213a0bf3ee4SKristof Provost 
214a0bf3ee4SKristof Provost 	/*
215a0bf3ee4SKristof Provost 	 * Outgoing packet firewall processing.
216a0bf3ee4SKristof Provost 	 */
217dda6376bSMateusz Guzik 	if (pfil_mbuf_out(V_inet6_pfil_head, &m, nh->nh_ifp,
21814c9a2dbSMateusz Guzik 	    NULL) != PFIL_PASS)
219a0bf3ee4SKristof Provost 		goto dropout;
220a0bf3ee4SKristof Provost 
2215a1842a2SAndrey V. Elsukov 	/*
2225a1842a2SAndrey V. Elsukov 	 * We used slow path processing for packets with scoped addresses.
2235a1842a2SAndrey V. Elsukov 	 * So, scope checks aren't needed here.
2245a1842a2SAndrey V. Elsukov 	 */
2259ac7c6cfSAlexander V. Chernikov 	if (m->m_pkthdr.len > nh->nh_mtu) {
2269ac7c6cfSAlexander V. Chernikov 		in6_ifstat_inc(nh->nh_ifp, ifs6_in_toobig);
2279ac7c6cfSAlexander V. Chernikov 		icmp6_error(m, ICMP6_PACKET_TOO_BIG, 0, nh->nh_mtu);
2285a1842a2SAndrey V. Elsukov 		m = NULL;
2295a1842a2SAndrey V. Elsukov 		goto dropout;
2305a1842a2SAndrey V. Elsukov 	}
2315a1842a2SAndrey V. Elsukov 
2325a1842a2SAndrey V. Elsukov 	/*
2335a1842a2SAndrey V. Elsukov 	 * If packet filter sets the M_FASTFWD_OURS flag, this means
2345a1842a2SAndrey V. Elsukov 	 * that new destination or next hop is our local address.
2355a1842a2SAndrey V. Elsukov 	 * So, we can just go back to ip6_input.
2365a1842a2SAndrey V. Elsukov 	 *
2375a1842a2SAndrey V. Elsukov 	 * Also it can forward packet to another destination, e.g.
2385a1842a2SAndrey V. Elsukov 	 * M_IP6_NEXTHOP flag is set and fwd_tag is attached to mbuf.
2395a1842a2SAndrey V. Elsukov 	 */
2405a1842a2SAndrey V. Elsukov 	if (m->m_flags & M_FASTFWD_OURS) {
2415a1842a2SAndrey V. Elsukov 		/*
2425a1842a2SAndrey V. Elsukov 		 * XXX: we did one hop and should decrement hop limit. But
2435a1842a2SAndrey V. Elsukov 		 * now we are the destination and just don't pay attention.
2445a1842a2SAndrey V. Elsukov 		 */
2455a1842a2SAndrey V. Elsukov 		return (m);
2465a1842a2SAndrey V. Elsukov 	}
2475a1842a2SAndrey V. Elsukov 	/*
2485a1842a2SAndrey V. Elsukov 	 * Again. A packet filter could change the destination address.
2495a1842a2SAndrey V. Elsukov 	 */
2505a1842a2SAndrey V. Elsukov 	ip6 = mtod(m, struct ip6_hdr *);
2515a1842a2SAndrey V. Elsukov 	if (m->m_flags & M_IP6_NEXTHOP)
2525a1842a2SAndrey V. Elsukov 		fwd_tag = m_tag_find(m, PACKET_TAG_IPFORWARD, NULL);
2535a1842a2SAndrey V. Elsukov 	else
2545a1842a2SAndrey V. Elsukov 		fwd_tag = NULL;
2555a1842a2SAndrey V. Elsukov 
2565a1842a2SAndrey V. Elsukov 	if (fwd_tag != NULL ||
2575a1842a2SAndrey V. Elsukov 	    !IN6_ARE_ADDR_EQUAL(&dst.sin6_addr, &ip6->ip6_dst)) {
2585a1842a2SAndrey V. Elsukov 		if (fwd_tag != NULL) {
2595a1842a2SAndrey V. Elsukov 			bcopy((fwd_tag + 1), &dst, sizeof(dst));
2605a1842a2SAndrey V. Elsukov 			m->m_flags &= ~M_IP6_NEXTHOP;
2615a1842a2SAndrey V. Elsukov 			m_tag_delete(m, fwd_tag);
2625a1842a2SAndrey V. Elsukov 		} else
2635a1842a2SAndrey V. Elsukov 			dst.sin6_addr = ip6->ip6_dst;
2645a1842a2SAndrey V. Elsukov 		/*
2655a1842a2SAndrey V. Elsukov 		 * Redo route lookup with new destination address
2665a1842a2SAndrey V. Elsukov 		 */
2675a1842a2SAndrey V. Elsukov 		if (ip6_findroute(&nh, &dst, m) != 0) {
2685a1842a2SAndrey V. Elsukov 			m = NULL;
2695a1842a2SAndrey V. Elsukov 			goto dropout;
2705a1842a2SAndrey V. Elsukov 		}
2715a1842a2SAndrey V. Elsukov 	}
2725a1842a2SAndrey V. Elsukov passout:
2735a1842a2SAndrey V. Elsukov #ifdef IPSTEALTH
2745a1842a2SAndrey V. Elsukov 	if (!V_ip6stealth)
2755a1842a2SAndrey V. Elsukov #endif
2765a1842a2SAndrey V. Elsukov 	{
2775a1842a2SAndrey V. Elsukov 		ip6->ip6_hlim -= IPV6_HLIMDEC;
2785a1842a2SAndrey V. Elsukov 	}
2795a1842a2SAndrey V. Elsukov 
2805a1842a2SAndrey V. Elsukov 	m_clrprotoflags(m);	/* Avoid confusing lower layers. */
2819ac7c6cfSAlexander V. Chernikov 	IP_PROBE(send, NULL, NULL, ip6, nh->nh_ifp, NULL, ip6);
2825a1842a2SAndrey V. Elsukov 
2839ac7c6cfSAlexander V. Chernikov 	if (nh->nh_flags & NHF_GATEWAY)
2849ac7c6cfSAlexander V. Chernikov 		dst.sin6_addr = nh->gw6_sa.sin6_addr;
2859ac7c6cfSAlexander V. Chernikov 	error = (*nh->nh_ifp->if_output)(nh->nh_ifp, m,
2865a1842a2SAndrey V. Elsukov 	    (struct sockaddr *)&dst, NULL);
2875a1842a2SAndrey V. Elsukov 	if (error != 0) {
2889ac7c6cfSAlexander V. Chernikov 		in6_ifstat_inc(nh->nh_ifp, ifs6_out_discard);
2895a1842a2SAndrey V. Elsukov 		IP6STAT_INC(ip6s_cantforward);
2905a1842a2SAndrey V. Elsukov 	} else {
2919ac7c6cfSAlexander V. Chernikov 		in6_ifstat_inc(nh->nh_ifp, ifs6_out_forward);
2925a1842a2SAndrey V. Elsukov 		IP6STAT_INC(ip6s_forward);
2935a1842a2SAndrey V. Elsukov 	}
2945a1842a2SAndrey V. Elsukov 	return (NULL);
2955a1842a2SAndrey V. Elsukov dropin:
2965a1842a2SAndrey V. Elsukov 	in6_ifstat_inc(rcvif, ifs6_in_discard);
2975a1842a2SAndrey V. Elsukov 	goto drop;
2985a1842a2SAndrey V. Elsukov dropout:
2999ac7c6cfSAlexander V. Chernikov 	in6_ifstat_inc(nh->nh_ifp, ifs6_out_discard);
3005a1842a2SAndrey V. Elsukov drop:
3015a1842a2SAndrey V. Elsukov 	if (m != NULL)
3025a1842a2SAndrey V. Elsukov 		m_freem(m);
3035a1842a2SAndrey V. Elsukov 	return (NULL);
3045a1842a2SAndrey V. Elsukov }
305