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$
   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, "");
   69 
   70 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         int error;
  113 
  114         /*
  115          * Are we forwarding packets?  Big enough for an IP packet?
  116          */
  117         if (!ipforwarding || !ipflow_active || m->m_len < sizeof(struct ip))
  118                 return 0;
  119         /*
  120          * IP header with no option and valid version and length
  121          */
  122         ip = mtod(m, struct ip *);
  123         if (ip->ip_v != IPVERSION || ip->ip_hl != (sizeof(struct ip) >> 2)
  124             || ntohs(ip->ip_len) > m->m_pkthdr.len)
  125                 return 0;
  126         /*
  127          * Find a flow.
  128          */
  129         if ((ipf = ipflow_lookup(ip)) == NULL)
  130                 return 0;
  131 
  132         /*
  133          * Route and interface still up?
  134          */
  135         rt = ipf->ipf_ro.ro_rt;
  136         if ((rt->rt_flags & RTF_UP) == 0 || (rt->rt_ifp->if_flags & IFF_UP) == 0)
  137                 return 0;
  138 
  139         /*
  140          * Packet size OK?  TTL?
  141          */
  142         if (m->m_pkthdr.len > rt->rt_ifp->if_mtu || ip->ip_ttl <= IPTTLDEC)
  143                 return 0;
  144 
  145         /*
  146          * Everything checks out and so we can forward this packet.
  147          * Modify the TTL and incrementally change the checksum.
  148          */
  149         ip->ip_ttl -= IPTTLDEC;
  150         if (ip->ip_sum >= htons(0xffff - (IPTTLDEC << 8))) {
  151                 ip->ip_sum += htons(IPTTLDEC << 8) + 1;
  152         } else {
  153                 ip->ip_sum += htons(IPTTLDEC << 8);
  154         }
  155 
  156         /*
  157          * Send the packet on its way.  All we can get back is ENOBUFS
  158          */
  159         ipf->ipf_uses++;
  160         ipf->ipf_timer = IPFLOW_TIMER;
  161         if ((error = (*rt->rt_ifp->if_output)(rt->rt_ifp, m, &ipf->ipf_ro.ro_dst, rt)) != 0) {
  162                 if (error == ENOBUFS)
  163                         ipf->ipf_dropped++;
  164                 else
  165                         ipf->ipf_errors++;
  166         }
  167         return 1;
  168 }
  169 
  170 static void
  171 ipflow_addstats(
  172         struct ipflow *ipf)
  173 {
  174         ipf->ipf_ro.ro_rt->rt_use += ipf->ipf_uses;
  175         ipstat.ips_cantforward += ipf->ipf_errors + ipf->ipf_dropped;
  176         ipstat.ips_forward += ipf->ipf_uses;
  177         ipstat.ips_fastforward += ipf->ipf_uses;
  178 }
  179 
  180 static void
  181 ipflow_free(
  182         struct ipflow *ipf)
  183 {
  184         int s;
  185         /*
  186          * Remove the flow from the hash table (at elevated IPL).
  187          * Once it's off the list, we can deal with it at normal
  188          * network IPL.
  189          */
  190         s = splimp();
  191         LIST_REMOVE(ipf, ipf_next);
  192         splx(s);
  193         ipflow_addstats(ipf);
  194         RTFREE(ipf->ipf_ro.ro_rt);
  195         ipflow_inuse--;
  196         FREE(ipf, M_IPFLOW);
  197 }
  198 
  199 static struct ipflow *
  200 ipflow_reap(
  201         void)
  202 {
  203         struct ipflow *ipf, *maybe_ipf = NULL;
  204         int idx;
  205         int s;
  206 
  207         for (idx = 0; idx < IPFLOW_HASHSIZE; idx++) {
  208                 ipf = LIST_FIRST(&ipflows[idx]);
  209                 while (ipf != NULL) {
  210                         /*
  211                          * If this no longer points to a valid route
  212                          * reclaim it.
  213                          */
  214                         if ((ipf->ipf_ro.ro_rt->rt_flags & RTF_UP) == 0)
  215                                 goto done;
  216                         /*
  217                          * choose the one that's been least recently used
  218                          * or has had the least uses in the last 1.5 
  219                          * intervals.
  220                          */
  221                         if (maybe_ipf == NULL
  222                             || ipf->ipf_timer < maybe_ipf->ipf_timer
  223                             || (ipf->ipf_timer == maybe_ipf->ipf_timer
  224                                 && ipf->ipf_last_uses + ipf->ipf_uses <
  225                                       maybe_ipf->ipf_last_uses +
  226                                         maybe_ipf->ipf_uses))
  227                                 maybe_ipf = ipf;
  228                         ipf = LIST_NEXT(ipf, ipf_next);
  229                 }
  230         }
  231         ipf = maybe_ipf;
  232     done:
  233         /*
  234          * Remove the entry from the flow table.
  235          */
  236         s = splimp();
  237         LIST_REMOVE(ipf, ipf_next);
  238         splx(s);
  239         ipflow_addstats(ipf);
  240         RTFREE(ipf->ipf_ro.ro_rt);
  241         return ipf;
  242 }
  243 
  244 void
  245 ipflow_slowtimo(
  246         void)
  247 {
  248         struct ipflow *ipf;
  249         int idx;
  250 
  251         for (idx = 0; idx < IPFLOW_HASHSIZE; idx++) {
  252                 ipf = LIST_FIRST(&ipflows[idx]);
  253                 while (ipf != NULL) {
  254                         struct ipflow *next_ipf = LIST_NEXT(ipf, ipf_next);
  255                         if (--ipf->ipf_timer == 0) {
  256                                 ipflow_free(ipf);
  257                         } else {
  258                                 ipf->ipf_last_uses = ipf->ipf_uses;
  259                                 ipf->ipf_ro.ro_rt->rt_use += ipf->ipf_uses;
  260                                 ipstat.ips_forward += ipf->ipf_uses;
  261                                 ipstat.ips_fastforward += ipf->ipf_uses;
  262                                 ipf->ipf_uses = 0;
  263                         }
  264                         ipf = next_ipf;
  265                 }
  266         }
  267 }
  268 
  269 void
  270 ipflow_create(
  271         const struct route *ro,
  272         struct mbuf *m)
  273 {
  274         const struct ip *const ip = mtod(m, struct ip *);
  275         struct ipflow *ipf;
  276         unsigned hash;
  277         int s;
  278 
  279         /*
  280          * Don't create cache entries for ICMP messages.
  281          */
  282         if (!ipflow_active || ip->ip_p == IPPROTO_ICMP)
  283                 return;
  284         /*
  285          * See if an existing flow struct exists.  If so remove it from it's
  286          * list and free the old route.  If not, try to malloc a new one
  287          * (if we aren't at our limit).
  288          */
  289         ipf = ipflow_lookup(ip);
  290         if (ipf == NULL) {
  291                 if (ipflow_inuse == IPFLOW_MAX) {
  292                         ipf = ipflow_reap();
  293                 } else {
  294                         ipf = (struct ipflow *) malloc(sizeof(*ipf), M_IPFLOW,
  295                                                        M_NOWAIT);
  296                         if (ipf == NULL)
  297                                 return;
  298                         ipflow_inuse++;
  299                 }
  300                 bzero((caddr_t) ipf, sizeof(*ipf));
  301         } else {
  302                 s = splimp();
  303                 LIST_REMOVE(ipf, ipf_next);
  304                 splx(s);
  305                 ipflow_addstats(ipf);
  306                 RTFREE(ipf->ipf_ro.ro_rt);
  307                 ipf->ipf_uses = ipf->ipf_last_uses = 0;
  308                 ipf->ipf_errors = ipf->ipf_dropped = 0;
  309         }
  310 
  311         /*
  312          * Fill in the updated information.
  313          */
  314         ipf->ipf_ro = *ro;
  315         ro->ro_rt->rt_refcnt++;
  316         ipf->ipf_dst = ip->ip_dst;
  317         ipf->ipf_src = ip->ip_src;
  318         ipf->ipf_tos = ip->ip_tos;
  319         ipf->ipf_timer = IPFLOW_TIMER;
  320         /*
  321          * Insert into the approriate bucket of the flow table.
  322          */
  323         hash = ipflow_hash(ip->ip_dst, ip->ip_src, ip->ip_tos);
  324         s = splimp();
  325         LIST_INSERT_HEAD(&ipflows[hash], ipf, ipf_next);
  326         splx(s);
  327 }

Cache object: a0a3414e6216dced2bc74426e74dfeda


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