Annotation of sys/netinet6/ip6_forward.c, Revision 1.1
1.1 ! nbrk 1: /* $OpenBSD: ip6_forward.c,v 1.39 2007/06/01 00:52:38 henning Exp $ */
! 2: /* $KAME: ip6_forward.c,v 1.75 2001/06/29 12:42:13 jinmei Exp $ */
! 3:
! 4: /*
! 5: * Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project.
! 6: * All rights reserved.
! 7: *
! 8: * Redistribution and use in source and binary forms, with or without
! 9: * modification, are permitted provided that the following conditions
! 10: * are met:
! 11: * 1. Redistributions of source code must retain the above copyright
! 12: * notice, this list of conditions and the following disclaimer.
! 13: * 2. Redistributions in binary form must reproduce the above copyright
! 14: * notice, this list of conditions and the following disclaimer in the
! 15: * documentation and/or other materials provided with the distribution.
! 16: * 3. Neither the name of the project nor the names of its contributors
! 17: * may be used to endorse or promote products derived from this software
! 18: * without specific prior written permission.
! 19: *
! 20: * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND
! 21: * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
! 22: * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
! 23: * ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE
! 24: * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
! 25: * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
! 26: * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
! 27: * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
! 28: * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
! 29: * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
! 30: * SUCH DAMAGE.
! 31: */
! 32:
! 33: #include "pf.h"
! 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 <netinet/ip_var.h>
! 53: #include <netinet/ip6.h>
! 54: #include <netinet6/ip6_var.h>
! 55: #include <netinet/icmp6.h>
! 56: #include <netinet6/nd6.h>
! 57:
! 58: #if NPF > 0
! 59: #include <net/pfvar.h>
! 60: #endif
! 61:
! 62: #ifdef IPSEC
! 63: #include <netinet/ip_ipsp.h>
! 64: #include <netinet/ip_ah.h>
! 65: #include <netinet/ip_esp.h>
! 66: #include <netinet/udp.h>
! 67: #include <netinet/tcp.h>
! 68: #include <net/pfkeyv2.h>
! 69: #endif
! 70:
! 71: struct route_in6 ip6_forward_rt;
! 72: int ip6_forward_rtableid;
! 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:
! 87: void
! 88: ip6_forward(m, srcrt)
! 89: struct mbuf *m;
! 90: int srcrt;
! 91: {
! 92: struct ip6_hdr *ip6 = mtod(m, struct ip6_hdr *);
! 93: struct sockaddr_in6 *dst;
! 94: struct rtentry *rt;
! 95: int error = 0, type = 0, code = 0;
! 96: struct mbuf *mcopy = NULL;
! 97: struct ifnet *origifp; /* maybe unnecessary */
! 98: #ifdef IPSEC
! 99: u_int8_t sproto = 0;
! 100: struct m_tag *mtag;
! 101: union sockaddr_union sdst;
! 102: struct tdb_ident *tdbi;
! 103: u_int32_t sspi;
! 104: struct tdb *tdb;
! 105: int s;
! 106: #endif /* IPSEC */
! 107: int rtableid = 0;
! 108:
! 109: /*
! 110: * Do not forward packets to multicast destination (should be handled
! 111: * by ip6_mforward().
! 112: * Do not forward packets with unspecified source. It was discussed
! 113: * in July 2000, on ipngwg mailing list.
! 114: */
! 115: if ((m->m_flags & (M_BCAST|M_MCAST)) != 0 ||
! 116: IN6_IS_ADDR_MULTICAST(&ip6->ip6_dst) ||
! 117: IN6_IS_ADDR_UNSPECIFIED(&ip6->ip6_src)) {
! 118: ip6stat.ip6s_cantforward++;
! 119: /* XXX in6_ifstat_inc(rt->rt_ifp, ifs6_in_discard) */
! 120: if (ip6_log_time + ip6_log_interval < time_second) {
! 121: ip6_log_time = time_second;
! 122: log(LOG_DEBUG,
! 123: "cannot forward "
! 124: "from %s to %s nxt %d received on %s\n",
! 125: ip6_sprintf(&ip6->ip6_src),
! 126: ip6_sprintf(&ip6->ip6_dst),
! 127: ip6->ip6_nxt,
! 128: m->m_pkthdr.rcvif->if_xname);
! 129: }
! 130: m_freem(m);
! 131: return;
! 132: }
! 133:
! 134: if (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: ip6->ip6_hlim -= IPV6_HLIMDEC;
! 141:
! 142: #ifdef IPSEC
! 143: if (!ipsec_in_use)
! 144: goto done_spd;
! 145:
! 146: s = splnet();
! 147:
! 148: /*
! 149: * Check if there was an outgoing SA bound to the flow
! 150: * from a transport protocol.
! 151: */
! 152:
! 153: /* Do we have any pending SAs to apply ? */
! 154: mtag = m_tag_find(m, PACKET_TAG_IPSEC_PENDING_TDB, NULL);
! 155: if (mtag != NULL) {
! 156: #ifdef DIAGNOSTIC
! 157: if (mtag->m_tag_len != sizeof (struct tdb_ident))
! 158: panic("ip6_forward: tag of length %d (should be %d",
! 159: mtag->m_tag_len, sizeof (struct tdb_ident));
! 160: #endif
! 161: tdbi = (struct tdb_ident *)(mtag + 1);
! 162: tdb = gettdb(tdbi->spi, &tdbi->dst, tdbi->proto);
! 163: if (tdb == NULL)
! 164: error = -EINVAL;
! 165: m_tag_delete(m, mtag);
! 166: } else
! 167: tdb = ipsp_spd_lookup(m, AF_INET6, sizeof(struct ip6_hdr),
! 168: &error, IPSP_DIRECTION_OUT, NULL, NULL);
! 169:
! 170: if (tdb == NULL) {
! 171: splx(s);
! 172:
! 173: if (error == 0) {
! 174: /*
! 175: * No IPsec processing required, we'll just send the
! 176: * packet out.
! 177: */
! 178: sproto = 0;
! 179:
! 180: /* Fall through to routing/multicast handling */
! 181: } else {
! 182: /*
! 183: * -EINVAL is used to indicate that the packet should
! 184: * be silently dropped, typically because we've asked
! 185: * key management for an SA.
! 186: */
! 187: if (error == -EINVAL) /* Should silently drop packet */
! 188: error = 0;
! 189:
! 190: goto freecopy;
! 191: }
! 192: } else {
! 193: /* Loop detection */
! 194: for (mtag = m_tag_first(m); mtag != NULL;
! 195: mtag = m_tag_next(m, mtag)) {
! 196: if (mtag->m_tag_id != PACKET_TAG_IPSEC_OUT_DONE &&
! 197: mtag->m_tag_id !=
! 198: PACKET_TAG_IPSEC_OUT_CRYPTO_NEEDED)
! 199: continue;
! 200: tdbi = (struct tdb_ident *)(mtag + 1);
! 201: if (tdbi->spi == tdb->tdb_spi &&
! 202: tdbi->proto == tdb->tdb_sproto &&
! 203: !bcmp(&tdbi->dst, &tdb->tdb_dst,
! 204: sizeof(union sockaddr_union))) {
! 205: splx(s);
! 206: sproto = 0; /* mark as no-IPsec-needed */
! 207: goto done_spd;
! 208: }
! 209: }
! 210:
! 211: /* We need to do IPsec */
! 212: bcopy(&tdb->tdb_dst, &sdst, sizeof(sdst));
! 213: sspi = tdb->tdb_spi;
! 214: sproto = tdb->tdb_sproto;
! 215: splx(s);
! 216: }
! 217:
! 218: /* Fall through to the routing/multicast handling code */
! 219: done_spd:
! 220: #endif /* IPSEC */
! 221:
! 222: #if NPF > 0
! 223: rtableid = m->m_pkthdr.pf.rtableid;
! 224: #endif
! 225:
! 226: /*
! 227: * Save at most ICMPV6_PLD_MAXLEN (= the min IPv6 MTU -
! 228: * size of IPv6 + ICMPv6 headers) bytes of the packet in case
! 229: * we need to generate an ICMP6 message to the src.
! 230: * Thanks to M_EXT, in most cases copy will not occur.
! 231: *
! 232: * It is important to save it before IPsec processing as IPsec
! 233: * processing may modify the mbuf.
! 234: */
! 235: mcopy = m_copy(m, 0, imin(m->m_pkthdr.len, ICMPV6_PLD_MAXLEN));
! 236:
! 237: dst = &ip6_forward_rt.ro_dst;
! 238: if (!srcrt) {
! 239: /*
! 240: * ip6_forward_rt.ro_dst.sin6_addr is equal to ip6->ip6_dst
! 241: */
! 242: if (ip6_forward_rt.ro_rt == 0 ||
! 243: (ip6_forward_rt.ro_rt->rt_flags & RTF_UP) == 0 ||
! 244: ip6_forward_rtableid != rtableid) {
! 245: if (ip6_forward_rt.ro_rt) {
! 246: RTFREE(ip6_forward_rt.ro_rt);
! 247: ip6_forward_rt.ro_rt = 0;
! 248: }
! 249: /* this probably fails but give it a try again */
! 250: rtalloc_mpath((struct route *)&ip6_forward_rt,
! 251: &ip6->ip6_src.s6_addr32[0], rtableid);
! 252: ip6_forward_rtableid = rtableid;
! 253: }
! 254:
! 255: if (ip6_forward_rt.ro_rt == 0) {
! 256: ip6stat.ip6s_noroute++;
! 257: /* XXX in6_ifstat_inc(rt->rt_ifp, ifs6_in_noroute) */
! 258: if (mcopy) {
! 259: icmp6_error(mcopy, ICMP6_DST_UNREACH,
! 260: ICMP6_DST_UNREACH_NOROUTE, 0);
! 261: }
! 262: m_freem(m);
! 263: return;
! 264: }
! 265: } else if ((rt = ip6_forward_rt.ro_rt) == 0 ||
! 266: !IN6_ARE_ADDR_EQUAL(&ip6->ip6_dst, &dst->sin6_addr)) {
! 267: if (ip6_forward_rt.ro_rt) {
! 268: RTFREE(ip6_forward_rt.ro_rt);
! 269: ip6_forward_rt.ro_rt = 0;
! 270: }
! 271: bzero(dst, sizeof(*dst));
! 272: dst->sin6_len = sizeof(struct sockaddr_in6);
! 273: dst->sin6_family = AF_INET6;
! 274: dst->sin6_addr = ip6->ip6_dst;
! 275:
! 276: rtalloc_mpath((struct route *)&ip6_forward_rt,
! 277: &ip6->ip6_src.s6_addr32[0], 0);
! 278:
! 279: if (ip6_forward_rt.ro_rt == 0) {
! 280: ip6stat.ip6s_noroute++;
! 281: /* XXX in6_ifstat_inc(rt->rt_ifp, ifs6_in_noroute) */
! 282: if (mcopy) {
! 283: icmp6_error(mcopy, ICMP6_DST_UNREACH,
! 284: ICMP6_DST_UNREACH_NOROUTE, 0);
! 285: }
! 286: m_freem(m);
! 287: return;
! 288: }
! 289: }
! 290: rt = ip6_forward_rt.ro_rt;
! 291:
! 292: /*
! 293: * Scope check: if a packet can't be delivered to its destination
! 294: * for the reason that the destination is beyond the scope of the
! 295: * source address, discard the packet and return an icmp6 destination
! 296: * unreachable error with Code 2 (beyond scope of source address).
! 297: * [draft-ietf-ipngwg-icmp-v3-00.txt, Section 3.1]
! 298: */
! 299: if (in6_addr2scopeid(m->m_pkthdr.rcvif, &ip6->ip6_src) !=
! 300: in6_addr2scopeid(rt->rt_ifp, &ip6->ip6_src)) {
! 301: ip6stat.ip6s_cantforward++;
! 302: ip6stat.ip6s_badscope++;
! 303: in6_ifstat_inc(rt->rt_ifp, ifs6_in_discard);
! 304:
! 305: if (ip6_log_time + ip6_log_interval < time_second) {
! 306: ip6_log_time = time_second;
! 307: log(LOG_DEBUG,
! 308: "cannot forward "
! 309: "src %s, dst %s, nxt %d, rcvif %s, outif %s\n",
! 310: ip6_sprintf(&ip6->ip6_src),
! 311: ip6_sprintf(&ip6->ip6_dst),
! 312: ip6->ip6_nxt,
! 313: m->m_pkthdr.rcvif->if_xname, rt->rt_ifp->if_xname);
! 314: }
! 315: if (mcopy)
! 316: icmp6_error(mcopy, ICMP6_DST_UNREACH,
! 317: ICMP6_DST_UNREACH_BEYONDSCOPE, 0);
! 318: m_freem(m);
! 319: goto freert;
! 320: }
! 321:
! 322: #ifdef IPSEC
! 323: /*
! 324: * Check if the packet needs encapsulation.
! 325: * ipsp_process_packet will never come back to here.
! 326: * XXX ipsp_process_packet() calls ip6_output(), and there'll be no
! 327: * PMTU notification. is it okay?
! 328: */
! 329: if (sproto != 0) {
! 330: s = splnet();
! 331:
! 332: tdb = gettdb(sspi, &sdst, sproto);
! 333: if (tdb == NULL) {
! 334: splx(s);
! 335: error = EHOSTUNREACH;
! 336: m_freem(m);
! 337: goto senderr; /*XXX*/
! 338: }
! 339:
! 340: m->m_flags &= ~(M_BCAST | M_MCAST); /* just in case */
! 341:
! 342: /* Callee frees mbuf */
! 343: error = ipsp_process_packet(m, tdb, AF_INET6, 0);
! 344: splx(s);
! 345: m_freem(mcopy);
! 346: goto freert;
! 347: }
! 348: #endif /* IPSEC */
! 349:
! 350: if (m->m_pkthdr.len > IN6_LINKMTU(rt->rt_ifp)) {
! 351: in6_ifstat_inc(rt->rt_ifp, ifs6_in_toobig);
! 352: if (mcopy) {
! 353: u_long mtu;
! 354:
! 355: mtu = IN6_LINKMTU(rt->rt_ifp);
! 356:
! 357: icmp6_error(mcopy, ICMP6_PACKET_TOO_BIG, 0, mtu);
! 358: }
! 359: m_freem(m);
! 360: goto freert;
! 361: }
! 362:
! 363: if (rt->rt_flags & RTF_GATEWAY)
! 364: dst = (struct sockaddr_in6 *)rt->rt_gateway;
! 365:
! 366: /*
! 367: * If we are to forward the packet using the same interface
! 368: * as one we got the packet from, perhaps we should send a redirect
! 369: * to sender to shortcut a hop.
! 370: * Only send redirect if source is sending directly to us,
! 371: * and if packet was not source routed (or has any options).
! 372: * Also, don't send redirect if forwarding using a route
! 373: * modified by a redirect.
! 374: */
! 375: if (rt->rt_ifp == m->m_pkthdr.rcvif && !srcrt && ip6_sendredirects &&
! 376: (rt->rt_flags & (RTF_DYNAMIC|RTF_MODIFIED)) == 0) {
! 377: if ((rt->rt_ifp->if_flags & IFF_POINTOPOINT) &&
! 378: nd6_is_addr_neighbor((struct sockaddr_in6 *)&ip6_forward_rt.ro_dst, rt->rt_ifp)) {
! 379: /*
! 380: * If the incoming interface is equal to the outgoing
! 381: * one, the link attached to the interface is
! 382: * point-to-point, and the IPv6 destination is
! 383: * regarded as on-link on the link, then it will be
! 384: * highly probable that the destination address does
! 385: * not exist on the link and that the packet is going
! 386: * to loop. Thus, we immediately drop the packet and
! 387: * send an ICMPv6 error message.
! 388: * For other routing loops, we dare to let the packet
! 389: * go to the loop, so that a remote diagnosing host
! 390: * can detect the loop by traceroute.
! 391: * type/code is based on suggestion by Rich Draves.
! 392: * not sure if it is the best pick.
! 393: */
! 394: icmp6_error(mcopy, ICMP6_DST_UNREACH,
! 395: ICMP6_DST_UNREACH_ADDR, 0);
! 396: m_freem(m);
! 397: goto freert;
! 398: }
! 399: type = ND_REDIRECT;
! 400: }
! 401:
! 402: /*
! 403: * Fake scoped addresses. Note that even link-local source or
! 404: * destinaion can appear, if the originating node just sends the
! 405: * packet to us (without address resolution for the destination).
! 406: * Since both icmp6_error and icmp6_redirect_output fill the embedded
! 407: * link identifiers, we can do this stuff after making a copy for
! 408: * returning an error.
! 409: */
! 410: if ((rt->rt_ifp->if_flags & IFF_LOOPBACK) != 0) {
! 411: /*
! 412: * See corresponding comments in ip6_output.
! 413: * XXX: but is it possible that ip6_forward() sends a packet
! 414: * to a loopback interface? I don't think so, and thus
! 415: * I bark here. (jinmei@kame.net)
! 416: * XXX: it is common to route invalid packets to loopback.
! 417: * also, the codepath will be visited on use of ::1 in
! 418: * rthdr. (itojun)
! 419: */
! 420: #if 1
! 421: if (0)
! 422: #else
! 423: if ((rt->rt_flags & (RTF_BLACKHOLE|RTF_REJECT)) == 0)
! 424: #endif
! 425: {
! 426: printf("ip6_forward: outgoing interface is loopback. "
! 427: "src %s, dst %s, nxt %d, rcvif %s, outif %s\n",
! 428: ip6_sprintf(&ip6->ip6_src),
! 429: ip6_sprintf(&ip6->ip6_dst),
! 430: ip6->ip6_nxt, m->m_pkthdr.rcvif->if_xname,
! 431: rt->rt_ifp->if_xname);
! 432: }
! 433:
! 434: /* we can just use rcvif in forwarding. */
! 435: origifp = m->m_pkthdr.rcvif;
! 436: }
! 437: else
! 438: origifp = rt->rt_ifp;
! 439: if (IN6_IS_SCOPE_EMBED(&ip6->ip6_src))
! 440: ip6->ip6_src.s6_addr16[1] = 0;
! 441: if (IN6_IS_SCOPE_EMBED(&ip6->ip6_dst))
! 442: ip6->ip6_dst.s6_addr16[1] = 0;
! 443:
! 444: #if NPF > 0
! 445: if (pf_test6(PF_OUT, rt->rt_ifp, &m, NULL) != PF_PASS) {
! 446: m_freem(m);
! 447: goto senderr;
! 448: }
! 449: if (m == NULL)
! 450: goto senderr;
! 451:
! 452: ip6 = mtod(m, struct ip6_hdr *);
! 453: #endif
! 454:
! 455: error = nd6_output(rt->rt_ifp, origifp, m, dst, rt);
! 456: if (error) {
! 457: in6_ifstat_inc(rt->rt_ifp, ifs6_out_discard);
! 458: ip6stat.ip6s_cantforward++;
! 459: } else {
! 460: ip6stat.ip6s_forward++;
! 461: in6_ifstat_inc(rt->rt_ifp, ifs6_out_forward);
! 462: if (type)
! 463: ip6stat.ip6s_redirectsent++;
! 464: else {
! 465: if (mcopy)
! 466: goto freecopy;
! 467: }
! 468: }
! 469:
! 470: #if NPF > 0 || defined(IPSEC)
! 471: senderr:
! 472: #endif
! 473: if (mcopy == NULL)
! 474: goto freert;
! 475: switch (error) {
! 476: case 0:
! 477: if (type == ND_REDIRECT) {
! 478: icmp6_redirect_output(mcopy, rt);
! 479: goto freert;
! 480: }
! 481: goto freecopy;
! 482:
! 483: case EMSGSIZE:
! 484: /* xxx MTU is constant in PPP? */
! 485: goto freecopy;
! 486:
! 487: case ENOBUFS:
! 488: /* Tell source to slow down like source quench in IP? */
! 489: goto freecopy;
! 490:
! 491: case ENETUNREACH: /* shouldn't happen, checked above */
! 492: case EHOSTUNREACH:
! 493: case ENETDOWN:
! 494: case EHOSTDOWN:
! 495: default:
! 496: type = ICMP6_DST_UNREACH;
! 497: code = ICMP6_DST_UNREACH_ADDR;
! 498: break;
! 499: }
! 500: icmp6_error(mcopy, type, code, 0);
! 501: goto freert;
! 502:
! 503: freecopy:
! 504: m_freem(mcopy);
! 505: freert:
! 506: #ifndef SMALL_KERNEL
! 507: if (ip6_multipath && ip6_forward_rt.ro_rt &&
! 508: (ip6_forward_rt.ro_rt->rt_flags & RTF_MPATH)) {
! 509: RTFREE(ip6_forward_rt.ro_rt);
! 510: ip6_forward_rt.ro_rt = 0;
! 511: }
! 512: #endif
! 513: return;
! 514: }
CVSweb