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 /*      $NetBSD: ip_flow.c,v 1.29 2005/02/03 22:43:34 perry Exp $       */
    2 
    3 /*-
    4  * Copyright (c) 1998 The NetBSD Foundation, Inc.
    5  * All rights reserved.
    6  *
    7  * This code is derived from software contributed to The NetBSD Foundation
    8  * by the 3am Software Foundry ("3am").  It was developed by Matt Thomas.
    9  *
   10  * Redistribution and use in source and binary forms, with or without
   11  * modification, are permitted provided that the following conditions
   12  * are met:
   13  * 1. Redistributions of source code must retain the above copyright
   14  *    notice, this list of conditions and the following disclaimer.
   15  * 2. Redistributions in binary form must reproduce the above copyright
   16  *    notice, this list of conditions and the following disclaimer in the
   17  *    documentation and/or other materials provided with the distribution.
   18  * 3. All advertising materials mentioning features or use of this software
   19  *    must display the following acknowledgement:
   20  *      This product includes software developed by the NetBSD
   21  *      Foundation, Inc. and its contributors.
   22  * 4. Neither the name of The NetBSD Foundation nor the names of its
   23  *    contributors may be used to endorse or promote products derived
   24  *    from this software without specific prior written permission.
   25  *
   26  * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
   27  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
   28  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
   29  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
   30  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
   31  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
   32  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
   33  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
   34  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
   35  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
   36  * POSSIBILITY OF SUCH DAMAGE.
   37  */
   38 
   39 #include <sys/cdefs.h>
   40 __KERNEL_RCSID(0, "$NetBSD: ip_flow.c,v 1.29 2005/02/03 22:43:34 perry Exp $");
   41 
   42 #include <sys/param.h>
   43 #include <sys/systm.h>
   44 #include <sys/malloc.h>
   45 #include <sys/mbuf.h>
   46 #include <sys/domain.h>
   47 #include <sys/protosw.h>
   48 #include <sys/socket.h>
   49 #include <sys/socketvar.h>
   50 #include <sys/errno.h>
   51 #include <sys/time.h>
   52 #include <sys/kernel.h>
   53 #include <sys/pool.h>
   54 #include <sys/sysctl.h>
   55 
   56 #include <net/if.h>
   57 #include <net/if_dl.h>
   58 #include <net/route.h>
   59 #include <net/pfil.h>
   60 
   61 #include <netinet/in.h>
   62 #include <netinet/in_systm.h>
   63 #include <netinet/ip.h>
   64 #include <netinet/in_pcb.h>
   65 #include <netinet/in_var.h>
   66 #include <netinet/ip_var.h>
   67 
   68 POOL_INIT(ipflow_pool, sizeof(struct ipflow), 0, 0, 0, "ipflowpl", NULL);
   69 
   70 LIST_HEAD(ipflowhead, ipflow);
   71 
   72 #define IPFLOW_TIMER            (5 * PR_SLOWHZ)
   73 #define IPFLOW_HASHSIZE         (1 << IPFLOW_HASHBITS)
   74 
   75 static struct ipflowhead ipflowtable[IPFLOW_HASHSIZE];
   76 static struct ipflowhead ipflowlist;
   77 static int ipflow_inuse;
   78 
   79 #define IPFLOW_INSERT(bucket, ipf) \
   80 do { \
   81         LIST_INSERT_HEAD((bucket), (ipf), ipf_hash); \
   82         LIST_INSERT_HEAD(&ipflowlist, (ipf), ipf_list); \
   83 } while (/*CONSTCOND*/ 0)
   84 
   85 #define IPFLOW_REMOVE(ipf) \
   86 do { \
   87         LIST_REMOVE((ipf), ipf_hash); \
   88         LIST_REMOVE((ipf), ipf_list); \
   89 } while (/*CONSTCOND*/ 0)
   90 
   91 #ifndef IPFLOW_MAX
   92 #define IPFLOW_MAX              256
   93 #endif
   94 int ip_maxflows = IPFLOW_MAX;
   95 
   96 static unsigned
   97 ipflow_hash(struct in_addr dst, struct in_addr src, unsigned tos)
   98 {
   99         unsigned hash = tos;
  100         int idx;
  101         for (idx = 0; idx < 32; idx += IPFLOW_HASHBITS)
  102                 hash += (dst.s_addr >> (32 - idx)) + (src.s_addr >> idx);
  103         return hash & (IPFLOW_HASHSIZE-1);
  104 }
  105 
  106 static struct ipflow *
  107 ipflow_lookup(const struct ip *ip)
  108 {
  109         unsigned hash;
  110         struct ipflow *ipf;
  111 
  112         hash = ipflow_hash(ip->ip_dst, ip->ip_src, ip->ip_tos);
  113 
  114         ipf = LIST_FIRST(&ipflowtable[hash]);
  115         while (ipf != NULL) {
  116                 if (ip->ip_dst.s_addr == ipf->ipf_dst.s_addr
  117                     && ip->ip_src.s_addr == ipf->ipf_src.s_addr
  118                     && ip->ip_tos == ipf->ipf_tos)
  119                         break;
  120                 ipf = LIST_NEXT(ipf, ipf_hash);
  121         }
  122         return ipf;
  123 }
  124 
  125 void
  126 ipflow_init(void)
  127 {
  128         int i;
  129 
  130         LIST_INIT(&ipflowlist);
  131         for (i = 0; i < IPFLOW_HASHSIZE; i++)
  132                 LIST_INIT(&ipflowtable[i]);
  133 }
  134 
  135 int
  136 ipflow_fastforward(struct mbuf *m)
  137 {
  138         struct ip *ip, ip_store;
  139         struct ipflow *ipf;
  140         struct rtentry *rt;
  141         struct sockaddr *dst;
  142         int error;
  143         int iplen;
  144 
  145         /*
  146          * Are we forwarding packets?  Big enough for an IP packet?
  147          */
  148         if (!ipforwarding || ipflow_inuse == 0 || m->m_len < sizeof(struct ip))
  149                 return 0;
  150 
  151         /*
  152          * Was packet received as a link-level multicast or broadcast?
  153          * If so, don't try to fast forward..
  154          */
  155         if ((m->m_flags & (M_BCAST|M_MCAST)) != 0)
  156                 return 0;
  157 
  158         /*
  159          * IP header with no option and valid version and length
  160          */
  161         if (IP_HDR_ALIGNED_P(mtod(m, caddr_t)))
  162                 ip = mtod(m, struct ip *);
  163         else {
  164                 memcpy(&ip_store, mtod(m, caddr_t), sizeof(ip_store));
  165                 ip = &ip_store;
  166         }
  167         iplen = ntohs(ip->ip_len);
  168         if (ip->ip_v != IPVERSION || ip->ip_hl != (sizeof(struct ip) >> 2) ||
  169             iplen < sizeof(struct ip) || iplen > m->m_pkthdr.len)
  170                 return 0;
  171         /*
  172          * Find a flow.
  173          */
  174         if ((ipf = ipflow_lookup(ip)) == NULL)
  175                 return 0;
  176 
  177         /*
  178          * Verify the IP header checksum.
  179          */
  180         switch (m->m_pkthdr.csum_flags &
  181                 ((m->m_pkthdr.rcvif->if_csum_flags_rx & M_CSUM_IPv4) |
  182                  M_CSUM_IPv4_BAD)) {
  183         case M_CSUM_IPv4|M_CSUM_IPv4_BAD:
  184                 return (0);
  185 
  186         case M_CSUM_IPv4:
  187                 /* Checksum was okay. */
  188                 break;
  189 
  190         default:
  191                 /* Must compute it ourselves. */
  192                 if (in_cksum(m, sizeof(struct ip)) != 0)
  193                         return (0);
  194                 break;
  195         }
  196 
  197         /*
  198          * Route and interface still up?
  199          */
  200         rt = ipf->ipf_ro.ro_rt;
  201         if ((rt->rt_flags & RTF_UP) == 0 ||
  202             (rt->rt_ifp->if_flags & IFF_UP) == 0)
  203                 return 0;
  204 
  205         /*
  206          * Packet size OK?  TTL?
  207          */
  208         if (m->m_pkthdr.len > rt->rt_ifp->if_mtu || ip->ip_ttl <= IPTTLDEC)
  209                 return 0;
  210 
  211         /*
  212          * Clear any in-bound checksum flags for this packet.
  213          */
  214         m->m_pkthdr.csum_flags = 0;
  215 
  216         /*
  217          * Everything checks out and so we can forward this packet.
  218          * Modify the TTL and incrementally change the checksum.
  219          *
  220          * This method of adding the checksum works on either endian CPU.
  221          * If htons() is inlined, all the arithmetic is folded; otherwise
  222          * the htons()s are combined by CSE due to the __const__ attribute.
  223          *
  224          * Don't bother using HW checksumming here -- the incremental
  225          * update is pretty fast.
  226          */
  227         ip->ip_ttl -= IPTTLDEC;
  228         if (ip->ip_sum >= (u_int16_t) ~htons(IPTTLDEC << 8))
  229                 ip->ip_sum -= ~htons(IPTTLDEC << 8);
  230         else
  231                 ip->ip_sum += htons(IPTTLDEC << 8);
  232 
  233         /*
  234          * Done modifying the header; copy it back, if necessary.
  235          */
  236         if (IP_HDR_ALIGNED_P(mtod(m, caddr_t)) == 0)
  237                 memcpy(mtod(m, caddr_t), &ip_store, sizeof(ip_store));
  238 
  239         /*
  240          * Trim the packet in case it's too long..
  241          */
  242         if (m->m_pkthdr.len > iplen) {
  243                 if (m->m_len == m->m_pkthdr.len) {
  244                         m->m_len = iplen;
  245                         m->m_pkthdr.len = iplen;
  246                 } else
  247                         m_adj(m, iplen - m->m_pkthdr.len);
  248         }
  249 
  250         /*
  251          * Send the packet on it's way.  All we can get back is ENOBUFS
  252          */
  253         ipf->ipf_uses++;
  254         PRT_SLOW_ARM(ipf->ipf_timer, IPFLOW_TIMER);
  255 
  256         if (rt->rt_flags & RTF_GATEWAY)
  257                 dst = rt->rt_gateway;
  258         else
  259                 dst = &ipf->ipf_ro.ro_dst;
  260 
  261         if ((error = (*rt->rt_ifp->if_output)(rt->rt_ifp, m, dst, rt)) != 0) {
  262                 if (error == ENOBUFS)
  263                         ipf->ipf_dropped++;
  264                 else
  265                         ipf->ipf_errors++;
  266         }
  267         return 1;
  268 }
  269 
  270 static void
  271 ipflow_addstats(struct ipflow *ipf)
  272 {
  273         ipf->ipf_ro.ro_rt->rt_use += ipf->ipf_uses;
  274         ipstat.ips_cantforward += ipf->ipf_errors + ipf->ipf_dropped;
  275         ipstat.ips_forward += ipf->ipf_uses;
  276         ipstat.ips_fastforward += ipf->ipf_uses;
  277 }
  278 
  279 static void
  280 ipflow_free(struct ipflow *ipf)
  281 {
  282         int s;
  283         /*
  284          * Remove the flow from the hash table (at elevated IPL).
  285          * Once it's off the list, we can deal with it at normal
  286          * network IPL.
  287          */
  288         s = splnet();
  289         IPFLOW_REMOVE(ipf);
  290         splx(s);
  291         ipflow_addstats(ipf);
  292         RTFREE(ipf->ipf_ro.ro_rt);
  293         ipflow_inuse--;
  294         pool_put(&ipflow_pool, ipf);
  295 }
  296 
  297 struct ipflow *
  298 ipflow_reap(int just_one)
  299 {
  300         while (just_one || ipflow_inuse > ip_maxflows) {
  301                 struct ipflow *ipf, *maybe_ipf = NULL;
  302                 int s;
  303 
  304                 ipf = LIST_FIRST(&ipflowlist);
  305                 while (ipf != NULL) {
  306                         /*
  307                          * If this no longer points to a valid route
  308                          * reclaim it.
  309                          */
  310                         if ((ipf->ipf_ro.ro_rt->rt_flags & RTF_UP) == 0)
  311                                 goto done;
  312                         /*
  313                          * choose the one that's been least recently
  314                          * used or has had the least uses in the
  315                          * last 1.5 intervals.
  316                          */
  317                         if (maybe_ipf == NULL ||
  318                             ipf->ipf_timer < maybe_ipf->ipf_timer ||
  319                             (ipf->ipf_timer == maybe_ipf->ipf_timer &&
  320                              ipf->ipf_last_uses + ipf->ipf_uses <
  321                                  maybe_ipf->ipf_last_uses +
  322                                  maybe_ipf->ipf_uses))
  323                                 maybe_ipf = ipf;
  324                         ipf = LIST_NEXT(ipf, ipf_list);
  325                 }
  326                 ipf = maybe_ipf;
  327             done:
  328                 /*
  329                  * Remove the entry from the flow table.
  330                  */
  331                 s = splnet();
  332                 IPFLOW_REMOVE(ipf);
  333                 splx(s);
  334                 ipflow_addstats(ipf);
  335                 RTFREE(ipf->ipf_ro.ro_rt);
  336                 if (just_one)
  337                         return ipf;
  338                 pool_put(&ipflow_pool, ipf);
  339                 ipflow_inuse--;
  340         }
  341         return NULL;
  342 }
  343 
  344 void
  345 ipflow_slowtimo(void)
  346 {
  347         struct ipflow *ipf, *next_ipf;
  348 
  349         ipf = LIST_FIRST(&ipflowlist);
  350         while (ipf != NULL) {
  351                 next_ipf = LIST_NEXT(ipf, ipf_list);
  352                 if (PRT_SLOW_ISEXPIRED(ipf->ipf_timer)) {
  353                         ipflow_free(ipf);
  354                 } else {
  355                         ipf->ipf_last_uses = ipf->ipf_uses;
  356                         ipf->ipf_ro.ro_rt->rt_use += ipf->ipf_uses;
  357                         ipstat.ips_forward += ipf->ipf_uses;
  358                         ipstat.ips_fastforward += ipf->ipf_uses;
  359                         ipf->ipf_uses = 0;
  360                 }
  361                 ipf = next_ipf;
  362         }
  363 }
  364 
  365 void
  366 ipflow_create(const struct route *ro, struct mbuf *m)
  367 {
  368         const struct ip *const ip = mtod(m, struct ip *);
  369         struct ipflow *ipf;
  370         unsigned hash;
  371         int s;
  372 
  373         /*
  374          * Don't create cache entries for ICMP messages.
  375          */
  376         if (ip_maxflows == 0 || ip->ip_p == IPPROTO_ICMP)
  377                 return;
  378         /*
  379          * See if an existing flow struct exists.  If so remove it from it's
  380          * list and free the old route.  If not, try to malloc a new one
  381          * (if we aren't at our limit).
  382          */
  383         ipf = ipflow_lookup(ip);
  384         if (ipf == NULL) {
  385                 if (ipflow_inuse >= ip_maxflows) {
  386                         ipf = ipflow_reap(1);
  387                 } else {
  388                         ipf = pool_get(&ipflow_pool, PR_NOWAIT);
  389                         if (ipf == NULL)
  390                                 return;
  391                         ipflow_inuse++;
  392                 }
  393                 bzero((caddr_t) ipf, sizeof(*ipf));
  394         } else {
  395                 s = splnet();
  396                 IPFLOW_REMOVE(ipf);
  397                 splx(s);
  398                 ipflow_addstats(ipf);
  399                 RTFREE(ipf->ipf_ro.ro_rt);
  400                 ipf->ipf_uses = ipf->ipf_last_uses = 0;
  401                 ipf->ipf_errors = ipf->ipf_dropped = 0;
  402         }
  403 
  404         /*
  405          * Fill in the updated information.
  406          */
  407         ipf->ipf_ro = *ro;
  408         ro->ro_rt->rt_refcnt++;
  409         ipf->ipf_dst = ip->ip_dst;
  410         ipf->ipf_src = ip->ip_src;
  411         ipf->ipf_tos = ip->ip_tos;
  412         PRT_SLOW_ARM(ipf->ipf_timer, IPFLOW_TIMER);
  413         ipf->ipf_start = time.tv_sec;
  414         /*
  415          * Insert into the approriate bucket of the flow table.
  416          */
  417         hash = ipflow_hash(ip->ip_dst, ip->ip_src, ip->ip_tos);
  418         s = splnet();
  419         IPFLOW_INSERT(&ipflowtable[hash], ipf);
  420         splx(s);
  421 }
  422 
  423 void
  424 ipflow_invalidate_all(void)
  425 {
  426         struct ipflow *ipf, *next_ipf;
  427         int s;
  428 
  429         s = splnet();
  430         ipf = LIST_FIRST(&ipflowlist);
  431         for (ipf = LIST_FIRST(&ipflowlist); ipf != NULL; ipf = next_ipf) {
  432                 next_ipf = LIST_NEXT(ipf, ipf_list);
  433                 ipflow_free(ipf);
  434         }
  435         splx(s);
  436 }

Cache object: 21885983b703a683fcdb70b8c8a072d5


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