/*-1* SPDX-License-Identifier: BSD-3-Clause2*3* Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project.4* All rights reserved.5*6* Redistribution and use in source and binary forms, with or without7* modification, are permitted provided that the following conditions8* are met:9* 1. Redistributions of source code must retain the above copyright10* notice, this list of conditions and the following disclaimer.11* 2. Redistributions in binary form must reproduce the above copyright12* notice, this list of conditions and the following disclaimer in the13* documentation and/or other materials provided with the distribution.14* 3. Neither the name of the project nor the names of its contributors15* may be used to endorse or promote products derived from this software16* without specific prior written permission.17*18* THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND19* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE20* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE21* ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE22* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL23* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS24* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)25* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT26* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY27* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF28* SUCH DAMAGE.29*30* $KAME: ip6_forward.c,v 1.69 2001/05/17 03:48:30 itojun Exp $31*/3233#include <sys/cdefs.h>34#include "opt_inet.h"35#include "opt_inet6.h"36#include "opt_ipsec.h"37#include "opt_ipstealth.h"38#include "opt_sctp.h"3940#include <sys/param.h>41#include <sys/systm.h>42#include <sys/malloc.h>43#include <sys/mbuf.h>44#include <sys/domain.h>45#include <sys/protosw.h>46#include <sys/socket.h>47#include <sys/errno.h>48#include <sys/time.h>49#include <sys/kernel.h>50#include <sys/syslog.h>5152#include <net/if.h>53#include <net/if_var.h>54#include <net/if_private.h>55#include <net/netisr.h>56#include <net/route.h>57#include <net/route/nhop.h>58#include <net/pfil.h>5960#include <netinet/in.h>61#include <netinet/in_var.h>62#include <netinet/in_systm.h>63#include <netinet/ip.h>64#include <netinet/ip_var.h>65#include <netinet6/in6_var.h>66#include <netinet/ip6.h>67#include <netinet6/in6_fib.h>68#include <netinet6/ip6_var.h>69#include <netinet6/scope6_var.h>70#include <netinet/icmp6.h>71#include <netinet6/nd6.h>7273#include <netinet/in_pcb.h>7475#include <netipsec/ipsec_support.h>7677#if defined(SCTP) || defined(SCTP_SUPPORT)78#include <netinet/sctp_crc32.h>79#endif8081/*82* Forward a packet. If some error occurs return the sender83* an icmp packet. Note we can't always generate a meaningful84* icmp message because icmp doesn't have a large enough repertoire85* of codes and types.86*87* If not forwarding, just drop the packet. This could be confusing88* if ipforwarding was zero but some routing protocol was advancing89* us as a gateway to somewhere. However, we must let the routing90* protocol deal with that.91*92*/93void94ip6_forward(struct mbuf *m, int srcrt)95{96struct ip6_hdr *ip6 = mtod(m, struct ip6_hdr *);97struct sockaddr_in6 dst;98struct nhop_object *nh = NULL;99int error, type = 0, code = 0;100struct mbuf *mcopy = NULL;101struct ifnet *origifp; /* maybe unnecessary */102u_int32_t inzone, outzone;103struct in6_addr odst;104struct m_tag *fwd_tag;105char ip6bufs[INET6_ADDRSTRLEN], ip6bufd[INET6_ADDRSTRLEN];106107/*108* Do not forward packets to multicast destination (should be handled109* by ip6_mforward().110* Do not forward packets with unspecified source. It was discussed111* in July 2000, on the ipngwg mailing list.112*/113if ((m->m_flags & (M_BCAST|M_MCAST)) != 0 ||114IN6_IS_ADDR_MULTICAST(&ip6->ip6_dst) ||115IN6_IS_ADDR_UNSPECIFIED(&ip6->ip6_src) ||116IN6_IS_ADDR_UNSPECIFIED(&ip6->ip6_dst)) {117IP6STAT_INC(ip6s_cantforward);118/* XXX in6_ifstat_inc(rt->rt_ifp, ifs6_in_discard) */119if (V_ip6_log_cannot_forward && ip6_log_ratelimit()) {120log(LOG_DEBUG,121"cannot forward "122"from %s to %s nxt %d received on %s\n",123ip6_sprintf(ip6bufs, &ip6->ip6_src),124ip6_sprintf(ip6bufd, &ip6->ip6_dst),125ip6->ip6_nxt,126if_name(m->m_pkthdr.rcvif));127}128m_freem(m);129return;130}131132if (133#ifdef IPSTEALTH134V_ip6stealth == 0 &&135#endif136ip6->ip6_hlim <= IPV6_HLIMDEC) {137/* XXX in6_ifstat_inc(rt->rt_ifp, ifs6_in_discard) */138icmp6_error(m, ICMP6_TIME_EXCEEDED,139ICMP6_TIME_EXCEED_TRANSIT, 0);140return;141}142143/*144* Save at most ICMPV6_PLD_MAXLEN (= the min IPv6 MTU -145* size of IPv6 + ICMPv6 headers) bytes of the packet in case146* we need to generate an ICMP6 message to the src.147* Thanks to M_EXT, in most cases copy will not occur.148*149* It is important to save it before IPsec processing as IPsec150* processing may modify the mbuf.151*/152mcopy = m_copym(m, 0, imin(m->m_pkthdr.len, ICMPV6_PLD_MAXLEN),153M_NOWAIT);154#ifdef IPSTEALTH155if (V_ip6stealth == 0)156#endif157ip6->ip6_hlim -= IPV6_HLIMDEC;158159#if defined(IPSEC) || defined(IPSEC_SUPPORT)160if (IPSEC_ENABLED(ipv6)) {161if ((error = IPSEC_FORWARD(ipv6, m)) != 0) {162/* mbuf consumed by IPsec */163m_freem(mcopy);164if (error != EINPROGRESS)165IP6STAT_INC(ip6s_cantforward);166return;167}168/* No IPsec processing required */169}170#endif171/*172* ip6_forward() operates with IPv6 addresses with deembedded scope.173*174* There are 3 sources of IPv6 destination address:175*176* 1) ip6_input(), where ip6_dst contains deembedded address.177* In order to deal with forwarding of link-local packets,178* calculate the scope based on input interface (RFC 4007, clause 9).179* 2) packet filters changing ip6_dst directly. It would embed scope180* for LL addresses, so in6_localip() performs properly.181* 3) packet filters attaching PACKET_TAG_IPFORWARD would embed182* scope for the nexthop.183*/184bzero(&dst, sizeof(struct sockaddr_in6));185dst.sin6_family = AF_INET6;186dst.sin6_addr = ip6->ip6_dst;187dst.sin6_scope_id = in6_get_unicast_scopeid(&ip6->ip6_dst, m->m_pkthdr.rcvif);188again:189nh = fib6_lookup(M_GETFIB(m), &dst.sin6_addr, dst.sin6_scope_id,190NHR_REF, m->m_pkthdr.flowid);191if (nh == NULL) {192IP6STAT_INC(ip6s_noroute);193in6_ifstat_inc(m->m_pkthdr.rcvif, ifs6_in_noroute);194if (mcopy) {195icmp6_error(mcopy, ICMP6_DST_UNREACH,196ICMP6_DST_UNREACH_NOROUTE, 0);197}198goto bad;199}200201if (nh->nh_flags & (NHF_BLACKHOLE | NHF_REJECT)) {202IP6STAT_INC(ip6s_cantforward);203if (mcopy != NULL) {204if (nh->nh_flags & NHF_REJECT) {205icmp6_error(mcopy, ICMP6_DST_UNREACH,206ICMP6_DST_UNREACH_REJECT, 0);207} else208m_freem(mcopy);209}210goto bad;211}212213/*214* Source scope check: if a packet can't be delivered to its215* destination for the reason that the destination is beyond the scope216* of the source address, discard the packet and return an icmp6217* destination unreachable error with Code 2 (beyond scope of source218* address).219* [draft-ietf-ipngwg-icmp-v3-04.txt, Section 3.1]220*/221outzone = in6_get_unicast_scopeid(&ip6->ip6_src, nh->nh_ifp);222inzone = in6_get_unicast_scopeid(&ip6->ip6_src, m->m_pkthdr.rcvif);223if (inzone != outzone) {224IP6STAT_INC(ip6s_cantforward);225IP6STAT_INC(ip6s_badscope);226in6_ifstat_inc(nh->nh_ifp, ifs6_in_discard);227228if (V_ip6_log_cannot_forward && ip6_log_ratelimit()) {229log(LOG_DEBUG,230"cannot forward "231"src %s, dst %s, nxt %d, rcvif %s, outif %s\n",232ip6_sprintf(ip6bufs, &ip6->ip6_src),233ip6_sprintf(ip6bufd, &ip6->ip6_dst),234ip6->ip6_nxt,235if_name(m->m_pkthdr.rcvif), if_name(nh->nh_ifp));236}237if (mcopy)238icmp6_error(mcopy, ICMP6_DST_UNREACH,239ICMP6_DST_UNREACH_BEYONDSCOPE, 0);240goto bad;241}242243/*244* Destination scope check: if a packet is going to break the scope245* zone of packet's destination address, discard it. This case should246* usually be prevented by appropriately-configured routing table, but247* we need an explicit check because we may mistakenly forward the248* packet to a different zone by (e.g.) a default route.249*/250inzone = in6_get_unicast_scopeid(&ip6->ip6_dst, m->m_pkthdr.rcvif);251outzone = in6_get_unicast_scopeid(&ip6->ip6_dst, nh->nh_ifp);252253if (inzone != outzone) {254IP6STAT_INC(ip6s_cantforward);255IP6STAT_INC(ip6s_badscope);256goto bad;257}258259if (nh->nh_flags & NHF_GATEWAY) {260/* Store gateway address in deembedded form */261dst.sin6_addr = nh->gw6_sa.sin6_addr;262dst.sin6_scope_id = ntohs(in6_getscope(&dst.sin6_addr));263in6_clearscope(&dst.sin6_addr);264}265266/*267* If we are to forward the packet using the same interface268* as one we got the packet from, perhaps we should send a redirect269* to sender to shortcut a hop.270* Only send redirect if source is sending directly to us,271* and if packet was not source routed (or has any options).272* Also, don't send redirect if forwarding using a route273* modified by a redirect.274*/275if (V_ip6_sendredirects && nh->nh_ifp == m->m_pkthdr.rcvif && !srcrt &&276(nh->nh_flags & NHF_REDIRECT) == 0)277type = ND_REDIRECT;278279/*280* Fake scoped addresses. Note that even link-local source or281* destinaion can appear, if the originating node just sends the282* packet to us (without address resolution for the destination).283* Since both icmp6_error and icmp6_redirect_output fill the embedded284* link identifiers, we can do this stuff after making a copy for285* returning an error.286*/287if ((nh->nh_ifp->if_flags & IFF_LOOPBACK) != 0) {288/*289* See corresponding comments in ip6_output.290* XXX: but is it possible that ip6_forward() sends a packet291* to a loopback interface? I don't think so, and thus292* I bark here. ([email protected])293* XXX: it is common to route invalid packets to loopback.294* also, the codepath will be visited on use of ::1 in295* rthdr. (itojun)296*/297#if 1298if (0)299#else300if ((rt->rt_flags & (RTF_BLACKHOLE|RTF_REJECT)) == 0)301#endif302{303printf("ip6_forward: outgoing interface is loopback. "304"src %s, dst %s, nxt %d, rcvif %s, outif %s\n",305ip6_sprintf(ip6bufs, &ip6->ip6_src),306ip6_sprintf(ip6bufd, &ip6->ip6_dst),307ip6->ip6_nxt, if_name(m->m_pkthdr.rcvif),308if_name(nh->nh_ifp));309}310311/* we can just use rcvif in forwarding. */312origifp = m->m_pkthdr.rcvif;313}314else315origifp = nh->nh_ifp;316/*317* clear embedded scope identifiers if necessary.318* in6_clearscope will touch the addresses only when necessary.319*/320in6_clearscope(&ip6->ip6_src);321in6_clearscope(&ip6->ip6_dst);322323/* Jump over all PFIL processing if hooks are not active. */324if (!PFIL_HOOKED_OUT(V_inet6_pfil_head))325goto pass;326327odst = ip6->ip6_dst;328/* Run through list of hooks for forwarded packets. */329if (pfil_mbuf_fwd(V_inet6_pfil_head, &m, nh->nh_ifp,330NULL) != PFIL_PASS)331goto freecopy;332ip6 = mtod(m, struct ip6_hdr *);333334/* See if destination IP address was changed by packet filter. */335if (!IN6_ARE_ADDR_EQUAL(&odst, &ip6->ip6_dst)) {336m->m_flags |= M_SKIP_FIREWALL;337/* If destination is now ourself drop to ip6_input(). */338if (in6_localip(&ip6->ip6_dst))339m->m_flags |= M_FASTFWD_OURS;340else {341NH_FREE(nh);342343/* Update address and scopeid. Assume scope is embedded */344dst.sin6_scope_id = ntohs(in6_getscope(&ip6->ip6_dst));345dst.sin6_addr = ip6->ip6_dst;346in6_clearscope(&dst.sin6_addr);347goto again; /* Redo the routing table lookup. */348}349}350351/* See if local, if yes, send it to netisr. */352if (m->m_flags & M_FASTFWD_OURS) {353if (m->m_pkthdr.rcvif == NULL)354m->m_pkthdr.rcvif = V_loif;355if (m->m_pkthdr.csum_flags & CSUM_DELAY_DATA_IPV6) {356m->m_pkthdr.csum_flags |=357CSUM_DATA_VALID_IPV6 | CSUM_PSEUDO_HDR;358m->m_pkthdr.csum_data = 0xffff;359}360#if defined(SCTP) || defined(SCTP_SUPPORT)361if (m->m_pkthdr.csum_flags & CSUM_SCTP_IPV6)362m->m_pkthdr.csum_flags |= CSUM_SCTP_VALID;363#endif364error = netisr_queue(NETISR_IPV6, m);365goto out;366}367/* Or forward to some other address? */368if ((m->m_flags & M_IP6_NEXTHOP) &&369(fwd_tag = m_tag_find(m, PACKET_TAG_IPFORWARD, NULL)) != NULL) {370struct sockaddr_in6 *gw6 = (struct sockaddr_in6 *)(fwd_tag + 1);371372/* Update address and scopeid. Assume scope is embedded */373dst.sin6_scope_id = ntohs(in6_getscope(&gw6->sin6_addr));374dst.sin6_addr = gw6->sin6_addr;375in6_clearscope(&dst.sin6_addr);376377m->m_flags |= M_SKIP_FIREWALL;378m->m_flags &= ~M_IP6_NEXTHOP;379m_tag_delete(m, fwd_tag);380NH_FREE(nh);381goto again;382}383384pass:385/* See if the size was changed by the packet filter. */386/* TODO: change to nh->nh_mtu */387if (m->m_pkthdr.len > IN6_LINKMTU(nh->nh_ifp)) {388in6_ifstat_inc(nh->nh_ifp, ifs6_in_toobig);389if (mcopy)390icmp6_error(mcopy, ICMP6_PACKET_TOO_BIG, 0,391IN6_LINKMTU(nh->nh_ifp));392goto bad;393}394395/*396* If TCP/UDP header still needs a valid checksum and interface will not397* calculate it for us, do it here.398*/399if (__predict_false(m->m_pkthdr.csum_flags & CSUM_DELAY_DATA_IPV6 &400~nh->nh_ifp->if_hwassist)) {401int offset = ip6_lasthdr(m, 0, IPPROTO_IPV6, NULL);402403if (offset < sizeof(struct ip6_hdr) || offset > m->m_pkthdr.len)404goto bad;405in6_delayed_cksum(m, m->m_pkthdr.len - offset, offset);406m->m_pkthdr.csum_flags &= ~CSUM_DELAY_DATA_IPV6;407}408#if defined(SCTP) || defined(SCTP_SUPPORT)409if (__predict_false(m->m_pkthdr.csum_flags & CSUM_IP6_SCTP &410~nh->nh_ifp->if_hwassist)) {411int offset = ip6_lasthdr(m, 0, IPPROTO_IPV6, NULL);412413sctp_delayed_cksum(m, offset);414m->m_pkthdr.csum_flags &= ~CSUM_IP6_SCTP;415}416#endif417418/* Currently LLE layer stores embedded IPv6 addresses */419if (IN6_IS_SCOPE_LINKLOCAL(&dst.sin6_addr)) {420in6_set_unicast_scopeid(&dst.sin6_addr, dst.sin6_scope_id);421dst.sin6_scope_id = 0;422}423error = nd6_output_ifp(nh->nh_ifp, origifp, m, &dst, NULL);424if (error) {425in6_ifstat_inc(nh->nh_ifp, ifs6_out_discard);426IP6STAT_INC(ip6s_cantforward);427} else {428IP6STAT_INC(ip6s_forward);429in6_ifstat_inc(nh->nh_ifp, ifs6_out_forward);430if (type)431IP6STAT_INC(ip6s_redirectsent);432else {433if (mcopy)434goto freecopy;435}436}437438if (mcopy == NULL)439goto out;440switch (error) {441case 0:442if (type == ND_REDIRECT) {443icmp6_redirect_output(mcopy, nh);444goto out;445}446goto freecopy;447448case EMSGSIZE:449/* xxx MTU is constant in PPP? */450goto freecopy;451452case ENOBUFS:453/* Tell source to slow down like source quench in IP? */454goto freecopy;455456case ENETUNREACH: /* shouldn't happen, checked above */457case EHOSTUNREACH:458case ENETDOWN:459case EHOSTDOWN:460default:461type = ICMP6_DST_UNREACH;462code = ICMP6_DST_UNREACH_ADDR;463break;464}465icmp6_error(mcopy, type, code, 0);466goto out;467468freecopy:469m_freem(mcopy);470goto out;471bad:472m_freem(m);473out:474if (nh != NULL)475NH_FREE(nh);476}477478479