The Design and Implementation of the FreeBSD Operating System, Second Edition
Now available: The Design and Implementation of the FreeBSD Operating System (Second Edition)


[ source navigation ] [ diff markup ] [ identifier search ] [ freetext search ] [ file search ] [ list types ] [ track identifier ]

FreeBSD/Linux Kernel Cross Reference
sys/netinet/ip_fastfwd.c

Version: -  FREEBSD  -  FREEBSD-13-STABLE  -  FREEBSD-13-0  -  FREEBSD-12-STABLE  -  FREEBSD-12-0  -  FREEBSD-11-STABLE  -  FREEBSD-11-0  -  FREEBSD-10-STABLE  -  FREEBSD-10-0  -  FREEBSD-9-STABLE  -  FREEBSD-9-0  -  FREEBSD-8-STABLE  -  FREEBSD-8-0  -  FREEBSD-7-STABLE  -  FREEBSD-7-0  -  FREEBSD-6-STABLE  -  FREEBSD-6-0  -  FREEBSD-5-STABLE  -  FREEBSD-5-0  -  FREEBSD-4-STABLE  -  FREEBSD-3-STABLE  -  FREEBSD22  -  l41  -  OPENBSD  -  linux-2.6  -  MK84  -  PLAN9  -  xnu-8792 
SearchContext: -  none  -  3  -  10 

    1 /*
    2  * Copyright (c) 2003 Andre Oppermann, Internet Business Solutions AG
    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. The name of the author may not be used to endorse or promote
   14  *    products derived from this software without specific prior written
   15  *    permission.
   16  *
   17  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR 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 AUTHOR 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  * $FreeBSD: releng/5.2/sys/netinet/ip_fastfwd.c 122921 2003-11-20 19:47:31Z andre $
   30  */
   31 
   32 /*
   33  * ip_fastforward gets its speed from processing the forwarded packet to
   34  * completion (if_output on the other side) without any queues or netisr's.
   35  * The receiving interface DMAs the packet into memory, the upper half of
   36  * driver calls ip_fastforward, we do our routing table lookup and directly
   37  * send it off to the outgoing interface which DMAs the packet to the
   38  * network card. The only part of the packet we touch with the CPU is the
   39  * IP header (unless there are complex firewall rules touching other parts
   40  * of the packet, but that is up to you). We are essentially limited by bus
   41  * bandwidth and how fast the network card/driver can set up receives and
   42  * transmits.
   43  *
   44  * We handle basic errors, ip header errors, checksum errors,
   45  * destination unreachable, fragmentation and fragmentation needed and
   46  * report them via icmp to the sender.
   47  *
   48  * Else if something is not pure IPv4 unicast forwarding we fall back to
   49  * the normal ip_input processing path. We should only be called from
   50  * interfaces connected to the outside world.
   51  *
   52  * Firewalling is fully supported including divert, ipfw fwd and ipfilter
   53  * ipnat and address rewrite.
   54  *
   55  * IPSEC is not supported if this host is a tunnel broker. IPSEC is
   56  * supported for connections to/from local host.
   57  *
   58  * We try to do the least expensive (in CPU ops) checks and operations
   59  * first to catch junk with as little overhead as possible.
   60  * 
   61  * We take full advantage of hardware support for ip checksum and
   62  * fragmentation offloading.
   63  *
   64  * We don't do ICMP redirect in the fast forwarding path. I have had my own
   65  * cases where two core routers with Zebra routing suite would send millions
   66  * ICMP redirects to connected hosts if the router to dest was not the default
   67  * gateway. In one case it was filling the routing table of a host with close
   68  * 300'000 cloned redirect entries until it ran out of kernel memory. However
   69  * the networking code proved very robust and it didn't crash or went ill
   70  * otherwise.
   71  */
   72 
   73 /*
   74  * Many thanks to Matt Thomas of NetBSD for basic structure of ip_flow.c which
   75  * is being followed here.
   76  */
   77 
   78 #include "opt_ipfw.h"
   79 #include "opt_ipdn.h"
   80 #include "opt_ipdivert.h"
   81 #include "opt_ipfilter.h"
   82 #include "opt_ipstealth.h"
   83 #include "opt_mac.h"
   84 #include "opt_pfil_hooks.h"
   85 
   86 #include <sys/param.h>
   87 #include <sys/systm.h>
   88 #include <sys/kernel.h>
   89 #include <sys/mac.h>
   90 #include <sys/malloc.h>
   91 #include <sys/mbuf.h>
   92 #include <sys/protosw.h>
   93 #include <sys/socket.h>
   94 #include <sys/sysctl.h>
   95 
   96 #include <net/pfil.h>
   97 #include <net/if.h>
   98 #include <net/if_types.h>
   99 #include <net/if_var.h>
  100 #include <net/if_dl.h>
  101 #include <net/route.h>
  102 
  103 #include <netinet/in.h>
  104 #include <netinet/in_systm.h>
  105 #include <netinet/in_var.h>
  106 #include <netinet/ip.h>
  107 #include <netinet/ip_var.h>
  108 #include <netinet/ip_icmp.h>
  109 
  110 #include <machine/in_cksum.h>
  111 
  112 #include <netinet/ip_fw.h>
  113 #include <netinet/ip_dummynet.h>
  114 
  115 static int ipfastforward_active = 0;
  116 SYSCTL_INT(_net_inet_ip, OID_AUTO, fastforwarding, CTLFLAG_RW,
  117     &ipfastforward_active, 0, "Enable fast IP forwarding");
  118 
  119 /*
  120  * Try to forward a packet based on the destination address.
  121  * This is a fast path optimized for the plain forwarding case.
  122  * If the packet is handled (and consumed) here then we return 1;
  123  * otherwise 0 is returned and the packet should be delivered
  124  * to ip_input for full processing.
  125  */
  126 int
  127 ip_fastforward(struct mbuf *m)
  128 {
  129         struct ip *ip;
  130         struct mbuf *m0 = NULL;
  131 #ifdef IPDIVERT
  132         struct ip *tip;
  133         struct mbuf *teem = NULL;
  134 #endif
  135         struct mbuf *tag = NULL;
  136         struct route ro;
  137         struct sockaddr_in *dst = NULL;
  138         struct in_ifaddr *ia = NULL;
  139         struct ifaddr *ifa = NULL;
  140         struct ifnet *ifp = NULL;
  141         struct ip_fw_args args;
  142         in_addr_t odest, dest;
  143         u_short sum;
  144         int error = 0;
  145         int hlen, ipfw, mtu;
  146 
  147         /*
  148          * Are we active and forwarding packets?
  149          */
  150         if (!ipfastforward_active || !ipforwarding)
  151                 return 0;
  152 
  153         /*
  154          * If there is any MT_TAG we fall back to ip_input because we can't
  155          * handle TAGs here. Should never happen as we get directly called
  156          * from the if_output routines.
  157          */
  158         if (m->m_type == MT_TAG) {
  159                 KASSERT(0, ("%s: packet with MT_TAG not expected", __func__));
  160                 return 0;
  161         }
  162 
  163         M_ASSERTVALID(m);
  164         M_ASSERTPKTHDR(m);
  165 
  166         /*
  167          * Step 1: check for packet drop conditions (and sanity checks)
  168          */
  169 
  170         /*
  171          * Is entire packet big enough?
  172          */
  173         if (m->m_pkthdr.len < sizeof(struct ip)) {
  174                 ipstat.ips_tooshort++;
  175                 goto drop;
  176         }
  177 
  178         /*
  179          * Is first mbuf large enough for ip header and is header present?
  180          */
  181         if (m->m_len < sizeof (struct ip) &&
  182            (m = m_pullup(m, sizeof (struct ip))) == 0) {
  183                 ipstat.ips_toosmall++;
  184                 goto drop;
  185         }
  186 
  187         ip = mtod(m, struct ip *);
  188 
  189         /*
  190          * Is it IPv4?
  191          */
  192         if (ip->ip_v != IPVERSION) {
  193                 ipstat.ips_badvers++;
  194                 goto drop;
  195         }
  196 
  197         /*
  198          * Is IP header length correct and is it in first mbuf?
  199          */
  200         hlen = ip->ip_hl << 2;
  201         if (hlen < sizeof(struct ip)) { /* minimum header length */
  202                 ipstat.ips_badlen++;
  203                 goto drop;
  204         }
  205         if (hlen > m->m_len) {
  206                 if ((m = m_pullup(m, hlen)) == 0) {
  207                         ipstat.ips_badhlen++;
  208                         goto drop;
  209                 }
  210                 ip = mtod(m, struct ip *);
  211         }
  212 
  213         /*
  214          * Checksum correct?
  215          */
  216         if (m->m_pkthdr.csum_flags & CSUM_IP_CHECKED)
  217                 sum = !(m->m_pkthdr.csum_flags & CSUM_IP_VALID);
  218         else {
  219                 if (hlen == sizeof(struct ip))
  220                         sum = in_cksum_hdr(ip);
  221                 else
  222                         sum = in_cksum(m, hlen);
  223         }
  224         if (sum) {
  225                 ipstat.ips_badsum++;
  226                 goto drop;
  227         }
  228         m->m_pkthdr.csum_flags |= (CSUM_IP_CHECKED | CSUM_IP_VALID);
  229 
  230         /*
  231          * Convert to host representation
  232          */
  233         ip->ip_len = ntohs(ip->ip_len);
  234         ip->ip_off = ntohs(ip->ip_off);
  235 
  236         /*
  237          * Is IP length longer than packet we have got?
  238          */
  239         if (m->m_pkthdr.len < ip->ip_len) {
  240                 ipstat.ips_tooshort++;
  241                 goto drop;
  242         }
  243 
  244         /*
  245          * Is packet longer than IP header tells us? If yes, truncate packet.
  246          */
  247         if (m->m_pkthdr.len > ip->ip_len) {
  248                 if (m->m_len == m->m_pkthdr.len) {
  249                         m->m_len = ip->ip_len;
  250                         m->m_pkthdr.len = ip->ip_len;
  251                 } else
  252                         m_adj(m, ip->ip_len - m->m_pkthdr.len);
  253         }
  254 
  255         /*
  256          * Is packet from or to 127/8?
  257          */
  258         if ((ntohl(ip->ip_dst.s_addr) >> IN_CLASSA_NSHIFT) == IN_LOOPBACKNET ||
  259             (ntohl(ip->ip_src.s_addr) >> IN_CLASSA_NSHIFT) == IN_LOOPBACKNET) {
  260                 ipstat.ips_badaddr++;
  261                 goto drop;
  262         }
  263 
  264         /*
  265          * Step 2: fallback conditions to normal ip_input path processing
  266          */
  267 
  268         /*
  269          * Only IP packets without options
  270          */
  271         if (ip->ip_hl != (sizeof(struct ip) >> 2))
  272                 goto fallback;
  273 
  274         /*
  275          * Only unicast IP, not from loopback, no L2 or IP broadcast,
  276          * no multicast, no INADDR_ANY
  277          *
  278          * XXX: Probably some of these checks could be direct drop
  279          * conditions.  However it is not clear whether there are some
  280          * hacks or obscure behaviours which make it neccessary to
  281          * let ip_input handle it.  We play safe here and let ip_input
  282          * deal with it until it is proven that we can directly drop it.
  283          */
  284         if ((m->m_pkthdr.rcvif->if_flags & IFF_LOOPBACK) ||
  285             ntohl(ip->ip_src.s_addr) == (u_long)INADDR_BROADCAST ||
  286             ntohl(ip->ip_dst.s_addr) == (u_long)INADDR_BROADCAST ||
  287             IN_MULTICAST(ntohl(ip->ip_src.s_addr)) ||
  288             IN_MULTICAST(ntohl(ip->ip_dst.s_addr)) ||
  289             ip->ip_dst.s_addr == INADDR_ANY )
  290                 goto fallback;
  291 
  292         /*
  293          * Is it for a local address on this host?
  294          */
  295         LIST_FOREACH(ia, INADDR_HASH(ip->ip_dst.s_addr), ia_hash) {
  296                 if (IA_SIN(ia)->sin_addr.s_addr == ip->ip_dst.s_addr)
  297                         goto fallback;
  298         }
  299 
  300         /*
  301          * Or is it for a local IP broadcast address on this host?
  302          */
  303         if (m->m_pkthdr.rcvif->if_flags & IFF_BROADCAST) {
  304                 TAILQ_FOREACH(ifa, &m->m_pkthdr.rcvif->if_addrhead, ifa_link) {
  305                         if (ifa->ifa_addr->sa_family != AF_INET)
  306                                 continue;
  307                         ia = ifatoia(ifa);
  308                         if (ia->ia_netbroadcast.s_addr == ip->ip_dst.s_addr)
  309                                 goto fallback;
  310                         if (satosin(&ia->ia_broadaddr)->sin_addr.s_addr ==
  311                             ip->ip_dst.s_addr)
  312                                 goto fallback;
  313                         continue;
  314 fallback:
  315                         /* return packet back to netisr for slow processing */
  316                         ip->ip_len = htons(ip->ip_len);
  317                         ip->ip_off = htons(ip->ip_off);
  318                         return 0;
  319                 }
  320         }
  321         ipstat.ips_total++;
  322 
  323         /*
  324          * Step 3: incoming packet firewall processing
  325          */
  326 
  327         odest = dest = ip->ip_dst.s_addr;
  328 #ifdef PFIL_HOOKS
  329         /*
  330          * Run through list of ipfilter hooks for input packets
  331          */
  332         if (pfil_run_hooks(&inet_pfil_hook, &m, m->m_pkthdr.rcvif, PFIL_IN) ||
  333             m == NULL)
  334                 return 1;
  335 
  336         M_ASSERTVALID(m);
  337         M_ASSERTPKTHDR(m);
  338 
  339         ip = mtod(m, struct ip *);      /* m may have changed by pfil hook */
  340         dest = ip->ip_dst.s_addr;
  341 #endif
  342 
  343         /*
  344          * Run through ipfw for input packets
  345          */
  346         if (fw_enable && IPFW_LOADED) {
  347                 bzero(&args, sizeof(args));
  348                 args.m = m;
  349 
  350                 ipfw = ip_fw_chk_ptr(&args);
  351                 m = args.m;
  352 
  353                 M_ASSERTVALID(m);
  354                 M_ASSERTPKTHDR(m);
  355 
  356                 /*
  357                  * Packet denied, drop it
  358                  */
  359                 if ((ipfw & IP_FW_PORT_DENY_FLAG) || m == NULL)
  360                         goto drop;
  361                 /*
  362                  * Send packet to the appropriate pipe
  363                  */
  364                 if (DUMMYNET_LOADED && (ipfw & IP_FW_PORT_DYNT_FLAG) != 0) {
  365                         ip_dn_io_ptr(m, ipfw & 0xffff, DN_TO_IP_IN, &args);
  366                         return 1;
  367                 }
  368 #ifdef IPDIVERT
  369                 /*
  370                  * Divert packet
  371                  */
  372                 if (ipfw != 0 && (ipfw & IP_FW_PORT_DYNT_FLAG) == 0) {
  373                         /*
  374                          * See if this is a fragment
  375                          */
  376                         if (ip->ip_off & (IP_MF | IP_OFFMASK)) {
  377                                 MGETHDR(tag, M_DONTWAIT, MT_TAG);
  378                                 if (tag == NULL)
  379                                         goto drop;
  380                                 tag->m_flags = PACKET_TAG_DIVERT;
  381                                 tag->m_data = (caddr_t)(intptr_t)args.divert_rule;
  382                                 tag->m_next = m;
  383                                 /* XXX: really bloody hack, see ip_input */
  384                                 tag->m_nextpkt = (struct mbuf *)1;
  385                                 m = tag;
  386                                 tag = NULL;
  387 
  388                                 goto droptoours;
  389                         }
  390                         /*
  391                          * Tee packet
  392                          */
  393                         if ((ipfw & IP_FW_PORT_TEE_FLAG) != 0)
  394                                 teem = m_dup(m, M_DONTWAIT);
  395                         else
  396                                 teem = m;
  397                         if (teem == NULL)
  398                                 goto passin;
  399 
  400                         /*
  401                          * Delayed checksums are not compatible
  402                          */
  403                         if (teem->m_pkthdr.csum_flags & CSUM_DELAY_DATA) {
  404                                 in_delayed_cksum(teem);
  405                                 teem->m_pkthdr.csum_flags &= ~CSUM_DELAY_DATA;
  406                         }
  407                         /*
  408                          * Restore packet header fields to original values
  409                          */
  410                         tip = mtod(teem, struct ip *);
  411                         tip->ip_len = htons(tip->ip_len);
  412                         tip->ip_off = htons(tip->ip_off);
  413                         /*
  414                          * Deliver packet to divert input routine
  415                          */
  416                         divert_packet(teem, 0, ipfw & 0xffff, args.divert_rule);
  417                         /*
  418                          * If this was not tee, we are done
  419                          */
  420                         if ((ipfw & IP_FW_PORT_TEE_FLAG) == 0)
  421                                 return 1;
  422                         /* Continue if it was tee */
  423                         goto passin;
  424                 }
  425 #endif
  426                 if (ipfw == 0 && args.next_hop != NULL) {
  427                         dest = args.next_hop->sin_addr.s_addr;
  428                         goto passin;
  429                 }
  430                 /*
  431                  * Let through or not?
  432                  */
  433                 if (ipfw != 0)
  434                         goto drop;
  435         }
  436 passin:
  437         ip = mtod(m, struct ip *);      /* if m changed during fw processing */
  438 
  439         /*
  440          * Destination address changed?
  441          */
  442         if (odest != dest) {
  443                 /*
  444                  * Is it now for a local address on this host?
  445                  */
  446                 LIST_FOREACH(ia, INADDR_HASH(ip->ip_dst.s_addr), ia_hash) {
  447                         if (IA_SIN(ia)->sin_addr.s_addr == ip->ip_dst.s_addr)
  448                                 goto forwardlocal;
  449                 }
  450                 /*
  451                  * Go on with new destination address
  452                  */
  453         }
  454 
  455         /*
  456          * Step 4: decrement TTL and look up route
  457          */
  458 
  459         /*
  460          * Check TTL
  461          */
  462 #ifdef IPSTEALTH
  463         if (!ipstealth) {
  464 #endif
  465         if (ip->ip_ttl <= IPTTLDEC) {
  466                 icmp_error(m, ICMP_TIMXCEED, ICMP_TIMXCEED_INTRANS, NULL, NULL);
  467                 return 1;
  468         }
  469 
  470         /*
  471          * Decrement the TTL and incrementally change the checksum.
  472          * Don't bother doing this with hw checksum offloading.
  473          */
  474         ip->ip_ttl -= IPTTLDEC;
  475         if (ip->ip_sum >= (u_int16_t) ~htons(IPTTLDEC << 8))
  476                 ip->ip_sum -= ~htons(IPTTLDEC << 8);
  477         else
  478                 ip->ip_sum += htons(IPTTLDEC << 8);
  479 #ifdef IPSTEALTH
  480         }
  481 #endif
  482 
  483         /*
  484          * Find route to destination.
  485          */
  486         bzero(&ro, sizeof(ro));
  487         dst = (struct sockaddr_in *)&ro.ro_dst;
  488         dst->sin_family = AF_INET;
  489         dst->sin_len = sizeof(*dst);
  490         dst->sin_addr.s_addr = dest;
  491         rtalloc_ign(&ro, RTF_CLONING);
  492 
  493         /*
  494          * Route there and interface still up?
  495          */
  496         if (ro.ro_rt &&
  497             (ro.ro_rt->rt_flags & RTF_UP) &&
  498             (ro.ro_rt->rt_ifp->if_flags & IFF_UP)) {
  499                 ia = ifatoia(ro.ro_rt->rt_ifa);
  500                 ifp = ro.ro_rt->rt_ifp;
  501                 if (ro.ro_rt->rt_flags & RTF_GATEWAY)
  502                         dst = (struct sockaddr_in *)ro.ro_rt->rt_gateway;
  503         } else {
  504                 ipstat.ips_noroute++;
  505                 ipstat.ips_cantforward++;
  506                 icmp_error(m, ICMP_UNREACH, ICMP_UNREACH_HOST, NULL, NULL);
  507                 if (ro.ro_rt)
  508                         RTFREE(ro.ro_rt);
  509                 return 1;
  510         }
  511 
  512         /*
  513          * Step 5: outgoing firewall packet processing
  514          */
  515 
  516 #ifdef PFIL_HOOKS
  517         /*
  518          * Run through list of hooks for output packets.
  519          */
  520         if (pfil_run_hooks(&inet_pfil_hook, &m, ifp, PFIL_OUT) || m == NULL) {
  521                 RTFREE(ro.ro_rt);
  522                 return 1;
  523         }
  524 
  525         M_ASSERTVALID(m);
  526         M_ASSERTPKTHDR(m);
  527 
  528         ip = mtod(m, struct ip *);
  529         dest = ip->ip_dst.s_addr;
  530 #endif
  531         if (fw_enable && IPFW_LOADED && !args.next_hop) {
  532                 bzero(&args, sizeof(args));
  533                 args.m = m;
  534                 args.oif = ifp;
  535 
  536                 ipfw = ip_fw_chk_ptr(&args);
  537                 m = args.m;
  538 
  539                 M_ASSERTVALID(m);
  540                 M_ASSERTPKTHDR(m);
  541 
  542                 if ((ipfw & IP_FW_PORT_DENY_FLAG) || m == NULL) {
  543                         RTFREE(ro.ro_rt);
  544                         goto drop;
  545                 }
  546                 if (DUMMYNET_LOADED && (ipfw & IP_FW_PORT_DYNT_FLAG) != 0) {
  547                         /*
  548                          * XXX note: if the ifp or rt entry are deleted
  549                          * while a pkt is in dummynet, we are in trouble!
  550                          */
  551                         args.ro = &ro;          /* dummynet does not save it */
  552                         args.dst = dst;
  553 
  554                         ip_dn_io_ptr(m, ipfw & 0xffff, DN_TO_IP_OUT, &args);
  555                         RTFREE(ro.ro_rt);
  556                         return 1;
  557                 }
  558 #ifdef IPDIVERT
  559                 if (ipfw != 0 && (ipfw & IP_FW_PORT_DYNT_FLAG) == 0) {
  560                         /*
  561                          * See if this is a fragment
  562                          */
  563                         if (ip->ip_off & (IP_MF | IP_OFFMASK)) {
  564                                 MGETHDR(tag, M_DONTWAIT, MT_TAG);
  565                                 if (tag == NULL) {
  566                                         RTFREE(ro.ro_rt);
  567                                         goto drop;
  568                                 }
  569                                 tag->m_flags = PACKET_TAG_DIVERT;
  570                                 tag->m_data = (caddr_t)(intptr_t)args.divert_rule;
  571                                 tag->m_next = m;
  572                                 /* XXX: really bloody hack, see ip_input */
  573                                 tag->m_nextpkt = (struct mbuf *)1;
  574                                 m = tag;
  575                                 tag = NULL;
  576 
  577                                 goto droptoours;
  578                         }
  579                         /*
  580                          * Tee packet
  581                          */
  582                         if ((ipfw & IP_FW_PORT_TEE_FLAG) != 0)
  583                                 teem = m_dup(m, M_DONTWAIT);
  584                         else
  585                                 teem = m;
  586                         if (teem == NULL)
  587                                 goto passout;
  588 
  589                         /*
  590                          * Delayed checksums are not compatible with divert
  591                          */
  592                         if (teem->m_pkthdr.csum_flags & CSUM_DELAY_DATA) {
  593                                 in_delayed_cksum(teem);
  594                                 teem->m_pkthdr.csum_flags &= ~CSUM_DELAY_DATA;
  595                         }
  596                         /*
  597                          * Restore packet header fields to original values
  598                          */
  599                         tip = mtod(teem, struct ip *);
  600                         tip->ip_len = htons(tip->ip_len);
  601                         tip->ip_off = htons(tip->ip_off);
  602                         /*
  603                          * Deliver packet to divert input routine
  604                          */
  605                         divert_packet(teem, 0, ipfw & 0xffff, args.divert_rule);
  606                         /*
  607                          * If this was not tee, we are done
  608                          */
  609                         if ((ipfw & IP_FW_PORT_TEE_FLAG) == 0) {
  610                                 RTFREE(ro.ro_rt);
  611                                 return 1;
  612                         }
  613                         /* Continue if it was tee */
  614                         goto passout;
  615                 }
  616 #endif
  617                 if (ipfw == 0 && args.next_hop != NULL) {
  618                         dest = args.next_hop->sin_addr.s_addr;
  619                         goto passout;
  620                 }
  621                 /*
  622                  * Let through or not?
  623                  */
  624                 if (ipfw != 0)
  625                         goto drop;
  626         }
  627 passout:
  628         ip = mtod(m, struct ip *);
  629 
  630         /*
  631          * Destination address changed?
  632          */
  633         if (odest != dest) {
  634                 /*
  635                  * Is it now for a local address on this host?
  636                  */
  637                 LIST_FOREACH(ia, INADDR_HASH(ip->ip_dst.s_addr), ia_hash) {
  638                         if (IA_SIN(ia)->sin_addr.s_addr == ip->ip_dst.s_addr) {
  639 forwardlocal:
  640                                 if (args.next_hop) {
  641                                         /* XXX leak */
  642                                         MGETHDR(tag, M_DONTWAIT, MT_TAG);
  643                                         if (tag == NULL) {
  644                                                 if (ro.ro_rt)
  645                                                         RTFREE(ro.ro_rt);
  646                                                 goto drop;
  647                                         }
  648                                         tag->m_flags = PACKET_TAG_IPFORWARD;
  649                                         tag->m_data = (caddr_t)args.next_hop;
  650                                         tag->m_next = m;
  651                                         /* XXX: really bloody hack,
  652                                          * see ip_input */
  653                                         tag->m_nextpkt = (struct mbuf *)1;
  654                                         m = tag;
  655                                         tag = NULL;
  656                                 }
  657 #ifdef IPDIVERT
  658 droptoours:     /* Used for DIVERT */
  659 #endif
  660                                 MGETHDR(tag, M_DONTWAIT, MT_TAG);
  661                                 if (tag == NULL) {
  662                                         if (ro.ro_rt)
  663                                                 RTFREE(ro.ro_rt);
  664                                         goto drop;
  665                                 }
  666                                 tag->m_flags = PACKET_TAG_IPFASTFWD_OURS;
  667                                 tag->m_data = NULL;
  668                                 tag->m_next = m;
  669                                 /* XXX: really bloody hack, see ip_input */
  670                                 tag->m_nextpkt = (struct mbuf *)1;
  671                                 m = tag;
  672                                 tag = NULL;
  673 
  674                                 /* ip still points to the real packet */
  675                                 ip->ip_len = htons(ip->ip_len);
  676                                 ip->ip_off = htons(ip->ip_off);
  677 
  678                                 /*
  679                                  * Return packet for processing by ip_input
  680                                  */
  681                                 if (ro.ro_rt)
  682                                         RTFREE(ro.ro_rt);
  683                                 return 0;
  684                         }
  685                 }
  686                 /*
  687                  * Redo route lookup with new destination address
  688                  */
  689                 RTFREE(ro.ro_rt);
  690                 bzero(&ro, sizeof(ro));
  691                 dst = (struct sockaddr_in *)&ro.ro_dst;
  692                 dst->sin_family = AF_INET;
  693                 dst->sin_len = sizeof(*dst);
  694                 dst->sin_addr.s_addr = dest;
  695                 rtalloc_ign(&ro, RTF_CLONING);
  696 
  697                 /*
  698                  * Route there and interface still up?
  699                  */
  700                 if (ro.ro_rt &&
  701                     (ro.ro_rt->rt_flags & RTF_UP) &&
  702                     (ro.ro_rt->rt_ifp->if_flags & IFF_UP)) {
  703                         ia = ifatoia(ro.ro_rt->rt_ifa);
  704                         ifp = ro.ro_rt->rt_ifp;
  705                         if (ro.ro_rt->rt_flags & RTF_GATEWAY)
  706                                 dst = (struct sockaddr_in *)ro.ro_rt->rt_gateway;
  707                 } else {
  708                         ipstat.ips_noroute++;
  709                         ipstat.ips_cantforward++;
  710                         icmp_error(m, ICMP_UNREACH, ICMP_UNREACH_HOST,
  711                                 NULL, NULL);
  712                         if (ro.ro_rt)
  713                                 RTFREE(ro.ro_rt);
  714                         return 1;
  715                 }
  716         }
  717 
  718         /*
  719          * Step 6: send off the packet
  720          */
  721 
  722         /*
  723          * Check if packet fits MTU or if hardware will fragement for us
  724          */
  725         if (ro.ro_rt->rt_rmx.rmx_mtu)
  726                 mtu = min(ro.ro_rt->rt_rmx.rmx_mtu, ifp->if_mtu);
  727         else
  728                 mtu = ifp->if_mtu;
  729 
  730         if (ip->ip_len <= mtu ||
  731             (ifp->if_hwassist & CSUM_FRAGMENT && (ip->ip_off & IP_DF) == 0)) {
  732                 /*
  733                  * Restore packet header fields to original values
  734                  */
  735                 ip->ip_len = htons(ip->ip_len);
  736                 ip->ip_off = htons(ip->ip_off);
  737                 /*
  738                  * Send off the packet via outgoing interface
  739                  */
  740                 error = (*ifp->if_output)(ifp, m,
  741                                 (struct sockaddr *)dst, ro.ro_rt);
  742                 if (ia) {
  743                         ia->ia_ifa.if_opackets++;
  744                         ia->ia_ifa.if_obytes += m->m_pkthdr.len;
  745                 }
  746         } else {
  747                 /*
  748                  * Handle EMSGSIZE with icmp reply
  749                  * needfrag for TCP MTU discovery
  750                  */
  751                 if (ip->ip_off & IP_DF) {
  752                         icmp_error(m, ICMP_UNREACH, ICMP_UNREACH_NEEDFRAG,
  753                                 NULL, ifp);
  754                         ipstat.ips_cantfrag++;
  755                         RTFREE(ro.ro_rt);
  756                         return 1;
  757                 } else {
  758                         /*
  759                          * We have to fragement the packet
  760                          */
  761                         m->m_pkthdr.csum_flags |= CSUM_IP;
  762                         if (ip_fragment(ip, &m, mtu, ifp->if_hwassist,
  763                                         (~ifp->if_hwassist & CSUM_DELAY_IP))) {
  764                                 RTFREE(ro.ro_rt);
  765                                 goto drop;
  766                         }
  767                         KASSERT(m != NULL, ("null mbuf and no error"));
  768                         /*
  769                          * Send off the fragments via outgoing interface
  770                          */
  771                         error = 0;
  772                         do {
  773                                 m0 = m->m_nextpkt;
  774                                 m->m_nextpkt = NULL;
  775 
  776                                 error = (*ifp->if_output)(ifp, m,
  777                                         (struct sockaddr *)dst, ro.ro_rt);
  778                                 if (error)
  779                                         break;
  780                         } while ((m = m0) != NULL);
  781                         if (error) {
  782                                 /* Reclaim remaining fragments */
  783                                 for (; m; m = m0) {
  784                                         m0 = m->m_nextpkt;
  785                                         m->m_nextpkt = NULL;
  786                                         m_freem(m);
  787                                 }
  788                         } else
  789                                 ipstat.ips_fragmented++;
  790                 }
  791         }
  792 
  793         if (error != 0)
  794                 ipstat.ips_odropped++;
  795         else {
  796                 ro.ro_rt->rt_rmx.rmx_pksent++;
  797                 ipstat.ips_forward++;
  798                 ipstat.ips_fastforward++;
  799         }
  800         RTFREE(ro.ro_rt);
  801         return 1;
  802 drop:
  803         if (m)
  804                 m_freem(m);
  805         return 1;
  806 }

Cache object: 9c081cc289454e984bf71f69b3a52541


[ source navigation ] [ diff markup ] [ identifier search ] [ freetext search ] [ file search ] [ list types ] [ track identifier ]


This page is part of the FreeBSD/Linux Linux Kernel Cross-Reference, and was automatically generated using a modified version of the LXR engine.