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