ip6_forward.c revision 53541
1/*
2 * Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project.
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 * 1. Redistributions of source code must retain the above copyright
9 *    notice, this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright
11 *    notice, this list of conditions and the following disclaimer in the
12 *    documentation and/or other materials provided with the distribution.
13 * 3. Neither the name of the project nor the names of its contributors
14 *    may be used to endorse or promote products derived from this software
15 *    without specific prior written permission.
16 *
17 * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND
18 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20 * ARE DISCLAIMED.  IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE
21 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
22 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
23 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
24 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
26 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
27 * SUCH DAMAGE.
28 *
29 * $FreeBSD: head/sys/netinet6/ip6_forward.c 53541 1999-11-22 02:45:11Z shin $
30 */
31
32#include "opt_ip6fw.h"
33#include "opt_inet.h"
34#include "opt_ipsec.h"
35#include "opt_key.h"
36
37#include <sys/param.h>
38#include <sys/systm.h>
39#include <sys/malloc.h>
40#include <sys/mbuf.h>
41#include <sys/domain.h>
42#include <sys/protosw.h>
43#include <sys/socket.h>
44#include <sys/errno.h>
45#include <sys/time.h>
46#include <sys/kernel.h>
47#include <sys/syslog.h>
48
49#include <net/if.h>
50#include <net/route.h>
51
52#include <netinet/in.h>
53#include <netinet/in_var.h>
54#include <netinet6/ip6.h>
55#include <netinet6/ip6_var.h>
56#include <netinet6/icmp6.h>
57#include <netinet6/nd6.h>
58
59#ifdef IPSEC_IPV6FWD
60#include <netinet6/ipsec.h>
61#ifdef INET6
62#include <netinet6/ipsec6.h>
63#endif /* INET6 */
64#include <netkey/key.h>
65#ifdef KEY_DEBUG
66#include <netkey/key_debug.h>
67#ifdef INET6
68#include <netkey/key_debug6.h>
69#endif /* INET6 */
70#else
71#define DPRINTF(lev,arg)
72#define DDO(lev, stmt)
73#define DP(x, y, z)
74#endif /* KEY_DEBUG */
75#endif /* IPSEC_IPV6FWD */
76
77#ifdef IPV6FIREWALL
78#include <netinet6/ip6_fw.h>
79#endif
80
81#include <net/net_osdep.h>
82
83struct	route_in6 ip6_forward_rt;
84
85/*
86 * Forward a packet.  If some error occurs return the sender
87 * an icmp packet.  Note we can't always generate a meaningful
88 * icmp message because icmp doesn't have a large enough repertoire
89 * of codes and types.
90 *
91 * If not forwarding, just drop the packet.  This could be confusing
92 * if ipforwarding was zero but some routing protocol was advancing
93 * us as a gateway to somewhere.  However, we must let the routing
94 * protocol deal with that.
95 *
96 */
97
98void
99ip6_forward(m, srcrt)
100	struct mbuf *m;
101	int srcrt;
102{
103	struct ip6_hdr *ip6 = mtod(m, struct ip6_hdr *);
104	register struct sockaddr_in6 *dst;
105	register struct rtentry *rt;
106	int error, type = 0, code = 0;
107	struct mbuf *mcopy;
108#ifdef IPSEC_IPV6FWD
109	struct secpolicy *sp = NULL;
110#endif
111
112#ifdef IPSEC_IPV6FWD
113	/*
114	 * Check AH/ESP integrity.
115	 */
116	/*
117	 * Don't increment ip6s_cantforward because this is the check
118	 * before forwarding packet actually.
119	 */
120	if (ipsec6_in_reject(m, NULL)) {
121		ipsec6stat.in_polvio++;
122		m_freem(m);
123		return;
124	}
125#endif /*IPSEC_IPV6FWD*/
126
127	if (m->m_flags & (M_BCAST|M_MCAST) ||
128	   in6_canforward(&ip6->ip6_src, &ip6->ip6_dst) == 0) {
129		ip6stat.ip6s_cantforward++;
130		ip6stat.ip6s_badscope++;
131		/* XXX in6_ifstat_inc(rt->rt_ifp, ifs6_in_discard) */
132		if (ip6_log_time + ip6_log_interval < time_second) {
133			char addr[INET6_ADDRSTRLEN];
134			ip6_log_time = time_second;
135			strncpy(addr, ip6_sprintf(&ip6->ip6_src), sizeof(addr));
136			log(LOG_DEBUG,
137			    "cannot forward "
138			    "from %s to %s nxt %d received on %s\n",
139			    addr, ip6_sprintf(&ip6->ip6_dst),
140			    ip6->ip6_nxt,
141			    if_name(m->m_pkthdr.rcvif));
142		}
143		m_freem(m);
144		return;
145	}
146
147	if (ip6->ip6_hlim <= IPV6_HLIMDEC) {
148		/* XXX in6_ifstat_inc(rt->rt_ifp, ifs6_in_discard) */
149		icmp6_error(m, ICMP6_TIME_EXCEEDED,
150				ICMP6_TIME_EXCEED_TRANSIT, 0);
151		return;
152	}
153	ip6->ip6_hlim -= IPV6_HLIMDEC;
154
155#ifdef IPSEC_IPV6FWD
156	/* get a security policy for this packet */
157	sp = ipsec6_getpolicybyaddr(m, IPSEC_DIR_OUTBOUND, 0, &error);
158	if (sp == NULL) {
159		ipsec6stat.out_inval++;
160		ip6stat.ip6s_cantforward++;
161		/* XXX: any icmp ? */
162		m_freem(m);
163		return;
164	}
165
166	error = 0;
167
168	/* check policy */
169	switch (sp->policy) {
170	case IPSEC_POLICY_DISCARD:
171		/*
172		 * This packet is just discarded.
173		 */
174		ipsec6stat.out_polvio++;
175		ip6stat.ip6s_cantforward++;
176		key_freesp(sp);
177		/* XXX: any icmp ? */
178		m_freem(m);
179		return;
180
181	case IPSEC_POLICY_BYPASS:
182	case IPSEC_POLICY_NONE:
183		/* no need to do IPsec. */
184		key_freesp(sp);
185		goto skip_ipsec;
186
187	case IPSEC_POLICY_IPSEC:
188		if (sp->req == NULL) {
189			/* XXX should be panic ? */
190			printf("ip6_forward: No IPsec request specified.\n");
191			ip6stat.ip6s_cantforward++;
192			key_freesp(sp);
193			/* XXX: any icmp ? */
194			m_freem(m);
195			return;
196		}
197		/* do IPsec */
198		break;
199
200	case IPSEC_POLICY_ENTRUST:
201	default:
202		/* should be panic ?? */
203		printf("ip6_forward: Invalid policy found. %d\n", sp->policy);
204		key_freesp(sp);
205		goto skip_ipsec;
206	}
207
208    {
209	struct ipsec_output_state state;
210
211	/*
212	 * All the extension headers will become inaccessible
213	 * (since they can be encrypted).
214	 * Don't panic, we need no more updates to extension headers
215	 * on inner IPv6 packet (since they are now encapsulated).
216	 *
217	 * IPv6 [ESP|AH] IPv6 [extension headers] payload
218	 */
219	bzero(&state, sizeof(state));
220	state.m = m;
221	state.ro = NULL;	/* update at ipsec6_output_tunnel() */
222	state.dst = NULL;	/* update at ipsec6_output_tunnel() */
223
224	error = ipsec6_output_tunnel(&state, sp, 0);
225
226	m = state.m;
227	/* XXX allocate a route (ro, dst) again later */
228	key_freesp(sp);
229
230	if (error) {
231		/* mbuf is already reclaimed in ipsec6_output_tunnel. */
232		switch (error) {
233		case EHOSTUNREACH:
234		case ENETUNREACH:
235		case EMSGSIZE:
236		case ENOBUFS:
237		case ENOMEM:
238			break;
239		default:
240			printf("ip6_output (ipsec): error code %d\n", error);
241			/*fall through*/
242		case ENOENT:
243			/* don't show these error codes to the user */
244			break;
245		}
246		ip6stat.ip6s_cantforward++;
247		/* XXX: any icmp ? */
248		m_freem(m);
249		return;
250	}
251    }
252    skip_ipsec:
253#endif /* IPSEC_IPV6FWD */
254
255	dst = &ip6_forward_rt.ro_dst;
256	if (!srcrt) {
257		/*
258		 * ip6_forward_rt.ro_dst.sin6_addr is equal to ip6->ip6_dst
259		 */
260		if (ip6_forward_rt.ro_rt == 0 ||
261		    (ip6_forward_rt.ro_rt->rt_flags & RTF_UP) == 0) {
262			if (ip6_forward_rt.ro_rt) {
263				RTFREE(ip6_forward_rt.ro_rt);
264				ip6_forward_rt.ro_rt = 0;
265			}
266			/* this probably fails but give it a try again */
267			rtalloc_ign((struct route *)&ip6_forward_rt,
268				    RTF_PRCLONING);
269		}
270
271		if (ip6_forward_rt.ro_rt == 0) {
272			ip6stat.ip6s_noroute++;
273			/* XXX in6_ifstat_inc(rt->rt_ifp, ifs6_in_noroute) */
274			icmp6_error(m, ICMP6_DST_UNREACH,
275				    ICMP6_DST_UNREACH_NOROUTE, 0);
276			return;
277		}
278	} else if ((rt = ip6_forward_rt.ro_rt) == 0 ||
279		 !IN6_ARE_ADDR_EQUAL(&ip6->ip6_dst, &dst->sin6_addr)) {
280		if (ip6_forward_rt.ro_rt) {
281			RTFREE(ip6_forward_rt.ro_rt);
282			ip6_forward_rt.ro_rt = 0;
283		}
284		bzero(dst, sizeof(*dst));
285		dst->sin6_len = sizeof(struct sockaddr_in6);
286		dst->sin6_family = AF_INET6;
287		dst->sin6_addr = ip6->ip6_dst;
288
289  		rtalloc_ign((struct route *)&ip6_forward_rt, RTF_PRCLONING);
290		if (ip6_forward_rt.ro_rt == 0) {
291			ip6stat.ip6s_noroute++;
292			/* XXX in6_ifstat_inc(rt->rt_ifp, ifs6_in_noroute) */
293			icmp6_error(m, ICMP6_DST_UNREACH,
294				    ICMP6_DST_UNREACH_NOROUTE, 0);
295			return;
296		}
297	}
298	rt = ip6_forward_rt.ro_rt;
299	if (m->m_pkthdr.len > rt->rt_ifp->if_mtu){
300		in6_ifstat_inc(rt->rt_ifp, ifs6_in_toobig);
301 		icmp6_error(m, ICMP6_PACKET_TOO_BIG, 0, rt->rt_ifp->if_mtu);
302		return;
303 	}
304
305	if (rt->rt_flags & RTF_GATEWAY)
306		dst = (struct sockaddr_in6 *)rt->rt_gateway;
307	/*
308	 * Save at most 528 bytes of the packet in case
309	 * we need to generate an ICMP6 message to the src.
310	 * Thanks to M_EXT, in most cases copy will not occur.
311	 */
312	mcopy = m_copy(m, 0, imin(m->m_pkthdr.len, ICMPV6_PLD_MAXLEN));
313
314	/*
315	 * If we are to forward the packet using the same interface
316	 * as one we got the packet from, perhaps we should send a redirect
317	 * to sender to shortcut a hop.
318	 * Only send redirect if source is sending directly to us,
319	 * and if packet was not source routed (or has any options).
320	 * Also, don't send redirect if forwarding using a route
321	 * modified by a redirect.
322	 */
323	if (rt->rt_ifp == m->m_pkthdr.rcvif && !srcrt &&
324	    (rt->rt_flags & (RTF_DYNAMIC|RTF_MODIFIED)) == 0)
325		type = ND_REDIRECT;
326
327#ifdef IPV6FIREWALL
328	/*
329	 * Check with the firewall...
330	 */
331	if (ip6_fw_chk_ptr) {
332		u_short port = 0;
333		/* If ipfw says divert, we have to just drop packet */
334		if ((*ip6_fw_chk_ptr)(&ip6, rt->rt_ifp, &port, &m)) {
335			m_freem(m);
336			goto freecopy;
337		}
338		if (!m)
339			goto freecopy;
340	}
341#endif
342
343	error = nd6_output(rt->rt_ifp, m, dst, rt);
344	if (error) {
345		in6_ifstat_inc(rt->rt_ifp, ifs6_out_discard);
346		ip6stat.ip6s_cantforward++;
347	} else {
348		ip6stat.ip6s_forward++;
349		in6_ifstat_inc(rt->rt_ifp, ifs6_out_forward);
350		if (type)
351			ip6stat.ip6s_redirectsent++;
352		else {
353			if (mcopy)
354				goto freecopy;
355		}
356	}
357	if (mcopy == NULL)
358		return;
359
360	switch (error) {
361	case 0:
362		if (type == ND_REDIRECT) {
363			icmp6_redirect_output(mcopy, rt);
364			return;
365		}
366		goto freecopy;
367
368	case EMSGSIZE:
369		/* xxx MTU is constant in PPP? */
370		goto freecopy;
371
372	case ENOBUFS:
373		/* Tell source to slow down like source quench in IP? */
374		goto freecopy;
375
376	case ENETUNREACH:	/* shouldn't happen, checked above */
377	case EHOSTUNREACH:
378	case ENETDOWN:
379	case EHOSTDOWN:
380	default:
381		type = ICMP6_DST_UNREACH;
382		code = ICMP6_DST_UNREACH_ADDR;
383		break;
384	}
385	icmp6_error(mcopy, type, code, 0);
386	return;
387
388 freecopy:
389	m_freem(mcopy);
390	return;
391}
392