ip6_forward.c revision 59391
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 59391 2000-04-19 14:58:28Z phk $
30 */
31
32#include "opt_ipsec.h"
33
34#include <sys/param.h>
35#include <sys/systm.h>
36#include <sys/mbuf.h>
37#include <sys/domain.h>
38#include <sys/protosw.h>
39#include <sys/socket.h>
40#include <sys/errno.h>
41#include <sys/time.h>
42#include <sys/kernel.h>
43#include <sys/syslog.h>
44
45#include <net/if.h>
46#include <net/route.h>
47
48#include <netinet/in.h>
49#include <netinet/in_var.h>
50#include <netinet6/ip6.h>
51#include <netinet6/ip6_var.h>
52#include <netinet6/icmp6.h>
53#include <netinet6/nd6.h>
54
55#ifdef IPSEC_IPV6FWD
56#include <netinet6/ipsec.h>
57#include <netinet6/ipsec6.h>
58#include <netkey/key.h>
59#ifdef IPSEC_DEBUG
60#include <netkey/key_debug.h>
61#else
62#define KEYDEBUG(lev,arg)
63#endif
64#endif /* IPSEC_IPV6FWD */
65
66#ifdef IPV6FIREWALL
67#include <netinet6/ip6_fw.h>
68#endif
69
70#include <net/net_osdep.h>
71
72struct	route_in6 ip6_forward_rt;
73
74/*
75 * Forward a packet.  If some error occurs return the sender
76 * an icmp packet.  Note we can't always generate a meaningful
77 * icmp message because icmp doesn't have a large enough repertoire
78 * of codes and types.
79 *
80 * If not forwarding, just drop the packet.  This could be confusing
81 * if ipforwarding was zero but some routing protocol was advancing
82 * us as a gateway to somewhere.  However, we must let the routing
83 * protocol deal with that.
84 *
85 */
86
87void
88ip6_forward(m, srcrt)
89	struct mbuf *m;
90	int srcrt;
91{
92	struct ip6_hdr *ip6 = mtod(m, struct ip6_hdr *);
93	register struct sockaddr_in6 *dst;
94	register struct rtentry *rt;
95	int error, type = 0, code = 0;
96	struct mbuf *mcopy;
97#ifdef IPSEC_IPV6FWD
98	struct secpolicy *sp = NULL;
99#endif
100
101#ifdef IPSEC_IPV6FWD
102	/*
103	 * Check AH/ESP integrity.
104	 */
105	/*
106	 * Don't increment ip6s_cantforward because this is the check
107	 * before forwarding packet actually.
108	 */
109	if (ipsec6_in_reject(m, NULL)) {
110		ipsec6stat.in_polvio++;
111		m_freem(m);
112		return;
113	}
114#endif /*IPSEC_IPV6FWD*/
115
116	if (m->m_flags & (M_BCAST|M_MCAST) ||
117	   in6_canforward(&ip6->ip6_src, &ip6->ip6_dst) == 0) {
118		ip6stat.ip6s_cantforward++;
119		ip6stat.ip6s_badscope++;
120		/* XXX in6_ifstat_inc(rt->rt_ifp, ifs6_in_discard) */
121		if (ip6_log_time + ip6_log_interval < time_second) {
122			char addr[INET6_ADDRSTRLEN];
123			ip6_log_time = time_second;
124			strncpy(addr, ip6_sprintf(&ip6->ip6_src), sizeof(addr));
125			log(LOG_DEBUG,
126			    "cannot forward "
127			    "from %s to %s nxt %d received on %s\n",
128			    addr, ip6_sprintf(&ip6->ip6_dst),
129			    ip6->ip6_nxt,
130			    if_name(m->m_pkthdr.rcvif));
131		}
132		m_freem(m);
133		return;
134	}
135
136	if (ip6->ip6_hlim <= IPV6_HLIMDEC) {
137		/* XXX in6_ifstat_inc(rt->rt_ifp, ifs6_in_discard) */
138		icmp6_error(m, ICMP6_TIME_EXCEEDED,
139				ICMP6_TIME_EXCEED_TRANSIT, 0);
140		return;
141	}
142	ip6->ip6_hlim -= IPV6_HLIMDEC;
143
144#ifdef IPSEC_IPV6FWD
145	/* get a security policy for this packet */
146	sp = ipsec6_getpolicybyaddr(m, IPSEC_DIR_OUTBOUND, 0, &error);
147	if (sp == NULL) {
148		ipsec6stat.out_inval++;
149		ip6stat.ip6s_cantforward++;
150		/* XXX: any icmp ? */
151		m_freem(m);
152		return;
153	}
154
155	error = 0;
156
157	/* check policy */
158	switch (sp->policy) {
159	case IPSEC_POLICY_DISCARD:
160		/*
161		 * This packet is just discarded.
162		 */
163		ipsec6stat.out_polvio++;
164		ip6stat.ip6s_cantforward++;
165		key_freesp(sp);
166		/* XXX: any icmp ? */
167		m_freem(m);
168		return;
169
170	case IPSEC_POLICY_BYPASS:
171	case IPSEC_POLICY_NONE:
172		/* no need to do IPsec. */
173		key_freesp(sp);
174		goto skip_ipsec;
175
176	case IPSEC_POLICY_IPSEC:
177		if (sp->req == NULL) {
178			/* XXX should be panic ? */
179			printf("ip6_forward: No IPsec request specified.\n");
180			ip6stat.ip6s_cantforward++;
181			key_freesp(sp);
182			/* XXX: any icmp ? */
183			m_freem(m);
184			return;
185		}
186		/* do IPsec */
187		break;
188
189	case IPSEC_POLICY_ENTRUST:
190	default:
191		/* should be panic ?? */
192		printf("ip6_forward: Invalid policy found. %d\n", sp->policy);
193		key_freesp(sp);
194		goto skip_ipsec;
195	}
196
197    {
198	struct ipsec_output_state state;
199
200	/*
201	 * All the extension headers will become inaccessible
202	 * (since they can be encrypted).
203	 * Don't panic, we need no more updates to extension headers
204	 * on inner IPv6 packet (since they are now encapsulated).
205	 *
206	 * IPv6 [ESP|AH] IPv6 [extension headers] payload
207	 */
208	bzero(&state, sizeof(state));
209	state.m = m;
210	state.ro = NULL;	/* update at ipsec6_output_tunnel() */
211	state.dst = NULL;	/* update at ipsec6_output_tunnel() */
212
213	error = ipsec6_output_tunnel(&state, sp, 0);
214
215	m = state.m;
216	/* XXX allocate a route (ro, dst) again later */
217	key_freesp(sp);
218
219	if (error) {
220		/* mbuf is already reclaimed in ipsec6_output_tunnel. */
221		switch (error) {
222		case EHOSTUNREACH:
223		case ENETUNREACH:
224		case EMSGSIZE:
225		case ENOBUFS:
226		case ENOMEM:
227			break;
228		default:
229			printf("ip6_output (ipsec): error code %d\n", error);
230			/*fall through*/
231		case ENOENT:
232			/* don't show these error codes to the user */
233			break;
234		}
235		ip6stat.ip6s_cantforward++;
236		/* XXX: any icmp ? */
237		m_freem(m);
238		return;
239	}
240    }
241    skip_ipsec:
242#endif /* IPSEC_IPV6FWD */
243
244	dst = &ip6_forward_rt.ro_dst;
245	if (!srcrt) {
246		/*
247		 * ip6_forward_rt.ro_dst.sin6_addr is equal to ip6->ip6_dst
248		 */
249		if (ip6_forward_rt.ro_rt == 0 ||
250		    (ip6_forward_rt.ro_rt->rt_flags & RTF_UP) == 0) {
251			if (ip6_forward_rt.ro_rt) {
252				RTFREE(ip6_forward_rt.ro_rt);
253				ip6_forward_rt.ro_rt = 0;
254			}
255			/* this probably fails but give it a try again */
256			rtalloc_ign((struct route *)&ip6_forward_rt,
257				    RTF_PRCLONING);
258		}
259
260		if (ip6_forward_rt.ro_rt == 0) {
261			ip6stat.ip6s_noroute++;
262			/* XXX in6_ifstat_inc(rt->rt_ifp, ifs6_in_noroute) */
263			icmp6_error(m, ICMP6_DST_UNREACH,
264				    ICMP6_DST_UNREACH_NOROUTE, 0);
265			return;
266		}
267	} else if ((rt = ip6_forward_rt.ro_rt) == 0 ||
268		 !IN6_ARE_ADDR_EQUAL(&ip6->ip6_dst, &dst->sin6_addr)) {
269		if (ip6_forward_rt.ro_rt) {
270			RTFREE(ip6_forward_rt.ro_rt);
271			ip6_forward_rt.ro_rt = 0;
272		}
273		bzero(dst, sizeof(*dst));
274		dst->sin6_len = sizeof(struct sockaddr_in6);
275		dst->sin6_family = AF_INET6;
276		dst->sin6_addr = ip6->ip6_dst;
277
278  		rtalloc_ign((struct route *)&ip6_forward_rt, RTF_PRCLONING);
279		if (ip6_forward_rt.ro_rt == 0) {
280			ip6stat.ip6s_noroute++;
281			/* XXX in6_ifstat_inc(rt->rt_ifp, ifs6_in_noroute) */
282			icmp6_error(m, ICMP6_DST_UNREACH,
283				    ICMP6_DST_UNREACH_NOROUTE, 0);
284			return;
285		}
286	}
287	rt = ip6_forward_rt.ro_rt;
288	if (m->m_pkthdr.len > rt->rt_ifp->if_mtu){
289		in6_ifstat_inc(rt->rt_ifp, ifs6_in_toobig);
290 		icmp6_error(m, ICMP6_PACKET_TOO_BIG, 0, rt->rt_ifp->if_mtu);
291		return;
292 	}
293
294	if (rt->rt_flags & RTF_GATEWAY)
295		dst = (struct sockaddr_in6 *)rt->rt_gateway;
296	/*
297	 * Save at most 528 bytes of the packet in case
298	 * we need to generate an ICMP6 message to the src.
299	 * Thanks to M_EXT, in most cases copy will not occur.
300	 */
301	mcopy = m_copy(m, 0, imin(m->m_pkthdr.len, ICMPV6_PLD_MAXLEN));
302
303	/*
304	 * If we are to forward the packet using the same interface
305	 * as one we got the packet from, perhaps we should send a redirect
306	 * to sender to shortcut a hop.
307	 * Only send redirect if source is sending directly to us,
308	 * and if packet was not source routed (or has any options).
309	 * Also, don't send redirect if forwarding using a route
310	 * modified by a redirect.
311	 */
312	if (rt->rt_ifp == m->m_pkthdr.rcvif && !srcrt &&
313	    (rt->rt_flags & (RTF_DYNAMIC|RTF_MODIFIED)) == 0)
314		type = ND_REDIRECT;
315
316#ifdef IPV6FIREWALL
317	/*
318	 * Check with the firewall...
319	 */
320	if (ip6_fw_chk_ptr) {
321		u_short port = 0;
322		/* If ipfw says divert, we have to just drop packet */
323		if ((*ip6_fw_chk_ptr)(&ip6, rt->rt_ifp, &port, &m)) {
324			m_freem(m);
325			goto freecopy;
326		}
327		if (!m)
328			goto freecopy;
329	}
330#endif
331
332	error = nd6_output(rt->rt_ifp, m, dst, rt);
333	if (error) {
334		in6_ifstat_inc(rt->rt_ifp, ifs6_out_discard);
335		ip6stat.ip6s_cantforward++;
336	} else {
337		ip6stat.ip6s_forward++;
338		in6_ifstat_inc(rt->rt_ifp, ifs6_out_forward);
339		if (type)
340			ip6stat.ip6s_redirectsent++;
341		else {
342			if (mcopy)
343				goto freecopy;
344		}
345	}
346	if (mcopy == NULL)
347		return;
348
349	switch (error) {
350	case 0:
351		if (type == ND_REDIRECT) {
352			icmp6_redirect_output(mcopy, rt);
353			return;
354		}
355		goto freecopy;
356
357	case EMSGSIZE:
358		/* xxx MTU is constant in PPP? */
359		goto freecopy;
360
361	case ENOBUFS:
362		/* Tell source to slow down like source quench in IP? */
363		goto freecopy;
364
365	case ENETUNREACH:	/* shouldn't happen, checked above */
366	case EHOSTUNREACH:
367	case ENETDOWN:
368	case EHOSTDOWN:
369	default:
370		type = ICMP6_DST_UNREACH;
371		code = ICMP6_DST_UNREACH_ADDR;
372		break;
373	}
374	icmp6_error(mcopy, type, code, 0);
375	return;
376
377 freecopy:
378	m_freem(mcopy);
379	return;
380}
381