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