ip6_forward.c revision 55009
153541Sshin/*
253541Sshin * Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project.
353541Sshin * All rights reserved.
453541Sshin *
553541Sshin * Redistribution and use in source and binary forms, with or without
653541Sshin * modification, are permitted provided that the following conditions
753541Sshin * are met:
853541Sshin * 1. Redistributions of source code must retain the above copyright
953541Sshin *    notice, this list of conditions and the following disclaimer.
1053541Sshin * 2. Redistributions in binary form must reproduce the above copyright
1153541Sshin *    notice, this list of conditions and the following disclaimer in the
1253541Sshin *    documentation and/or other materials provided with the distribution.
1353541Sshin * 3. Neither the name of the project nor the names of its contributors
1453541Sshin *    may be used to endorse or promote products derived from this software
1553541Sshin *    without specific prior written permission.
1653541Sshin *
1753541Sshin * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND
1853541Sshin * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
1953541Sshin * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
2053541Sshin * ARE DISCLAIMED.  IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE
2153541Sshin * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
2253541Sshin * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
2353541Sshin * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
2453541Sshin * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
2553541Sshin * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
2653541Sshin * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
2753541Sshin * SUCH DAMAGE.
2853541Sshin *
2953541Sshin * $FreeBSD: head/sys/netinet6/ip6_forward.c 55009 1999-12-22 19:13:38Z shin $
3053541Sshin */
3153541Sshin
3255009Sshin#include "opt_ipsec.h"
3353541Sshin
3453541Sshin#include <sys/param.h>
3553541Sshin#include <sys/systm.h>
3653541Sshin#include <sys/malloc.h>
3753541Sshin#include <sys/mbuf.h>
3853541Sshin#include <sys/domain.h>
3953541Sshin#include <sys/protosw.h>
4053541Sshin#include <sys/socket.h>
4153541Sshin#include <sys/errno.h>
4253541Sshin#include <sys/time.h>
4353541Sshin#include <sys/kernel.h>
4453541Sshin#include <sys/syslog.h>
4553541Sshin
4653541Sshin#include <net/if.h>
4753541Sshin#include <net/route.h>
4853541Sshin
4953541Sshin#include <netinet/in.h>
5053541Sshin#include <netinet/in_var.h>
5153541Sshin#include <netinet6/ip6.h>
5253541Sshin#include <netinet6/ip6_var.h>
5353541Sshin#include <netinet6/icmp6.h>
5453541Sshin#include <netinet6/nd6.h>
5553541Sshin
5653541Sshin#ifdef IPSEC_IPV6FWD
5753541Sshin#include <netinet6/ipsec.h>
5853541Sshin#include <netinet6/ipsec6.h>
5953541Sshin#include <netkey/key.h>
6055009Sshin#ifdef IPSEC_DEBUG
6153541Sshin#include <netkey/key_debug.h>
6253541Sshin#else
6355009Sshin#define KEYDEBUG(lev,arg)
6455009Sshin#endif
6553541Sshin#endif /* IPSEC_IPV6FWD */
6653541Sshin
6753541Sshin#ifdef IPV6FIREWALL
6853541Sshin#include <netinet6/ip6_fw.h>
6953541Sshin#endif
7053541Sshin
7153541Sshin#include <net/net_osdep.h>
7253541Sshin
7353541Sshinstruct	route_in6 ip6_forward_rt;
7453541Sshin
7553541Sshin/*
7653541Sshin * Forward a packet.  If some error occurs return the sender
7753541Sshin * an icmp packet.  Note we can't always generate a meaningful
7853541Sshin * icmp message because icmp doesn't have a large enough repertoire
7953541Sshin * of codes and types.
8053541Sshin *
8153541Sshin * If not forwarding, just drop the packet.  This could be confusing
8253541Sshin * if ipforwarding was zero but some routing protocol was advancing
8353541Sshin * us as a gateway to somewhere.  However, we must let the routing
8453541Sshin * protocol deal with that.
8553541Sshin *
8653541Sshin */
8753541Sshin
8853541Sshinvoid
8953541Sshinip6_forward(m, srcrt)
9053541Sshin	struct mbuf *m;
9153541Sshin	int srcrt;
9253541Sshin{
9353541Sshin	struct ip6_hdr *ip6 = mtod(m, struct ip6_hdr *);
9453541Sshin	register struct sockaddr_in6 *dst;
9553541Sshin	register struct rtentry *rt;
9653541Sshin	int error, type = 0, code = 0;
9753541Sshin	struct mbuf *mcopy;
9853541Sshin#ifdef IPSEC_IPV6FWD
9953541Sshin	struct secpolicy *sp = NULL;
10053541Sshin#endif
10153541Sshin
10253541Sshin#ifdef IPSEC_IPV6FWD
10353541Sshin	/*
10453541Sshin	 * Check AH/ESP integrity.
10553541Sshin	 */
10653541Sshin	/*
10753541Sshin	 * Don't increment ip6s_cantforward because this is the check
10853541Sshin	 * before forwarding packet actually.
10953541Sshin	 */
11053541Sshin	if (ipsec6_in_reject(m, NULL)) {
11153541Sshin		ipsec6stat.in_polvio++;
11253541Sshin		m_freem(m);
11353541Sshin		return;
11453541Sshin	}
11553541Sshin#endif /*IPSEC_IPV6FWD*/
11653541Sshin
11753541Sshin	if (m->m_flags & (M_BCAST|M_MCAST) ||
11853541Sshin	   in6_canforward(&ip6->ip6_src, &ip6->ip6_dst) == 0) {
11953541Sshin		ip6stat.ip6s_cantforward++;
12053541Sshin		ip6stat.ip6s_badscope++;
12153541Sshin		/* XXX in6_ifstat_inc(rt->rt_ifp, ifs6_in_discard) */
12253541Sshin		if (ip6_log_time + ip6_log_interval < time_second) {
12353541Sshin			char addr[INET6_ADDRSTRLEN];
12453541Sshin			ip6_log_time = time_second;
12553541Sshin			strncpy(addr, ip6_sprintf(&ip6->ip6_src), sizeof(addr));
12653541Sshin			log(LOG_DEBUG,
12753541Sshin			    "cannot forward "
12853541Sshin			    "from %s to %s nxt %d received on %s\n",
12953541Sshin			    addr, ip6_sprintf(&ip6->ip6_dst),
13053541Sshin			    ip6->ip6_nxt,
13153541Sshin			    if_name(m->m_pkthdr.rcvif));
13253541Sshin		}
13353541Sshin		m_freem(m);
13453541Sshin		return;
13553541Sshin	}
13653541Sshin
13753541Sshin	if (ip6->ip6_hlim <= IPV6_HLIMDEC) {
13853541Sshin		/* XXX in6_ifstat_inc(rt->rt_ifp, ifs6_in_discard) */
13953541Sshin		icmp6_error(m, ICMP6_TIME_EXCEEDED,
14053541Sshin				ICMP6_TIME_EXCEED_TRANSIT, 0);
14153541Sshin		return;
14253541Sshin	}
14353541Sshin	ip6->ip6_hlim -= IPV6_HLIMDEC;
14453541Sshin
14553541Sshin#ifdef IPSEC_IPV6FWD
14653541Sshin	/* get a security policy for this packet */
14753541Sshin	sp = ipsec6_getpolicybyaddr(m, IPSEC_DIR_OUTBOUND, 0, &error);
14853541Sshin	if (sp == NULL) {
14953541Sshin		ipsec6stat.out_inval++;
15053541Sshin		ip6stat.ip6s_cantforward++;
15153541Sshin		/* XXX: any icmp ? */
15253541Sshin		m_freem(m);
15353541Sshin		return;
15453541Sshin	}
15553541Sshin
15653541Sshin	error = 0;
15753541Sshin
15853541Sshin	/* check policy */
15953541Sshin	switch (sp->policy) {
16053541Sshin	case IPSEC_POLICY_DISCARD:
16153541Sshin		/*
16253541Sshin		 * This packet is just discarded.
16353541Sshin		 */
16453541Sshin		ipsec6stat.out_polvio++;
16553541Sshin		ip6stat.ip6s_cantforward++;
16653541Sshin		key_freesp(sp);
16753541Sshin		/* XXX: any icmp ? */
16853541Sshin		m_freem(m);
16953541Sshin		return;
17053541Sshin
17153541Sshin	case IPSEC_POLICY_BYPASS:
17253541Sshin	case IPSEC_POLICY_NONE:
17353541Sshin		/* no need to do IPsec. */
17453541Sshin		key_freesp(sp);
17553541Sshin		goto skip_ipsec;
17653541Sshin
17753541Sshin	case IPSEC_POLICY_IPSEC:
17853541Sshin		if (sp->req == NULL) {
17953541Sshin			/* XXX should be panic ? */
18053541Sshin			printf("ip6_forward: No IPsec request specified.\n");
18153541Sshin			ip6stat.ip6s_cantforward++;
18253541Sshin			key_freesp(sp);
18353541Sshin			/* XXX: any icmp ? */
18453541Sshin			m_freem(m);
18553541Sshin			return;
18653541Sshin		}
18753541Sshin		/* do IPsec */
18853541Sshin		break;
18953541Sshin
19053541Sshin	case IPSEC_POLICY_ENTRUST:
19153541Sshin	default:
19253541Sshin		/* should be panic ?? */
19353541Sshin		printf("ip6_forward: Invalid policy found. %d\n", sp->policy);
19453541Sshin		key_freesp(sp);
19553541Sshin		goto skip_ipsec;
19653541Sshin	}
19753541Sshin
19853541Sshin    {
19953541Sshin	struct ipsec_output_state state;
20053541Sshin
20153541Sshin	/*
20253541Sshin	 * All the extension headers will become inaccessible
20353541Sshin	 * (since they can be encrypted).
20453541Sshin	 * Don't panic, we need no more updates to extension headers
20553541Sshin	 * on inner IPv6 packet (since they are now encapsulated).
20653541Sshin	 *
20753541Sshin	 * IPv6 [ESP|AH] IPv6 [extension headers] payload
20853541Sshin	 */
20953541Sshin	bzero(&state, sizeof(state));
21053541Sshin	state.m = m;
21153541Sshin	state.ro = NULL;	/* update at ipsec6_output_tunnel() */
21253541Sshin	state.dst = NULL;	/* update at ipsec6_output_tunnel() */
21353541Sshin
21453541Sshin	error = ipsec6_output_tunnel(&state, sp, 0);
21553541Sshin
21653541Sshin	m = state.m;
21753541Sshin	/* XXX allocate a route (ro, dst) again later */
21853541Sshin	key_freesp(sp);
21953541Sshin
22053541Sshin	if (error) {
22153541Sshin		/* mbuf is already reclaimed in ipsec6_output_tunnel. */
22253541Sshin		switch (error) {
22353541Sshin		case EHOSTUNREACH:
22453541Sshin		case ENETUNREACH:
22553541Sshin		case EMSGSIZE:
22653541Sshin		case ENOBUFS:
22753541Sshin		case ENOMEM:
22853541Sshin			break;
22953541Sshin		default:
23053541Sshin			printf("ip6_output (ipsec): error code %d\n", error);
23153541Sshin			/*fall through*/
23253541Sshin		case ENOENT:
23353541Sshin			/* don't show these error codes to the user */
23453541Sshin			break;
23553541Sshin		}
23653541Sshin		ip6stat.ip6s_cantforward++;
23753541Sshin		/* XXX: any icmp ? */
23853541Sshin		m_freem(m);
23953541Sshin		return;
24053541Sshin	}
24153541Sshin    }
24253541Sshin    skip_ipsec:
24353541Sshin#endif /* IPSEC_IPV6FWD */
24453541Sshin
24553541Sshin	dst = &ip6_forward_rt.ro_dst;
24653541Sshin	if (!srcrt) {
24753541Sshin		/*
24853541Sshin		 * ip6_forward_rt.ro_dst.sin6_addr is equal to ip6->ip6_dst
24953541Sshin		 */
25053541Sshin		if (ip6_forward_rt.ro_rt == 0 ||
25153541Sshin		    (ip6_forward_rt.ro_rt->rt_flags & RTF_UP) == 0) {
25253541Sshin			if (ip6_forward_rt.ro_rt) {
25353541Sshin				RTFREE(ip6_forward_rt.ro_rt);
25453541Sshin				ip6_forward_rt.ro_rt = 0;
25553541Sshin			}
25653541Sshin			/* this probably fails but give it a try again */
25753541Sshin			rtalloc_ign((struct route *)&ip6_forward_rt,
25853541Sshin				    RTF_PRCLONING);
25953541Sshin		}
26053541Sshin
26153541Sshin		if (ip6_forward_rt.ro_rt == 0) {
26253541Sshin			ip6stat.ip6s_noroute++;
26353541Sshin			/* XXX in6_ifstat_inc(rt->rt_ifp, ifs6_in_noroute) */
26453541Sshin			icmp6_error(m, ICMP6_DST_UNREACH,
26553541Sshin				    ICMP6_DST_UNREACH_NOROUTE, 0);
26653541Sshin			return;
26753541Sshin		}
26853541Sshin	} else if ((rt = ip6_forward_rt.ro_rt) == 0 ||
26953541Sshin		 !IN6_ARE_ADDR_EQUAL(&ip6->ip6_dst, &dst->sin6_addr)) {
27053541Sshin		if (ip6_forward_rt.ro_rt) {
27153541Sshin			RTFREE(ip6_forward_rt.ro_rt);
27253541Sshin			ip6_forward_rt.ro_rt = 0;
27353541Sshin		}
27453541Sshin		bzero(dst, sizeof(*dst));
27553541Sshin		dst->sin6_len = sizeof(struct sockaddr_in6);
27653541Sshin		dst->sin6_family = AF_INET6;
27753541Sshin		dst->sin6_addr = ip6->ip6_dst;
27853541Sshin
27953541Sshin  		rtalloc_ign((struct route *)&ip6_forward_rt, RTF_PRCLONING);
28053541Sshin		if (ip6_forward_rt.ro_rt == 0) {
28153541Sshin			ip6stat.ip6s_noroute++;
28253541Sshin			/* XXX in6_ifstat_inc(rt->rt_ifp, ifs6_in_noroute) */
28353541Sshin			icmp6_error(m, ICMP6_DST_UNREACH,
28453541Sshin				    ICMP6_DST_UNREACH_NOROUTE, 0);
28553541Sshin			return;
28653541Sshin		}
28753541Sshin	}
28853541Sshin	rt = ip6_forward_rt.ro_rt;
28953541Sshin	if (m->m_pkthdr.len > rt->rt_ifp->if_mtu){
29053541Sshin		in6_ifstat_inc(rt->rt_ifp, ifs6_in_toobig);
29153541Sshin 		icmp6_error(m, ICMP6_PACKET_TOO_BIG, 0, rt->rt_ifp->if_mtu);
29253541Sshin		return;
29353541Sshin 	}
29453541Sshin
29553541Sshin	if (rt->rt_flags & RTF_GATEWAY)
29653541Sshin		dst = (struct sockaddr_in6 *)rt->rt_gateway;
29753541Sshin	/*
29853541Sshin	 * Save at most 528 bytes of the packet in case
29953541Sshin	 * we need to generate an ICMP6 message to the src.
30053541Sshin	 * Thanks to M_EXT, in most cases copy will not occur.
30153541Sshin	 */
30253541Sshin	mcopy = m_copy(m, 0, imin(m->m_pkthdr.len, ICMPV6_PLD_MAXLEN));
30353541Sshin
30453541Sshin	/*
30553541Sshin	 * If we are to forward the packet using the same interface
30653541Sshin	 * as one we got the packet from, perhaps we should send a redirect
30753541Sshin	 * to sender to shortcut a hop.
30853541Sshin	 * Only send redirect if source is sending directly to us,
30953541Sshin	 * and if packet was not source routed (or has any options).
31053541Sshin	 * Also, don't send redirect if forwarding using a route
31153541Sshin	 * modified by a redirect.
31253541Sshin	 */
31353541Sshin	if (rt->rt_ifp == m->m_pkthdr.rcvif && !srcrt &&
31453541Sshin	    (rt->rt_flags & (RTF_DYNAMIC|RTF_MODIFIED)) == 0)
31553541Sshin		type = ND_REDIRECT;
31653541Sshin
31753541Sshin#ifdef IPV6FIREWALL
31853541Sshin	/*
31953541Sshin	 * Check with the firewall...
32053541Sshin	 */
32153541Sshin	if (ip6_fw_chk_ptr) {
32253541Sshin		u_short port = 0;
32353541Sshin		/* If ipfw says divert, we have to just drop packet */
32453541Sshin		if ((*ip6_fw_chk_ptr)(&ip6, rt->rt_ifp, &port, &m)) {
32553541Sshin			m_freem(m);
32653541Sshin			goto freecopy;
32753541Sshin		}
32853541Sshin		if (!m)
32953541Sshin			goto freecopy;
33053541Sshin	}
33153541Sshin#endif
33253541Sshin
33353541Sshin	error = nd6_output(rt->rt_ifp, m, dst, rt);
33453541Sshin	if (error) {
33553541Sshin		in6_ifstat_inc(rt->rt_ifp, ifs6_out_discard);
33653541Sshin		ip6stat.ip6s_cantforward++;
33753541Sshin	} else {
33853541Sshin		ip6stat.ip6s_forward++;
33953541Sshin		in6_ifstat_inc(rt->rt_ifp, ifs6_out_forward);
34053541Sshin		if (type)
34153541Sshin			ip6stat.ip6s_redirectsent++;
34253541Sshin		else {
34353541Sshin			if (mcopy)
34453541Sshin				goto freecopy;
34553541Sshin		}
34653541Sshin	}
34753541Sshin	if (mcopy == NULL)
34853541Sshin		return;
34953541Sshin
35053541Sshin	switch (error) {
35153541Sshin	case 0:
35253541Sshin		if (type == ND_REDIRECT) {
35353541Sshin			icmp6_redirect_output(mcopy, rt);
35453541Sshin			return;
35553541Sshin		}
35653541Sshin		goto freecopy;
35753541Sshin
35853541Sshin	case EMSGSIZE:
35953541Sshin		/* xxx MTU is constant in PPP? */
36053541Sshin		goto freecopy;
36153541Sshin
36253541Sshin	case ENOBUFS:
36353541Sshin		/* Tell source to slow down like source quench in IP? */
36453541Sshin		goto freecopy;
36553541Sshin
36653541Sshin	case ENETUNREACH:	/* shouldn't happen, checked above */
36753541Sshin	case EHOSTUNREACH:
36853541Sshin	case ENETDOWN:
36953541Sshin	case EHOSTDOWN:
37053541Sshin	default:
37153541Sshin		type = ICMP6_DST_UNREACH;
37253541Sshin		code = ICMP6_DST_UNREACH_ADDR;
37353541Sshin		break;
37453541Sshin	}
37553541Sshin	icmp6_error(mcopy, type, code, 0);
37653541Sshin	return;
37753541Sshin
37853541Sshin freecopy:
37953541Sshin	m_freem(mcopy);
38053541Sshin	return;
38153541Sshin}
382