]> git.saurik.com Git - apple/xnu.git/blob - bsd/netinet6/ip6_forward.c
xnu-1456.1.26.tar.gz
[apple/xnu.git] / bsd / netinet6 / ip6_forward.c
1 /*
2 * Copyright (c) 2008 Apple Inc. All rights reserved.
3 *
4 * @APPLE_OSREFERENCE_LICENSE_HEADER_START@
5 *
6 * This file contains Original Code and/or Modifications of Original Code
7 * as defined in and that are subject to the Apple Public Source License
8 * Version 2.0 (the 'License'). You may not use this file except in
9 * compliance with the License. The rights granted to you under the License
10 * may not be used to create, or enable the creation or redistribution of,
11 * unlawful or unlicensed copies of an Apple operating system, or to
12 * circumvent, violate, or enable the circumvention or violation of, any
13 * terms of an Apple operating system software license agreement.
14 *
15 * Please obtain a copy of the License at
16 * http://www.opensource.apple.com/apsl/ and read it before using this file.
17 *
18 * The Original Code and all software distributed under the License are
19 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
20 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
21 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
22 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
23 * Please see the License for the specific language governing rights and
24 * limitations under the License.
25 *
26 * @APPLE_OSREFERENCE_LICENSE_HEADER_END@
27 */
28
29 /* $FreeBSD: src/sys/netinet6/ip6_forward.c,v 1.16 2002/10/16 02:25:05 sam Exp $ */
30 /* $KAME: ip6_forward.c,v 1.69 2001/05/17 03:48:30 itojun Exp $ */
31
32 /*
33 * Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project.
34 * All rights reserved.
35 *
36 * Redistribution and use in source and binary forms, with or without
37 * modification, are permitted provided that the following conditions
38 * are met:
39 * 1. Redistributions of source code must retain the above copyright
40 * notice, this list of conditions and the following disclaimer.
41 * 2. Redistributions in binary form must reproduce the above copyright
42 * notice, this list of conditions and the following disclaimer in the
43 * documentation and/or other materials provided with the distribution.
44 * 3. Neither the name of the project nor the names of its contributors
45 * may be used to endorse or promote products derived from this software
46 * without specific prior written permission.
47 *
48 * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND
49 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
50 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
51 * ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE
52 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
53 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
54 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
55 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
56 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
57 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
58 * SUCH DAMAGE.
59 */
60
61
62 #include <sys/param.h>
63 #include <sys/systm.h>
64 #include <sys/malloc.h>
65 #include <sys/mbuf.h>
66 #include <sys/domain.h>
67 #include <sys/protosw.h>
68 #include <sys/socket.h>
69 #include <sys/errno.h>
70 #include <sys/time.h>
71 #include <sys/kernel.h>
72 #include <sys/syslog.h>
73
74 #include <net/if.h>
75 #include <net/route.h>
76
77 #include <netinet/in.h>
78 #include <netinet/in_var.h>
79 #include <netinet/in_systm.h>
80 #include <netinet/ip.h>
81 #include <netinet/ip_var.h>
82 #include <netinet6/in6_var.h>
83 #include <netinet/ip6.h>
84 #include <netinet6/ip6_var.h>
85 #include <netinet/icmp6.h>
86 #include <netinet6/nd6.h>
87
88 #include <netinet/in_pcb.h>
89
90 #if IPSEC
91 #include <netinet6/ipsec.h>
92 #if INET6
93 #include <netinet6/ipsec6.h>
94 #endif
95 #include <netkey/key.h>
96 extern int ipsec_bypass;
97 #endif /* IPSEC */
98 extern lck_mtx_t *ip6_mutex;
99
100 #include <netinet6/ip6_fw.h>
101
102 #include <net/net_osdep.h>
103
104 #if PF
105 #include <net/pfvar.h>
106 #endif /* PF */
107
108 /*
109 * Forward a packet. If some error occurs return the sender
110 * an icmp packet. Note we can't always generate a meaningful
111 * icmp message because icmp doesn't have a large enough repertoire
112 * of codes and types.
113 *
114 * If not forwarding, just drop the packet. This could be confusing
115 * if ipforwarding was zero but some routing protocol was advancing
116 * us as a gateway to somewhere. However, we must let the routing
117 * protocol deal with that.
118 *
119 */
120
121 void
122 ip6_forward(struct mbuf *m, struct route_in6 *ip6forward_rt,
123 int srcrt, int locked)
124 {
125 struct ip6_hdr *ip6 = mtod(m, struct ip6_hdr *);
126 struct sockaddr_in6 *dst;
127 struct rtentry *rt;
128 int error, type = 0, code = 0;
129 struct mbuf *mcopy = NULL;
130 struct ifnet *ifp, *origifp; /* maybe unnecessary */
131 #if IPSEC
132 struct secpolicy *sp = NULL;
133 #endif
134 struct timeval timenow;
135 int tunneledv4 = 0;
136
137 getmicrotime(&timenow);
138
139
140 #if IPSEC
141 /*
142 * Check AH/ESP integrity.
143 */
144 /*
145 * Don't increment ip6s_cantforward because this is the check
146 * before forwarding packet actually.
147 */
148 if (ipsec_bypass == 0) {
149 if (ipsec6_in_reject(m, NULL)) {
150 IPSEC_STAT_INCREMENT(ipsec6stat.in_polvio);
151 m_freem(m);
152 return;
153 }
154 }
155 #endif /*IPSEC*/
156
157 /*
158 * Do not forward packets to multicast destination (should be handled
159 * by ip6_mforward().
160 * Do not forward packets with unspecified source. It was discussed
161 * in July 2000, on ipngwg mailing list.
162 */
163 if ((m->m_flags & (M_BCAST|M_MCAST)) != 0 ||
164 IN6_IS_ADDR_MULTICAST(&ip6->ip6_dst) ||
165 IN6_IS_ADDR_UNSPECIFIED(&ip6->ip6_src)) {
166 ip6stat.ip6s_cantforward++;
167 /* XXX in6_ifstat_inc(rt->rt_ifp, ifs6_in_discard) */
168 if (ip6_log_time + ip6_log_interval < timenow.tv_sec) {
169 ip6_log_time = timenow.tv_sec;
170 log(LOG_DEBUG,
171 "cannot forward "
172 "from %s to %s nxt %d received on %s\n",
173 ip6_sprintf(&ip6->ip6_src),
174 ip6_sprintf(&ip6->ip6_dst),
175 ip6->ip6_nxt,
176 if_name(m->m_pkthdr.rcvif));
177 }
178 m_freem(m);
179 return;
180 }
181
182 if (ip6->ip6_hlim <= IPV6_HLIMDEC) {
183 /* XXX in6_ifstat_inc(rt->rt_ifp, ifs6_in_discard) */
184 if (locked)
185 lck_mtx_unlock(ip6_mutex);
186 icmp6_error(m, ICMP6_TIME_EXCEEDED,
187 ICMP6_TIME_EXCEED_TRANSIT, 0);
188 if (locked)
189 lck_mtx_lock(ip6_mutex);
190 return;
191 }
192 ip6->ip6_hlim -= IPV6_HLIMDEC;
193
194 /*
195 * Save at most ICMPV6_PLD_MAXLEN (= the min IPv6 MTU -
196 * size of IPv6 + ICMPv6 headers) bytes of the packet in case
197 * we need to generate an ICMP6 message to the src.
198 * Thanks to M_EXT, in most cases copy will not occur.
199 *
200 * It is important to save it before IPsec processing as IPsec
201 * processing may modify the mbuf.
202 */
203 mcopy = m_copy(m, 0, imin(m->m_pkthdr.len, ICMPV6_PLD_MAXLEN));
204
205 #if IPSEC
206 if (ipsec_bypass != 0)
207 goto skip_ipsec;
208 /* get a security policy for this packet */
209 sp = ipsec6_getpolicybyaddr(m, IPSEC_DIR_OUTBOUND, IP_FORWARDING,
210 &error);
211 if (sp == NULL) {
212 IPSEC_STAT_INCREMENT(ipsec6stat.out_inval);
213 ip6stat.ip6s_cantforward++;
214 if (mcopy) {
215 #if 0
216 /* XXX: what icmp ? */
217 #else
218 m_freem(mcopy);
219 #endif
220 }
221 m_freem(m);
222 return;
223 }
224
225 error = 0;
226
227 /* check policy */
228 switch (sp->policy) {
229 case IPSEC_POLICY_DISCARD:
230 case IPSEC_POLICY_GENERATE:
231 /*
232 * This packet is just discarded.
233 */
234 IPSEC_STAT_INCREMENT(ipsec6stat.out_polvio);
235 ip6stat.ip6s_cantforward++;
236 key_freesp(sp, KEY_SADB_UNLOCKED);
237 if (mcopy) {
238 #if 0
239 /* XXX: what icmp ? */
240 #else
241 m_freem(mcopy);
242 #endif
243 }
244 m_freem(m);
245 return;
246
247 case IPSEC_POLICY_BYPASS:
248 case IPSEC_POLICY_NONE:
249 /* no need to do IPsec. */
250 key_freesp(sp, KEY_SADB_UNLOCKED);
251 goto skip_ipsec;
252
253 case IPSEC_POLICY_IPSEC:
254 if (sp->req == NULL) {
255 /* XXX should be panic ? */
256 printf("ip6_forward: No IPsec request specified.\n");
257 ip6stat.ip6s_cantforward++;
258 key_freesp(sp, KEY_SADB_UNLOCKED);
259 if (mcopy) {
260 #if 0
261 /* XXX: what icmp ? */
262 #else
263 m_freem(mcopy);
264 #endif
265 }
266 m_freem(m);
267 return;
268 }
269 /* do IPsec */
270 break;
271
272 case IPSEC_POLICY_ENTRUST:
273 default:
274 /* should be panic ?? */
275 printf("ip6_forward: Invalid policy found. %d\n", sp->policy);
276 key_freesp(sp, KEY_SADB_UNLOCKED);
277 goto skip_ipsec;
278 }
279
280 {
281 struct ipsec_output_state state;
282
283 /*
284 * All the extension headers will become inaccessible
285 * (since they can be encrypted).
286 * Don't panic, we need no more updates to extension headers
287 * on inner IPv6 packet (since they are now encapsulated).
288 *
289 * IPv6 [ESP|AH] IPv6 [extension headers] payload
290 */
291 bzero(&state, sizeof(state));
292 state.m = m;
293 state.ro = NULL; /* update at ipsec6_output_tunnel() */
294 state.dst = NULL; /* update at ipsec6_output_tunnel() */
295
296 if (locked)
297 lck_mtx_unlock(ip6_mutex);
298 error = ipsec6_output_tunnel(&state, sp, 0, &tunneledv4);
299 if (locked)
300 lck_mtx_lock(ip6_mutex);
301 key_freesp(sp, KEY_SADB_UNLOCKED);
302 if (tunneledv4)
303 return; /* packet is gone - sent over IPv4 */
304
305 m = state.m;
306 if (error) {
307 /* mbuf is already reclaimed in ipsec6_output_tunnel. */
308 switch (error) {
309 case EHOSTUNREACH:
310 case ENETUNREACH:
311 case EMSGSIZE:
312 case ENOBUFS:
313 case ENOMEM:
314 break;
315 default:
316 printf("ip6_output (ipsec): error code %d\n", error);
317 /* fall through */
318 case ENOENT:
319 /* don't show these error codes to the user */
320 break;
321 }
322 ip6stat.ip6s_cantforward++;
323 if (mcopy) {
324 #if 0
325 /* XXX: what icmp ? */
326 #else
327 m_freem(mcopy);
328 #endif
329 }
330 m_freem(m);
331 return;
332 }
333 }
334 skip_ipsec:
335 #endif /* IPSEC */
336
337 /*
338 * If "locked", ip6forward_rt points to the globally defined
339 * struct route cache which requires ip6_mutex, e.g. when this
340 * is called from ip6_input(). Else the caller is responsible
341 * for the struct route and its serialization (if needed), e.g.
342 * when this is called from ip6_rthdr0().
343 */
344 if (locked)
345 lck_mtx_assert(ip6_mutex, LCK_MTX_ASSERT_OWNED);
346 dst = (struct sockaddr_in6 *)&ip6forward_rt->ro_dst;
347 if ((rt = ip6forward_rt->ro_rt) != NULL) {
348 RT_LOCK(rt);
349 /* Take an extra ref for ourselves */
350 RT_ADDREF_LOCKED(rt);
351 }
352
353 if (!srcrt) {
354 /*
355 * ip6forward_rt->ro_dst.sin6_addr is equal to ip6->ip6_dst
356 */
357 if (rt == NULL || !(rt->rt_flags & RTF_UP) ||
358 rt->generation_id != route_generation) {
359 if (rt != NULL) {
360 /* Release extra ref */
361 RT_REMREF_LOCKED(rt);
362 RT_UNLOCK(rt);
363 rtfree(rt);
364 ip6forward_rt->ro_rt = NULL;
365 }
366 /* this probably fails but give it a try again */
367 rtalloc_ign((struct route *)ip6forward_rt,
368 RTF_PRCLONING);
369 if ((rt = ip6forward_rt->ro_rt) != NULL) {
370 RT_LOCK(rt);
371 /* Take an extra ref for ourselves */
372 RT_ADDREF_LOCKED(rt);
373 }
374 }
375
376 if (rt == NULL) {
377 ip6stat.ip6s_noroute++;
378 in6_ifstat_inc(m->m_pkthdr.rcvif, ifs6_in_noroute);
379 if (mcopy) {
380 if (locked)
381 lck_mtx_unlock(ip6_mutex);
382 icmp6_error(mcopy, ICMP6_DST_UNREACH,
383 ICMP6_DST_UNREACH_NOROUTE, 0);
384 if (locked)
385 lck_mtx_lock(ip6_mutex);
386 }
387 m_freem(m);
388 return;
389 }
390 RT_LOCK_ASSERT_HELD(rt);
391 } else if (rt == NULL || !(rt->rt_flags & RTF_UP) ||
392 !IN6_ARE_ADDR_EQUAL(&ip6->ip6_dst, &dst->sin6_addr) ||
393 rt->generation_id != route_generation) {
394 if (rt != NULL) {
395 /* Release extra ref */
396 RT_REMREF_LOCKED(rt);
397 RT_UNLOCK(rt);
398 rtfree(rt);
399 ip6forward_rt->ro_rt = NULL;
400 }
401 bzero(dst, sizeof(*dst));
402 dst->sin6_len = sizeof(struct sockaddr_in6);
403 dst->sin6_family = AF_INET6;
404 dst->sin6_addr = ip6->ip6_dst;
405
406 rtalloc_ign((struct route *)ip6forward_rt, RTF_PRCLONING);
407 if ((rt = ip6forward_rt->ro_rt) == NULL) {
408 ip6stat.ip6s_noroute++;
409 in6_ifstat_inc(m->m_pkthdr.rcvif, ifs6_in_noroute);
410 if (mcopy) {
411 if (locked)
412 lck_mtx_unlock(ip6_mutex);
413 icmp6_error(mcopy, ICMP6_DST_UNREACH,
414 ICMP6_DST_UNREACH_NOROUTE, 0);
415 if (locked)
416 lck_mtx_lock(ip6_mutex);
417 }
418 m_freem(m);
419 return;
420 }
421 RT_LOCK(rt);
422 /* Take an extra ref for ourselves */
423 RT_ADDREF_LOCKED(rt);
424 }
425
426 /*
427 * Scope check: if a packet can't be delivered to its destination
428 * for the reason that the destination is beyond the scope of the
429 * source address, discard the packet and return an icmp6 destination
430 * unreachable error with Code 2 (beyond scope of source address).
431 * [draft-ietf-ipngwg-icmp-v3-02.txt, Section 3.1]
432 */
433 if (in6_addr2scopeid(m->m_pkthdr.rcvif, &ip6->ip6_src) !=
434 in6_addr2scopeid(rt->rt_ifp, &ip6->ip6_src)) {
435 ip6stat.ip6s_cantforward++;
436 ip6stat.ip6s_badscope++;
437 in6_ifstat_inc(rt->rt_ifp, ifs6_in_discard);
438
439 if (ip6_log_time + ip6_log_interval < timenow.tv_sec) {
440 ip6_log_time = timenow.tv_sec;
441 log(LOG_DEBUG,
442 "cannot forward "
443 "src %s, dst %s, nxt %d, rcvif %s, outif %s\n",
444 ip6_sprintf(&ip6->ip6_src),
445 ip6_sprintf(&ip6->ip6_dst),
446 ip6->ip6_nxt,
447 if_name(m->m_pkthdr.rcvif), if_name(rt->rt_ifp));
448 }
449 /* Release extra ref */
450 RT_REMREF_LOCKED(rt);
451 RT_UNLOCK(rt);
452 if (mcopy) {
453 if (locked)
454 lck_mtx_unlock(ip6_mutex);
455 icmp6_error(mcopy, ICMP6_DST_UNREACH,
456 ICMP6_DST_UNREACH_BEYONDSCOPE, 0);
457 if (locked)
458 lck_mtx_lock(ip6_mutex);
459 }
460 m_freem(m);
461 return;
462 }
463
464 if (m->m_pkthdr.len > rt->rt_ifp->if_mtu) {
465 in6_ifstat_inc(rt->rt_ifp, ifs6_in_toobig);
466 if (mcopy) {
467 uint32_t mtu;
468 #if IPSEC
469 struct secpolicy *sp2;
470 int ipsecerror;
471 size_t ipsechdrsiz;
472 #endif
473
474 mtu = rt->rt_ifp->if_mtu;
475 #if IPSEC
476 /*
477 * When we do IPsec tunnel ingress, we need to play
478 * with if_mtu value (decrement IPsec header size
479 * from mtu value). The code is much simpler than v4
480 * case, as we have the outgoing interface for
481 * encapsulated packet as "rt->rt_ifp".
482 */
483 sp2 = ipsec6_getpolicybyaddr(mcopy, IPSEC_DIR_OUTBOUND,
484 IP_FORWARDING, &ipsecerror);
485 if (sp2) {
486 ipsechdrsiz = ipsec6_hdrsiz(mcopy,
487 IPSEC_DIR_OUTBOUND, NULL);
488 if (ipsechdrsiz < mtu)
489 mtu -= ipsechdrsiz;
490 key_freesp(sp2, KEY_SADB_UNLOCKED);
491 }
492 /*
493 * if mtu becomes less than minimum MTU,
494 * tell minimum MTU (and I'll need to fragment it).
495 */
496 if (mtu < IPV6_MMTU)
497 mtu = IPV6_MMTU;
498 #endif
499 /* Release extra ref */
500 RT_REMREF_LOCKED(rt);
501 RT_UNLOCK(rt);
502 if (locked)
503 lck_mtx_unlock(ip6_mutex);
504 icmp6_error(mcopy, ICMP6_PACKET_TOO_BIG, 0, mtu);
505 if (locked)
506 lck_mtx_lock(ip6_mutex);
507 } else {
508 /* Release extra ref */
509 RT_REMREF_LOCKED(rt);
510 RT_UNLOCK(rt);
511 }
512 m_freem(m);
513 return;
514 }
515
516 if (rt->rt_flags & RTF_GATEWAY)
517 dst = (struct sockaddr_in6 *)rt->rt_gateway;
518
519 /*
520 * If we are to forward the packet using the same interface
521 * as one we got the packet from, perhaps we should send a redirect
522 * to sender to shortcut a hop.
523 * Only send redirect if source is sending directly to us,
524 * and if packet was not source routed (or has any options).
525 * Also, don't send redirect if forwarding using a route
526 * modified by a redirect.
527 */
528 if (rt->rt_ifp == m->m_pkthdr.rcvif && !srcrt &&
529 (rt->rt_flags & (RTF_DYNAMIC|RTF_MODIFIED)) == 0) {
530 if ((rt->rt_ifp->if_flags & IFF_POINTOPOINT) != 0) {
531 /*
532 * If the incoming interface is equal to the outgoing
533 * one, and the link attached to the interface is
534 * point-to-point, then it will be highly probable
535 * that a routing loop occurs. Thus, we immediately
536 * drop the packet and send an ICMPv6 error message.
537 *
538 * type/code is based on suggestion by Rich Draves.
539 * not sure if it is the best pick.
540 */
541 RT_REMREF_LOCKED(rt); /* Release extra ref */
542 RT_UNLOCK(rt);
543 if (locked)
544 lck_mtx_unlock(ip6_mutex);
545 icmp6_error(mcopy, ICMP6_DST_UNREACH,
546 ICMP6_DST_UNREACH_ADDR, 0);
547 if (locked)
548 lck_mtx_lock(ip6_mutex);
549 m_freem(m);
550 return;
551 }
552 type = ND_REDIRECT;
553 }
554
555 /*
556 * Check with the firewall...
557 */
558 if (ip6_fw_enable && ip6_fw_chk_ptr) {
559 u_short port = 0;
560 ifp = rt->rt_ifp;
561 /* Drop the lock but retain the extra ref */
562 RT_UNLOCK(rt);
563 /* If ipfw says divert, we have to just drop packet */
564 if (ip6_fw_chk_ptr(&ip6, ifp, &port, &m)) {
565 m_freem(m);
566 goto freecopy;
567 }
568 if (!m) {
569 goto freecopy;
570 }
571 /* We still have the extra ref on rt */
572 RT_LOCK(rt);
573 }
574
575 /*
576 * Fake scoped addresses. Note that even link-local source or
577 * destinaion can appear, if the originating node just sends the
578 * packet to us (without address resolution for the destination).
579 * Since both icmp6_error and icmp6_redirect_output fill the embedded
580 * link identifiers, we can do this stuff after making a copy for
581 * returning an error.
582 */
583 if ((rt->rt_ifp->if_flags & IFF_LOOPBACK) != 0) {
584 /*
585 * See corresponding comments in ip6_output.
586 * XXX: but is it possible that ip6_forward() sends a packet
587 * to a loopback interface? I don't think so, and thus
588 * I bark here. (jinmei@kame.net)
589 * XXX: it is common to route invalid packets to loopback.
590 * also, the codepath will be visited on use of ::1 in
591 * rthdr. (itojun)
592 */
593 #if 1
594 if (0)
595 #else
596 if ((rt->rt_flags & (RTF_BLACKHOLE|RTF_REJECT)) == 0)
597 #endif
598 {
599 printf("ip6_forward: outgoing interface is loopback. "
600 "src %s, dst %s, nxt %d, rcvif %s, outif %s\n",
601 ip6_sprintf(&ip6->ip6_src),
602 ip6_sprintf(&ip6->ip6_dst),
603 ip6->ip6_nxt, if_name(m->m_pkthdr.rcvif),
604 if_name(rt->rt_ifp));
605 }
606
607 /* we can just use rcvif in forwarding. */
608 origifp = m->m_pkthdr.rcvif;
609 }
610 else
611 origifp = rt->rt_ifp;
612 #ifndef SCOPEDROUTING
613 /*
614 * clear embedded scope identifiers if necessary.
615 * in6_clearscope will touch the addresses only when necessary.
616 */
617 in6_clearscope(&ip6->ip6_src);
618 in6_clearscope(&ip6->ip6_dst);
619 #endif
620
621 ifp = rt->rt_ifp;
622 /* Drop the lock but retain the extra ref */
623 RT_UNLOCK(rt);
624
625 #if PF
626 if (locked)
627 lck_mtx_unlock(ip6_mutex);
628
629 /* Invoke outbound packet filter */
630 error = pf_af_hook(ifp, NULL, &m, AF_INET6, FALSE);
631
632 if (locked)
633 lck_mtx_lock(ip6_mutex);
634
635 if (error) {
636 if (m != NULL) {
637 panic("%s: unexpected packet %p\n", __func__, m);
638 /* NOTREACHED */
639 }
640 /* Already freed by callee */
641 goto senderr;
642 }
643 ip6 = mtod(m, struct ip6_hdr *);
644 #endif /* PF */
645
646 error = nd6_output(ifp, origifp, m, dst, rt, locked);
647 if (error) {
648 in6_ifstat_inc(ifp, ifs6_out_discard);
649 ip6stat.ip6s_cantforward++;
650 } else {
651 ip6stat.ip6s_forward++;
652 in6_ifstat_inc(ifp, ifs6_out_forward);
653 if (type)
654 ip6stat.ip6s_redirectsent++;
655 else {
656 if (mcopy) {
657 goto freecopy;
658 }
659 }
660 }
661 #if PF
662 senderr:
663 #endif /* PF */
664 if (mcopy == NULL) {
665 /* Release extra ref */
666 RT_REMREF(rt);
667 return;
668 }
669 switch (error) {
670 case 0:
671 #if 1
672 if (type == ND_REDIRECT) {
673 icmp6_redirect_output(mcopy, rt);
674 /* Release extra ref */
675 RT_REMREF(rt);
676 return;
677 }
678 #endif
679 goto freecopy;
680
681 case EMSGSIZE:
682 /* xxx MTU is constant in PPP? */
683 goto freecopy;
684
685 case ENOBUFS:
686 /* Tell source to slow down like source quench in IP? */
687 goto freecopy;
688
689 case ENETUNREACH: /* shouldn't happen, checked above */
690 case EHOSTUNREACH:
691 case ENETDOWN:
692 case EHOSTDOWN:
693 default:
694 type = ICMP6_DST_UNREACH;
695 code = ICMP6_DST_UNREACH_ADDR;
696 break;
697 }
698 if (locked)
699 lck_mtx_unlock(ip6_mutex);
700 icmp6_error(mcopy, type, code, 0);
701 if (locked)
702 lck_mtx_lock(ip6_mutex);
703 /* Release extra ref */
704 RT_REMREF(rt);
705 return;
706
707 freecopy:
708 m_freem(mcopy);
709 /* Release extra ref */
710 RT_REMREF(rt);
711 return;
712 }