| 1 |
/*- |
| 2 |
* SPDX-License-Identifier: BSD-3-Clause |
| 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 |
* $KAME: ip6_forward.c,v 1.69 2001/05/17 03:48:30 itojun Exp $ |
| 32 |
*/ |
| 33 |
|
| 34 |
#include <sys/cdefs.h> |
| 35 |
__FBSDID("$FreeBSD$"); |
| 36 |
|
| 37 |
#include "opt_inet.h" |
| 38 |
#include "opt_inet6.h" |
| 39 |
#include "opt_ipsec.h" |
| 40 |
#include "opt_ipstealth.h" |
| 41 |
#include "opt_sctp.h" |
| 42 |
|
| 43 |
#include <sys/param.h> |
| 44 |
#include <sys/systm.h> |
| 45 |
#include <sys/malloc.h> |
| 46 |
#include <sys/mbuf.h> |
| 47 |
#include <sys/domain.h> |
| 48 |
#include <sys/protosw.h> |
| 49 |
#include <sys/socket.h> |
| 50 |
#include <sys/errno.h> |
| 51 |
#include <sys/time.h> |
| 52 |
#include <sys/kernel.h> |
| 53 |
#include <sys/syslog.h> |
| 54 |
|
| 55 |
#include <net/if.h> |
| 56 |
#include <net/if_var.h> |
| 57 |
#include <net/netisr.h> |
| 58 |
#include <net/route.h> |
| 59 |
#include <net/route/nhop.h> |
| 60 |
#include <net/pfil.h> |
| 61 |
|
| 62 |
#include <netinet/in.h> |
| 63 |
#include <netinet/in_var.h> |
| 64 |
#include <netinet/in_systm.h> |
| 65 |
#include <netinet/ip.h> |
| 66 |
#include <netinet/ip_var.h> |
| 67 |
#include <netinet6/in6_var.h> |
| 68 |
#include <netinet/ip6.h> |
| 69 |
#include <netinet6/in6_fib.h> |
| 70 |
#include <netinet6/ip6_var.h> |
| 71 |
#include <netinet6/scope6_var.h> |
| 72 |
#include <netinet/icmp6.h> |
| 73 |
#include <netinet6/nd6.h> |
| 74 |
|
| 75 |
#include <netinet/in_pcb.h> |
| 76 |
|
| 77 |
#include <netipsec/ipsec_support.h> |
| 78 |
|
| 79 |
/* |
| 80 |
* Forward a packet. If some error occurs return the sender |
| 81 |
* an icmp packet. Note we can't always generate a meaningful |
| 82 |
* icmp message because icmp doesn't have a large enough repertoire |
| 83 |
* of codes and types. |
| 84 |
* |
| 85 |
* If not forwarding, just drop the packet. This could be confusing |
| 86 |
* if ipforwarding was zero but some routing protocol was advancing |
| 87 |
* us as a gateway to somewhere. However, we must let the routing |
| 88 |
* protocol deal with that. |
| 89 |
* |
| 90 |
*/ |
| 91 |
void |
| 92 |
ip6_forward(struct mbuf *m, int srcrt) |
| 93 |
{ |
| 94 |
struct ip6_hdr *ip6 = mtod(m, struct ip6_hdr *); |
| 95 |
struct sockaddr_in6 dst; |
| 96 |
struct nhop_object *nh = NULL; |
| 97 |
int error, type = 0, code = 0; |
| 98 |
struct mbuf *mcopy = NULL; |
| 99 |
struct ifnet *origifp; /* maybe unnecessary */ |
| 100 |
u_int32_t inzone, outzone; |
| 101 |
struct in6_addr odst; |
| 102 |
struct m_tag *fwd_tag; |
| 103 |
char ip6bufs[INET6_ADDRSTRLEN], ip6bufd[INET6_ADDRSTRLEN]; |
| 104 |
|
| 105 |
/* |
| 106 |
* Do not forward packets to multicast destination (should be handled |
| 107 |
* by ip6_mforward(). |
| 108 |
* Do not forward packets with unspecified source. It was discussed |
| 109 |
* in July 2000, on the ipngwg mailing list. |
| 110 |
*/ |
| 111 |
if ((m->m_flags & (M_BCAST|M_MCAST)) != 0 || |
| 112 |
IN6_IS_ADDR_MULTICAST(&ip6->ip6_dst) || |
| 113 |
IN6_IS_ADDR_UNSPECIFIED(&ip6->ip6_src)) { |
| 114 |
IP6STAT_INC(ip6s_cantforward); |
| 115 |
/* XXX in6_ifstat_inc(rt->rt_ifp, ifs6_in_discard) */ |
| 116 |
if (V_ip6_log_time + V_ip6_log_interval < time_uptime) { |
| 117 |
V_ip6_log_time = time_uptime; |
| 118 |
log(LOG_DEBUG, |
| 119 |
"cannot forward " |
| 120 |
"from %s to %s nxt %d received on %s\n", |
| 121 |
ip6_sprintf(ip6bufs, &ip6->ip6_src), |
| 122 |
ip6_sprintf(ip6bufd, &ip6->ip6_dst), |
| 123 |
ip6->ip6_nxt, |
| 124 |
if_name(m->m_pkthdr.rcvif)); |
| 125 |
} |
| 126 |
m_freem(m); |
| 127 |
return; |
| 128 |
} |
| 129 |
|
| 130 |
if ( |
| 131 |
#ifdef IPSTEALTH |
| 132 |
V_ip6stealth == 0 && |
| 133 |
#endif |
| 134 |
ip6->ip6_hlim <= IPV6_HLIMDEC) { |
| 135 |
/* XXX in6_ifstat_inc(rt->rt_ifp, ifs6_in_discard) */ |
| 136 |
icmp6_error(m, ICMP6_TIME_EXCEEDED, |
| 137 |
ICMP6_TIME_EXCEED_TRANSIT, 0); |
| 138 |
return; |
| 139 |
} |
| 140 |
|
| 141 |
/* |
| 142 |
* Save at most ICMPV6_PLD_MAXLEN (= the min IPv6 MTU - |
| 143 |
* size of IPv6 + ICMPv6 headers) bytes of the packet in case |
| 144 |
* we need to generate an ICMP6 message to the src. |
| 145 |
* Thanks to M_EXT, in most cases copy will not occur. |
| 146 |
* |
| 147 |
* It is important to save it before IPsec processing as IPsec |
| 148 |
* processing may modify the mbuf. |
| 149 |
*/ |
| 150 |
mcopy = m_copym(m, 0, imin(m->m_pkthdr.len, ICMPV6_PLD_MAXLEN), |
| 151 |
M_NOWAIT); |
| 152 |
#ifdef IPSTEALTH |
| 153 |
if (V_ip6stealth == 0) |
| 154 |
#endif |
| 155 |
ip6->ip6_hlim -= IPV6_HLIMDEC; |
| 156 |
|
| 157 |
#if defined(IPSEC) || defined(IPSEC_SUPPORT) |
| 158 |
if (IPSEC_ENABLED(ipv6)) { |
| 159 |
if ((error = IPSEC_FORWARD(ipv6, m)) != 0) { |
| 160 |
/* mbuf consumed by IPsec */ |
| 161 |
m_freem(mcopy); |
| 162 |
if (error != EINPROGRESS) |
| 163 |
IP6STAT_INC(ip6s_cantforward); |
| 164 |
return; |
| 165 |
} |
| 166 |
/* No IPsec processing required */ |
| 167 |
} |
| 168 |
#endif |
| 169 |
/* |
| 170 |
* ip6_forward() operates with IPv6 addresses with deembedded scope. |
| 171 |
* |
| 172 |
* There are 3 sources of IPv6 destination address: |
| 173 |
* |
| 174 |
* 1) ip6_input(), where ip6_dst contains deembedded address. |
| 175 |
* In order to deal with forwarding of link-local packets, |
| 176 |
* calculate the scope based on input interface (RFC 4007, clause 9). |
| 177 |
* 2) packet filters changing ip6_dst directly. It would embed scope |
| 178 |
* for LL addresses, so in6_localip() performs properly. |
| 179 |
* 3) packet filters attaching PACKET_TAG_IPFORWARD would embed |
| 180 |
* scope for the nexthop. |
| 181 |
*/ |
| 182 |
bzero(&dst, sizeof(struct sockaddr_in6)); |
| 183 |
dst.sin6_family = AF_INET6; |
| 184 |
dst.sin6_addr = ip6->ip6_dst; |
| 185 |
dst.sin6_scope_id = in6_get_unicast_scopeid(&ip6->ip6_dst, m->m_pkthdr.rcvif); |
| 186 |
again: |
| 187 |
nh = fib6_lookup(M_GETFIB(m), &dst.sin6_addr, dst.sin6_scope_id, |
| 188 |
NHR_REF, m->m_pkthdr.flowid); |
| 189 |
if (nh == NULL) { |
| 190 |
IP6STAT_INC(ip6s_noroute); |
| 191 |
in6_ifstat_inc(m->m_pkthdr.rcvif, ifs6_in_noroute); |
| 192 |
if (mcopy) { |
| 193 |
icmp6_error(mcopy, ICMP6_DST_UNREACH, |
| 194 |
ICMP6_DST_UNREACH_NOROUTE, 0); |
| 195 |
} |
| 196 |
goto bad; |
| 197 |
} |
| 198 |
|
| 199 |
/* |
| 200 |
* Source scope check: if a packet can't be delivered to its |
| 201 |
* destination for the reason that the destination is beyond the scope |
| 202 |
* of the source address, discard the packet and return an icmp6 |
| 203 |
* destination unreachable error with Code 2 (beyond scope of source |
| 204 |
* address). |
| 205 |
* [draft-ietf-ipngwg-icmp-v3-04.txt, Section 3.1] |
| 206 |
*/ |
| 207 |
outzone = in6_get_unicast_scopeid(&ip6->ip6_src, nh->nh_ifp); |
| 208 |
inzone = in6_get_unicast_scopeid(&ip6->ip6_src, m->m_pkthdr.rcvif); |
| 209 |
if (inzone != outzone) { |
| 210 |
IP6STAT_INC(ip6s_cantforward); |
| 211 |
IP6STAT_INC(ip6s_badscope); |
| 212 |
in6_ifstat_inc(nh->nh_ifp, ifs6_in_discard); |
| 213 |
|
| 214 |
if (V_ip6_log_time + V_ip6_log_interval < time_uptime) { |
| 215 |
V_ip6_log_time = time_uptime; |
| 216 |
log(LOG_DEBUG, |
| 217 |
"cannot forward " |
| 218 |
"src %s, dst %s, nxt %d, rcvif %s, outif %s\n", |
| 219 |
ip6_sprintf(ip6bufs, &ip6->ip6_src), |
| 220 |
ip6_sprintf(ip6bufd, &ip6->ip6_dst), |
| 221 |
ip6->ip6_nxt, |
| 222 |
if_name(m->m_pkthdr.rcvif), if_name(nh->nh_ifp)); |
| 223 |
} |
| 224 |
if (mcopy) |
| 225 |
icmp6_error(mcopy, ICMP6_DST_UNREACH, |
| 226 |
ICMP6_DST_UNREACH_BEYONDSCOPE, 0); |
| 227 |
goto bad; |
| 228 |
} |
| 229 |
|
| 230 |
/* |
| 231 |
* Destination scope check: if a packet is going to break the scope |
| 232 |
* zone of packet's destination address, discard it. This case should |
| 233 |
* usually be prevented by appropriately-configured routing table, but |
| 234 |
* we need an explicit check because we may mistakenly forward the |
| 235 |
* packet to a different zone by (e.g.) a default route. |
| 236 |
*/ |
| 237 |
inzone = in6_get_unicast_scopeid(&ip6->ip6_dst, m->m_pkthdr.rcvif); |
| 238 |
outzone = in6_get_unicast_scopeid(&ip6->ip6_dst, nh->nh_ifp); |
| 239 |
|
| 240 |
if (inzone != outzone) { |
| 241 |
IP6STAT_INC(ip6s_cantforward); |
| 242 |
IP6STAT_INC(ip6s_badscope); |
| 243 |
goto bad; |
| 244 |
} |
| 245 |
|
| 246 |
if (nh->nh_flags & NHF_GATEWAY) { |
| 247 |
/* Store gateway address in deembedded form */ |
| 248 |
dst.sin6_addr = nh->gw6_sa.sin6_addr; |
| 249 |
dst.sin6_scope_id = ntohs(in6_getscope(&dst.sin6_addr)); |
| 250 |
in6_clearscope(&dst.sin6_addr); |
| 251 |
} |
| 252 |
|
| 253 |
/* |
| 254 |
* If we are to forward the packet using the same interface |
| 255 |
* as one we got the packet from, perhaps we should send a redirect |
| 256 |
* to sender to shortcut a hop. |
| 257 |
* Only send redirect if source is sending directly to us, |
| 258 |
* and if packet was not source routed (or has any options). |
| 259 |
* Also, don't send redirect if forwarding using a route |
| 260 |
* modified by a redirect. |
| 261 |
*/ |
| 262 |
if (V_ip6_sendredirects && nh->nh_ifp == m->m_pkthdr.rcvif && !srcrt && |
| 263 |
(nh->nh_flags & NHF_REDIRECT) == 0) { |
| 264 |
if ((nh->nh_ifp->if_flags & IFF_POINTOPOINT) != 0) { |
| 265 |
/* |
| 266 |
* If the incoming interface is equal to the outgoing |
| 267 |
* one, and the link attached to the interface is |
| 268 |
* point-to-point, then it will be highly probable |
| 269 |
* that a routing loop occurs. Thus, we immediately |
| 270 |
* drop the packet and send an ICMPv6 error message. |
| 271 |
* |
| 272 |
* type/code is based on suggestion by Rich Draves. |
| 273 |
* not sure if it is the best pick. |
| 274 |
*/ |
| 275 |
icmp6_error(mcopy, ICMP6_DST_UNREACH, |
| 276 |
ICMP6_DST_UNREACH_ADDR, 0); |
| 277 |
goto bad; |
| 278 |
} |
| 279 |
type = ND_REDIRECT; |
| 280 |
} |
| 281 |
|
| 282 |
/* |
| 283 |
* Fake scoped addresses. Note that even link-local source or |
| 284 |
* destinaion can appear, if the originating node just sends the |
| 285 |
* packet to us (without address resolution for the destination). |
| 286 |
* Since both icmp6_error and icmp6_redirect_output fill the embedded |
| 287 |
* link identifiers, we can do this stuff after making a copy for |
| 288 |
* returning an error. |
| 289 |
*/ |
| 290 |
if ((nh->nh_ifp->if_flags & IFF_LOOPBACK) != 0) { |
| 291 |
/* |
| 292 |
* See corresponding comments in ip6_output. |
| 293 |
* XXX: but is it possible that ip6_forward() sends a packet |
| 294 |
* to a loopback interface? I don't think so, and thus |
| 295 |
* I bark here. (jinmei@kame.net) |
| 296 |
* XXX: it is common to route invalid packets to loopback. |
| 297 |
* also, the codepath will be visited on use of ::1 in |
| 298 |
* rthdr. (itojun) |
| 299 |
*/ |
| 300 |
#if 1 |
| 301 |
if (0) |
| 302 |
#else |
| 303 |
if ((rt->rt_flags & (RTF_BLACKHOLE|RTF_REJECT)) == 0) |
| 304 |
#endif |
| 305 |
{ |
| 306 |
printf("ip6_forward: outgoing interface is loopback. " |
| 307 |
"src %s, dst %s, nxt %d, rcvif %s, outif %s\n", |
| 308 |
ip6_sprintf(ip6bufs, &ip6->ip6_src), |
| 309 |
ip6_sprintf(ip6bufd, &ip6->ip6_dst), |
| 310 |
ip6->ip6_nxt, if_name(m->m_pkthdr.rcvif), |
| 311 |
if_name(nh->nh_ifp)); |
| 312 |
} |
| 313 |
|
| 314 |
/* we can just use rcvif in forwarding. */ |
| 315 |
origifp = m->m_pkthdr.rcvif; |
| 316 |
} |
| 317 |
else |
| 318 |
origifp = nh->nh_ifp; |
| 319 |
/* |
| 320 |
* clear embedded scope identifiers if necessary. |
| 321 |
* in6_clearscope will touch the addresses only when necessary. |
| 322 |
*/ |
| 323 |
in6_clearscope(&ip6->ip6_src); |
| 324 |
in6_clearscope(&ip6->ip6_dst); |
| 325 |
|
| 326 |
/* Jump over all PFIL processing if hooks are not active. */ |
| 327 |
if (!PFIL_HOOKED_OUT(V_inet6_pfil_head)) |
| 328 |
goto pass; |
| 329 |
|
| 330 |
odst = ip6->ip6_dst; |
| 331 |
/* Run through list of hooks for forwarded packets. */ |
| 332 |
if (pfil_run_hooks(V_inet6_pfil_head, &m, nh->nh_ifp, PFIL_OUT | |
| 333 |
PFIL_FWD, NULL) != PFIL_PASS) |
| 334 |
goto freecopy; |
| 335 |
ip6 = mtod(m, struct ip6_hdr *); |
| 336 |
|
| 337 |
/* See if destination IP address was changed by packet filter. */ |
| 338 |
if (!IN6_ARE_ADDR_EQUAL(&odst, &ip6->ip6_dst)) { |
| 339 |
m->m_flags |= M_SKIP_FIREWALL; |
| 340 |
/* If destination is now ourself drop to ip6_input(). */ |
| 341 |
if (in6_localip(&ip6->ip6_dst)) |
| 342 |
m->m_flags |= M_FASTFWD_OURS; |
| 343 |
else { |
| 344 |
NH_FREE(nh); |
| 345 |
|
| 346 |
/* Update address and scopeid. Assume scope is embedded */ |
| 347 |
dst.sin6_scope_id = ntohs(in6_getscope(&ip6->ip6_dst)); |
| 348 |
dst.sin6_addr = ip6->ip6_dst; |
| 349 |
in6_clearscope(&dst.sin6_addr); |
| 350 |
goto again; /* Redo the routing table lookup. */ |
| 351 |
} |
| 352 |
} |
| 353 |
|
| 354 |
/* See if local, if yes, send it to netisr. */ |
| 355 |
if (m->m_flags & M_FASTFWD_OURS) { |
| 356 |
if (m->m_pkthdr.rcvif == NULL) |
| 357 |
m->m_pkthdr.rcvif = V_loif; |
| 358 |
if (m->m_pkthdr.csum_flags & CSUM_DELAY_DATA_IPV6) { |
| 359 |
m->m_pkthdr.csum_flags |= |
| 360 |
CSUM_DATA_VALID_IPV6 | CSUM_PSEUDO_HDR; |
| 361 |
m->m_pkthdr.csum_data = 0xffff; |
| 362 |
} |
| 363 |
#if defined(SCTP) || defined(SCTP_SUPPORT) |
| 364 |
if (m->m_pkthdr.csum_flags & CSUM_SCTP_IPV6) |
| 365 |
m->m_pkthdr.csum_flags |= CSUM_SCTP_VALID; |
| 366 |
#endif |
| 367 |
error = netisr_queue(NETISR_IPV6, m); |
| 368 |
goto out; |
| 369 |
} |
| 370 |
/* Or forward to some other address? */ |
| 371 |
if ((m->m_flags & M_IP6_NEXTHOP) && |
| 372 |
(fwd_tag = m_tag_find(m, PACKET_TAG_IPFORWARD, NULL)) != NULL) { |
| 373 |
struct sockaddr_in6 *gw6 = (struct sockaddr_in6 *)(fwd_tag + 1); |
| 374 |
|
| 375 |
/* Update address and scopeid. Assume scope is embedded */ |
| 376 |
dst.sin6_scope_id = ntohs(in6_getscope(&gw6->sin6_addr)); |
| 377 |
dst.sin6_addr = gw6->sin6_addr; |
| 378 |
in6_clearscope(&dst.sin6_addr); |
| 379 |
|
| 380 |
m->m_flags |= M_SKIP_FIREWALL; |
| 381 |
m->m_flags &= ~M_IP6_NEXTHOP; |
| 382 |
m_tag_delete(m, fwd_tag); |
| 383 |
NH_FREE(nh); |
| 384 |
goto again; |
| 385 |
} |
| 386 |
|
| 387 |
pass: |
| 388 |
/* See if the size was changed by the packet filter. */ |
| 389 |
/* TODO: change to nh->nh_mtu */ |
| 390 |
if (m->m_pkthdr.len > IN6_LINKMTU(nh->nh_ifp)) { |
| 391 |
in6_ifstat_inc(nh->nh_ifp, ifs6_in_toobig); |
| 392 |
if (mcopy) |
| 393 |
icmp6_error(mcopy, ICMP6_PACKET_TOO_BIG, 0, |
| 394 |
IN6_LINKMTU(nh->nh_ifp)); |
| 395 |
goto bad; |
| 396 |
} |
| 397 |
|
| 398 |
/* Currently LLE layer stores embedded IPv6 addresses */ |
| 399 |
if (IN6_IS_SCOPE_LINKLOCAL(&dst.sin6_addr)) { |
| 400 |
in6_set_unicast_scopeid(&dst.sin6_addr, dst.sin6_scope_id); |
| 401 |
dst.sin6_scope_id = 0; |
| 402 |
} |
| 403 |
error = nd6_output_ifp(nh->nh_ifp, origifp, m, &dst, NULL); |
| 404 |
if (error) { |
| 405 |
in6_ifstat_inc(nh->nh_ifp, ifs6_out_discard); |
| 406 |
IP6STAT_INC(ip6s_cantforward); |
| 407 |
} else { |
| 408 |
IP6STAT_INC(ip6s_forward); |
| 409 |
in6_ifstat_inc(nh->nh_ifp, ifs6_out_forward); |
| 410 |
if (type) |
| 411 |
IP6STAT_INC(ip6s_redirectsent); |
| 412 |
else { |
| 413 |
if (mcopy) |
| 414 |
goto freecopy; |
| 415 |
} |
| 416 |
} |
| 417 |
|
| 418 |
if (mcopy == NULL) |
| 419 |
goto out; |
| 420 |
switch (error) { |
| 421 |
case 0: |
| 422 |
if (type == ND_REDIRECT) { |
| 423 |
icmp6_redirect_output(mcopy, nh); |
| 424 |
goto out; |
| 425 |
} |
| 426 |
goto freecopy; |
| 427 |
|
| 428 |
case EMSGSIZE: |
| 429 |
/* xxx MTU is constant in PPP? */ |
| 430 |
goto freecopy; |
| 431 |
|
| 432 |
case ENOBUFS: |
| 433 |
/* Tell source to slow down like source quench in IP? */ |
| 434 |
goto freecopy; |
| 435 |
|
| 436 |
case ENETUNREACH: /* shouldn't happen, checked above */ |
| 437 |
case EHOSTUNREACH: |
| 438 |
case ENETDOWN: |
| 439 |
case EHOSTDOWN: |
| 440 |
default: |
| 441 |
type = ICMP6_DST_UNREACH; |
| 442 |
code = ICMP6_DST_UNREACH_ADDR; |
| 443 |
break; |
| 444 |
} |
| 445 |
icmp6_error(mcopy, type, code, 0); |
| 446 |
goto out; |
| 447 |
|
| 448 |
freecopy: |
| 449 |
m_freem(mcopy); |
| 450 |
goto out; |
| 451 |
bad: |
| 452 |
m_freem(m); |
| 453 |
out: |
| 454 |
if (nh != NULL) |
| 455 |
NH_FREE(nh); |
| 456 |
} |