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_flow.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) 1998 The NetBSD Foundation, Inc.
    3  * All rights reserved.
    4  *
    5  * This code is derived from software contributed to The NetBSD Foundation
    6  * by the 3am Software Foundry ("3am").  It was developed by Matt Thomas.
    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. All advertising materials mentioning features or use of this software
   17  *    must display the following acknowledgement:
   18  *      This product includes software developed by the NetBSD
   19  *      Foundation, Inc. and its contributors.
   20  * 4. Neither the name of The NetBSD Foundation nor the names of its
   21  *    contributors may be used to endorse or promote products derived
   22  *    from this software without specific prior written permission.
   23  *
   24  * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
   25  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
   26  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
   27  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
   28  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
   29  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
   30  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
   31  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
   32  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
   33  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
   34  * POSSIBILITY OF SUCH DAMAGE.
   35  *
   36  * $FreeBSD: releng/5.0/sys/netinet/ip_flow.c 86031 2001-11-04 17:35:31Z luigi $
   37  */
   38 
   39 #include <sys/param.h>
   40 #include <sys/systm.h>
   41 #include <sys/malloc.h>
   42 #include <sys/mbuf.h>
   43 #include <sys/protosw.h>
   44 #include <sys/socket.h>
   45 #include <sys/kernel.h>
   46 
   47 #include <sys/sysctl.h>
   48 
   49 #include <net/if.h>
   50 #include <net/route.h>
   51 
   52 #include <netinet/in.h>
   53 #include <netinet/in_systm.h>
   54 #include <netinet/ip.h>
   55 #include <netinet/in_var.h>
   56 #include <netinet/ip_var.h>
   57 #include <netinet/ip_flow.h>
   58 
   59 #define IPFLOW_TIMER            (5 * PR_SLOWHZ)
   60 #define IPFLOW_HASHBITS         6       /* should not be a multiple of 8 */
   61 #define IPFLOW_HASHSIZE         (1 << IPFLOW_HASHBITS)
   62 static LIST_HEAD(ipflowhead, ipflow) ipflows[IPFLOW_HASHSIZE];
   63 static int ipflow_inuse;
   64 #define IPFLOW_MAX              256
   65 
   66 static int ipflow_active = 0;
   67 SYSCTL_INT(_net_inet_ip, IPCTL_FASTFORWARDING, fastforwarding, CTLFLAG_RW,
   68     &ipflow_active, 0, "Enable flow-based IP forwarding");
   69 
   70 static MALLOC_DEFINE(M_IPFLOW, "ip_flow", "IP flow");
   71 
   72 static unsigned
   73 ipflow_hash(
   74         struct in_addr dst,
   75         struct in_addr src,
   76         unsigned tos)
   77 {
   78         unsigned hash = tos;
   79         int idx;
   80         for (idx = 0; idx < 32; idx += IPFLOW_HASHBITS)
   81                 hash += (dst.s_addr >> (32 - idx)) + (src.s_addr >> idx);
   82         return hash & (IPFLOW_HASHSIZE-1);
   83 }
   84 
   85 static struct ipflow *
   86 ipflow_lookup(
   87         const struct ip *ip)
   88 {
   89         unsigned hash;
   90         struct ipflow *ipf;
   91 
   92         hash = ipflow_hash(ip->ip_dst, ip->ip_src, ip->ip_tos);
   93 
   94         ipf = LIST_FIRST(&ipflows[hash]);
   95         while (ipf != NULL) {
   96                 if (ip->ip_dst.s_addr == ipf->ipf_dst.s_addr
   97                     && ip->ip_src.s_addr == ipf->ipf_src.s_addr
   98                     && ip->ip_tos == ipf->ipf_tos)
   99                         break;
  100                 ipf = LIST_NEXT(ipf, ipf_next);
  101         }
  102         return ipf;
  103 }
  104 
  105 int
  106 ipflow_fastforward(
  107         struct mbuf *m)
  108 {
  109         struct ip *ip;
  110         struct ipflow *ipf;
  111         struct rtentry *rt;
  112         struct sockaddr *dst;
  113         int error;
  114 
  115         /*
  116          * Are we forwarding packets?  Big enough for an IP packet?
  117          */
  118         if (!ipforwarding || !ipflow_active || m->m_len < sizeof(struct ip))
  119                 return 0;
  120         /*
  121          * IP header with no option and valid version and length
  122          */
  123         ip = mtod(m, struct ip *);
  124         if (ip->ip_v != IPVERSION || ip->ip_hl != (sizeof(struct ip) >> 2)
  125             || ntohs(ip->ip_len) > m->m_pkthdr.len)
  126                 return 0;
  127         /*
  128          * Find a flow.
  129          */
  130         if ((ipf = ipflow_lookup(ip)) == NULL)
  131                 return 0;
  132 
  133         /*
  134          * Route and interface still up?
  135          */
  136         rt = ipf->ipf_ro.ro_rt;
  137         if ((rt->rt_flags & RTF_UP) == 0 || (rt->rt_ifp->if_flags & IFF_UP) == 0)
  138                 return 0;
  139 
  140         /*
  141          * Packet size OK?  TTL?
  142          */
  143         if (m->m_pkthdr.len > rt->rt_ifp->if_mtu || ip->ip_ttl <= IPTTLDEC)
  144                 return 0;
  145 
  146         /*
  147          * Everything checks out and so we can forward this packet.
  148          * Modify the TTL and incrementally change the checksum.
  149          */
  150         ip->ip_ttl -= IPTTLDEC;
  151         if (ip->ip_sum >= htons(0xffff - (IPTTLDEC << 8))) {
  152                 ip->ip_sum += htons(IPTTLDEC << 8) + 1;
  153         } else {
  154                 ip->ip_sum += htons(IPTTLDEC << 8);
  155         }
  156 
  157         /*
  158          * Send the packet on its way.  All we can get back is ENOBUFS
  159          */
  160         ipf->ipf_uses++;
  161         ipf->ipf_timer = IPFLOW_TIMER;
  162 
  163         if (rt->rt_flags & RTF_GATEWAY)
  164                 dst = rt->rt_gateway;
  165         else
  166                 dst = &ipf->ipf_ro.ro_dst;
  167         if ((error = (*rt->rt_ifp->if_output)(rt->rt_ifp, m, dst, rt)) != 0) {
  168                 if (error == ENOBUFS)
  169                         ipf->ipf_dropped++;
  170                 else
  171                         ipf->ipf_errors++;
  172         }
  173         return 1;
  174 }
  175 
  176 static void
  177 ipflow_addstats(
  178         struct ipflow *ipf)
  179 {
  180         ipf->ipf_ro.ro_rt->rt_use += ipf->ipf_uses;
  181         ipstat.ips_cantforward += ipf->ipf_errors + ipf->ipf_dropped;
  182         ipstat.ips_forward += ipf->ipf_uses;
  183         ipstat.ips_fastforward += ipf->ipf_uses;
  184 }
  185 
  186 static void
  187 ipflow_free(
  188         struct ipflow *ipf)
  189 {
  190         int s;
  191         /*
  192          * Remove the flow from the hash table (at elevated IPL).
  193          * Once it's off the list, we can deal with it at normal
  194          * network IPL.
  195          */
  196         s = splimp();
  197         LIST_REMOVE(ipf, ipf_next);
  198         splx(s);
  199         ipflow_addstats(ipf);
  200         RTFREE(ipf->ipf_ro.ro_rt);
  201         ipflow_inuse--;
  202         free(ipf, M_IPFLOW);
  203 }
  204 
  205 static struct ipflow *
  206 ipflow_reap(
  207         void)
  208 {
  209         struct ipflow *ipf, *maybe_ipf = NULL;
  210         int idx;
  211         int s;
  212 
  213         for (idx = 0; idx < IPFLOW_HASHSIZE; idx++) {
  214                 ipf = LIST_FIRST(&ipflows[idx]);
  215                 while (ipf != NULL) {
  216                         /*
  217                          * If this no longer points to a valid route
  218                          * reclaim it.
  219                          */
  220                         if ((ipf->ipf_ro.ro_rt->rt_flags & RTF_UP) == 0)
  221                                 goto done;
  222                         /*
  223                          * choose the one that's been least recently used
  224                          * or has had the least uses in the last 1.5 
  225                          * intervals.
  226                          */
  227                         if (maybe_ipf == NULL
  228                             || ipf->ipf_timer < maybe_ipf->ipf_timer
  229                             || (ipf->ipf_timer == maybe_ipf->ipf_timer
  230                                 && ipf->ipf_last_uses + ipf->ipf_uses <
  231                                       maybe_ipf->ipf_last_uses +
  232                                         maybe_ipf->ipf_uses))
  233                                 maybe_ipf = ipf;
  234                         ipf = LIST_NEXT(ipf, ipf_next);
  235                 }
  236         }
  237         ipf = maybe_ipf;
  238     done:
  239         /*
  240          * Remove the entry from the flow table.
  241          */
  242         s = splimp();
  243         LIST_REMOVE(ipf, ipf_next);
  244         splx(s);
  245         ipflow_addstats(ipf);
  246         RTFREE(ipf->ipf_ro.ro_rt);
  247         return ipf;
  248 }
  249 
  250 void
  251 ipflow_slowtimo(
  252         void)
  253 {
  254         struct ipflow *ipf;
  255         int idx;
  256 
  257         for (idx = 0; idx < IPFLOW_HASHSIZE; idx++) {
  258                 ipf = LIST_FIRST(&ipflows[idx]);
  259                 while (ipf != NULL) {
  260                         struct ipflow *next_ipf = LIST_NEXT(ipf, ipf_next);
  261                         if (--ipf->ipf_timer == 0) {
  262                                 ipflow_free(ipf);
  263                         } else {
  264                                 ipf->ipf_last_uses = ipf->ipf_uses;
  265                                 ipf->ipf_ro.ro_rt->rt_use += ipf->ipf_uses;
  266                                 ipstat.ips_forward += ipf->ipf_uses;
  267                                 ipstat.ips_fastforward += ipf->ipf_uses;
  268                                 ipf->ipf_uses = 0;
  269                         }
  270                         ipf = next_ipf;
  271                 }
  272         }
  273 }
  274 
  275 void
  276 ipflow_create(
  277         const struct route *ro,
  278         struct mbuf *m)
  279 {
  280         const struct ip *const ip = mtod(m, struct ip *);
  281         struct ipflow *ipf;
  282         unsigned hash;
  283         int s;
  284 
  285         /*
  286          * Don't create cache entries for ICMP messages.
  287          */
  288         if (!ipflow_active || ip->ip_p == IPPROTO_ICMP)
  289                 return;
  290         /*
  291          * See if an existing flow struct exists.  If so remove it from it's
  292          * list and free the old route.  If not, try to malloc a new one
  293          * (if we aren't at our limit).
  294          */
  295         ipf = ipflow_lookup(ip);
  296         if (ipf == NULL) {
  297                 if (ipflow_inuse == IPFLOW_MAX) {
  298                         ipf = ipflow_reap();
  299                 } else {
  300                         ipf = (struct ipflow *) malloc(sizeof(*ipf), M_IPFLOW,
  301                                                        M_NOWAIT);
  302                         if (ipf == NULL)
  303                                 return;
  304                         ipflow_inuse++;
  305                 }
  306                 bzero((caddr_t) ipf, sizeof(*ipf));
  307         } else {
  308                 s = splimp();
  309                 LIST_REMOVE(ipf, ipf_next);
  310                 splx(s);
  311                 ipflow_addstats(ipf);
  312                 RTFREE(ipf->ipf_ro.ro_rt);
  313                 ipf->ipf_uses = ipf->ipf_last_uses = 0;
  314                 ipf->ipf_errors = ipf->ipf_dropped = 0;
  315         }
  316 
  317         /*
  318          * Fill in the updated information.
  319          */
  320         ipf->ipf_ro = *ro;
  321         ro->ro_rt->rt_refcnt++;
  322         ipf->ipf_dst = ip->ip_dst;
  323         ipf->ipf_src = ip->ip_src;
  324         ipf->ipf_tos = ip->ip_tos;
  325         ipf->ipf_timer = IPFLOW_TIMER;
  326         /*
  327          * Insert into the approriate bucket of the flow table.
  328          */
  329         hash = ipflow_hash(ip->ip_dst, ip->ip_src, ip->ip_tos);
  330         s = splimp();
  331         LIST_INSERT_HEAD(&ipflows[hash], ipf, ipf_next);
  332         splx(s);
  333 }

Cache object: 8fe4fc742881ca8bd93d78642897f768


[ 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.