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/netgraph/netflow/netflow.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  * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
    3  *
    4  * Copyright (c) 2010-2011 Alexander V. Chernikov <melifaro@ipfw.ru>
    5  * Copyright (c) 2004-2005 Gleb Smirnoff <glebius@FreeBSD.org>
    6  * Copyright (c) 2001-2003 Roman V. Palagin <romanp@unshadow.net>
    7  * All rights reserved.
    8  *
    9  * Redistribution and use in source and binary forms, with or without
   10  * modification, are permitted provided that the following conditions
   11  * are met:
   12  * 1. Redistributions of source code must retain the above copyright
   13  *    notice, this list of conditions and the following disclaimer.
   14  * 2. Redistributions in binary form must reproduce the above copyright
   15  *    notice, this list of conditions and the following disclaimer in the
   16  *    documentation and/or other materials provided with the distribution.
   17  *
   18  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
   19  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
   20  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
   21  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
   22  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
   23  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
   24  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
   25  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
   26  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
   27  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
   28  * SUCH DAMAGE.
   29  *
   30  * $SourceForge: netflow.c,v 1.41 2004/09/05 11:41:10 glebius Exp $
   31  */
   32 
   33 #include <sys/cdefs.h>
   34 __FBSDID("$FreeBSD$");
   35 
   36 #include "opt_inet.h"
   37 #include "opt_inet6.h"
   38 #include "opt_route.h"
   39 #include <sys/param.h>
   40 #include <sys/bitstring.h>
   41 #include <sys/systm.h>
   42 #include <sys/counter.h>
   43 #include <sys/kernel.h>
   44 #include <sys/ktr.h>
   45 #include <sys/limits.h>
   46 #include <sys/mbuf.h>
   47 #include <sys/syslog.h>
   48 #include <sys/socket.h>
   49 #include <vm/uma.h>
   50 
   51 #include <net/if.h>
   52 #include <net/if_dl.h>
   53 #include <net/if_var.h>
   54 #include <net/route.h>
   55 #include <net/route/nhop.h>
   56 #include <net/route/route_ctl.h>
   57 #include <net/ethernet.h>
   58 #include <netinet/in.h>
   59 #include <netinet/in_fib.h>
   60 #include <netinet/in_systm.h>
   61 #include <netinet/ip.h>
   62 #include <netinet/ip6.h>
   63 #include <netinet/tcp.h>
   64 #include <netinet/udp.h>
   65 
   66 #include <netinet6/in6_fib.h>
   67 
   68 #include <netgraph/ng_message.h>
   69 #include <netgraph/netgraph.h>
   70 
   71 #include <netgraph/netflow/netflow.h>
   72 #include <netgraph/netflow/netflow_v9.h>
   73 #include <netgraph/netflow/ng_netflow.h>
   74 
   75 #define NBUCKETS        (65536)         /* must be power of 2 */
   76 
   77 /* This hash is for TCP or UDP packets. */
   78 #define FULL_HASH(addr1, addr2, port1, port2)   \
   79         (((addr1 ^ (addr1 >> 16) ^              \
   80         htons(addr2 ^ (addr2 >> 16))) ^         \
   81         port1 ^ htons(port2)) &                 \
   82         (NBUCKETS - 1))
   83 
   84 /* This hash is for all other IP packets. */
   85 #define ADDR_HASH(addr1, addr2)                 \
   86         ((addr1 ^ (addr1 >> 16) ^               \
   87         htons(addr2 ^ (addr2 >> 16))) &         \
   88         (NBUCKETS - 1))
   89 
   90 /* Macros to shorten logical constructions */
   91 /* XXX: priv must exist in namespace */
   92 #define INACTIVE(fle)   (time_uptime - fle->f.last > priv->nfinfo_inact_t)
   93 #define AGED(fle)       (time_uptime - fle->f.first > priv->nfinfo_act_t)
   94 #define ISFREE(fle)     (fle->f.packets == 0)
   95 
   96 /*
   97  * 4 is a magical number: statistically number of 4-packet flows is
   98  * bigger than 5,6,7...-packet flows by an order of magnitude. Most UDP/ICMP
   99  * scans are 1 packet (~ 90% of flow cache). TCP scans are 2-packet in case
  100  * of reachable host and 4-packet otherwise.
  101  */
  102 #define SMALL(fle)      (fle->f.packets <= 4)
  103 
  104 MALLOC_DEFINE(M_NETFLOW_HASH, "netflow_hash", "NetFlow hash");
  105 
  106 static int export_add(item_p, struct flow_entry *);
  107 static int export_send(priv_p, fib_export_p, item_p, int);
  108 
  109 #ifdef INET
  110 static int hash_insert(priv_p, struct flow_hash_entry *, struct flow_rec *,
  111     int, uint8_t, uint8_t);
  112 #endif
  113 #ifdef INET6
  114 static int hash6_insert(priv_p, struct flow_hash_entry *, struct flow6_rec *,
  115     int, uint8_t, uint8_t);
  116 #endif
  117 
  118 static void expire_flow(priv_p, fib_export_p, struct flow_entry *, int);
  119 
  120 #ifdef INET
  121 /*
  122  * Generate hash for a given flow record.
  123  *
  124  * FIB is not used here, because:
  125  * most VRFS will carry public IPv4 addresses which are unique even
  126  * without FIB private addresses can overlap, but this is worked out
  127  * via flow_rec bcmp() containing fib id. In IPv6 world addresses are
  128  * all globally unique (it's not fully true, there is FC00::/7 for example,
  129  * but chances of address overlap are MUCH smaller)
  130  */
  131 static inline uint32_t
  132 ip_hash(struct flow_rec *r)
  133 {
  134 
  135         switch (r->r_ip_p) {
  136         case IPPROTO_TCP:
  137         case IPPROTO_UDP:
  138                 return FULL_HASH(r->r_src.s_addr, r->r_dst.s_addr,
  139                     r->r_sport, r->r_dport);
  140         default:
  141                 return ADDR_HASH(r->r_src.s_addr, r->r_dst.s_addr);
  142         }
  143 }
  144 #endif
  145 
  146 #ifdef INET6
  147 /* Generate hash for a given flow6 record. Use lower 4 octets from v6 addresses */
  148 static inline uint32_t
  149 ip6_hash(struct flow6_rec *r)
  150 {
  151 
  152         switch (r->r_ip_p) {
  153         case IPPROTO_TCP:
  154         case IPPROTO_UDP:
  155                 return FULL_HASH(r->src.r_src6.__u6_addr.__u6_addr32[3],
  156                     r->dst.r_dst6.__u6_addr.__u6_addr32[3], r->r_sport,
  157                     r->r_dport);
  158         default:
  159                 return ADDR_HASH(r->src.r_src6.__u6_addr.__u6_addr32[3],
  160                     r->dst.r_dst6.__u6_addr.__u6_addr32[3]);
  161         }
  162 }
  163 
  164 #endif
  165 
  166 /*
  167  * Detach export datagram from priv, if there is any.
  168  * If there is no, allocate a new one.
  169  */
  170 static item_p
  171 get_export_dgram(priv_p priv, fib_export_p fe)
  172 {
  173         item_p  item = NULL;
  174 
  175         mtx_lock(&fe->export_mtx);
  176         if (fe->exp.item != NULL) {
  177                 item = fe->exp.item;
  178                 fe->exp.item = NULL;
  179         }
  180         mtx_unlock(&fe->export_mtx);
  181 
  182         if (item == NULL) {
  183                 struct netflow_v5_export_dgram *dgram;
  184                 struct mbuf *m;
  185 
  186                 m = m_getcl(M_NOWAIT, MT_DATA, M_PKTHDR);
  187                 if (m == NULL)
  188                         return (NULL);
  189                 item = ng_package_data(m, NG_NOFLAGS);
  190                 if (item == NULL)
  191                         return (NULL);
  192                 dgram = mtod(m, struct netflow_v5_export_dgram *);
  193                 dgram->header.count = 0;
  194                 dgram->header.version = htons(NETFLOW_V5);
  195                 dgram->header.pad = 0;
  196         }
  197 
  198         return (item);
  199 }
  200 
  201 /*
  202  * Re-attach incomplete datagram back to priv.
  203  * If there is already another one, then send incomplete. */
  204 static void
  205 return_export_dgram(priv_p priv, fib_export_p fe, item_p item, int flags)
  206 {
  207 
  208         /*
  209          * It may happen on SMP, that some thread has already
  210          * put its item there, in this case we bail out and
  211          * send what we have to collector.
  212          */
  213         mtx_lock(&fe->export_mtx);
  214         if (fe->exp.item == NULL) {
  215                 fe->exp.item = item;
  216                 mtx_unlock(&fe->export_mtx);
  217         } else {
  218                 mtx_unlock(&fe->export_mtx);
  219                 export_send(priv, fe, item, flags);
  220         }
  221 }
  222 
  223 /*
  224  * The flow is over. Call export_add() and free it. If datagram is
  225  * full, then call export_send().
  226  */
  227 static void
  228 expire_flow(priv_p priv, fib_export_p fe, struct flow_entry *fle, int flags)
  229 {
  230         struct netflow_export_item exp;
  231         uint16_t version = fle->f.version;
  232 
  233         if ((priv->export != NULL) && (version == IPVERSION)) {
  234                 exp.item = get_export_dgram(priv, fe);
  235                 if (exp.item == NULL) {
  236                         priv->nfinfo_export_failed++;
  237                         if (priv->export9 != NULL)
  238                                 priv->nfinfo_export9_failed++;
  239                         /* fle definitely contains IPv4 flow. */
  240                         uma_zfree_arg(priv->zone, fle, priv);
  241                         return;
  242                 }
  243 
  244                 if (export_add(exp.item, fle) > 0)
  245                         export_send(priv, fe, exp.item, flags);
  246                 else
  247                         return_export_dgram(priv, fe, exp.item, NG_QUEUE);
  248         }
  249 
  250         if (priv->export9 != NULL) {
  251                 exp.item9 = get_export9_dgram(priv, fe, &exp.item9_opt);
  252                 if (exp.item9 == NULL) {
  253                         priv->nfinfo_export9_failed++;
  254                         if (version == IPVERSION)
  255                                 uma_zfree_arg(priv->zone, fle, priv);
  256 #ifdef INET6
  257                         else if (version == IP6VERSION)
  258                                 uma_zfree_arg(priv->zone6, fle, priv);
  259 #endif
  260                         else
  261                                 panic("ng_netflow: Unknown IP proto: %d",
  262                                     version);
  263                         return;
  264                 }
  265 
  266                 if (export9_add(exp.item9, exp.item9_opt, fle) > 0)
  267                         export9_send(priv, fe, exp.item9, exp.item9_opt, flags);
  268                 else
  269                         return_export9_dgram(priv, fe, exp.item9,
  270                             exp.item9_opt, NG_QUEUE);
  271         }
  272 
  273         if (version == IPVERSION)
  274                 uma_zfree_arg(priv->zone, fle, priv);
  275 #ifdef INET6
  276         else if (version == IP6VERSION)
  277                 uma_zfree_arg(priv->zone6, fle, priv);
  278 #endif
  279 }
  280 
  281 /* Get a snapshot of node statistics */
  282 void
  283 ng_netflow_copyinfo(priv_p priv, struct ng_netflow_info *i)
  284 {
  285 
  286         i->nfinfo_bytes = counter_u64_fetch(priv->nfinfo_bytes);
  287         i->nfinfo_packets = counter_u64_fetch(priv->nfinfo_packets);
  288         i->nfinfo_bytes6 = counter_u64_fetch(priv->nfinfo_bytes6);
  289         i->nfinfo_packets6 = counter_u64_fetch(priv->nfinfo_packets6);
  290         i->nfinfo_sbytes = counter_u64_fetch(priv->nfinfo_sbytes);
  291         i->nfinfo_spackets = counter_u64_fetch(priv->nfinfo_spackets);
  292         i->nfinfo_sbytes6 = counter_u64_fetch(priv->nfinfo_sbytes6);
  293         i->nfinfo_spackets6 = counter_u64_fetch(priv->nfinfo_spackets6);
  294         i->nfinfo_act_exp = counter_u64_fetch(priv->nfinfo_act_exp);
  295         i->nfinfo_inact_exp = counter_u64_fetch(priv->nfinfo_inact_exp);
  296 
  297         i->nfinfo_used = uma_zone_get_cur(priv->zone);
  298 #ifdef INET6
  299         i->nfinfo_used6 = uma_zone_get_cur(priv->zone6);
  300 #endif
  301 
  302         i->nfinfo_alloc_failed = priv->nfinfo_alloc_failed;
  303         i->nfinfo_export_failed = priv->nfinfo_export_failed;
  304         i->nfinfo_export9_failed = priv->nfinfo_export9_failed;
  305         i->nfinfo_realloc_mbuf = priv->nfinfo_realloc_mbuf;
  306         i->nfinfo_alloc_fibs = priv->nfinfo_alloc_fibs;
  307         i->nfinfo_inact_t = priv->nfinfo_inact_t;
  308         i->nfinfo_act_t = priv->nfinfo_act_t;
  309 }
  310 
  311 /*
  312  * Insert a record into defined slot.
  313  *
  314  * First we get for us a free flow entry, then fill in all
  315  * possible fields in it.
  316  *
  317  * TODO: consider dropping hash mutex while filling in datagram,
  318  * as this was done in previous version. Need to test & profile
  319  * to be sure.
  320  */
  321 #ifdef INET
  322 static int
  323 hash_insert(priv_p priv, struct flow_hash_entry *hsh, struct flow_rec *r,
  324         int plen, uint8_t flags, uint8_t tcp_flags)
  325 {
  326         struct flow_entry *fle;
  327 
  328         mtx_assert(&hsh->mtx, MA_OWNED);
  329 
  330         fle = uma_zalloc_arg(priv->zone, priv, M_NOWAIT);
  331         if (fle == NULL) {
  332                 priv->nfinfo_alloc_failed++;
  333                 return (ENOMEM);
  334         }
  335 
  336         /*
  337          * Now fle is totally ours. It is detached from all lists,
  338          * we can safely edit it.
  339          */
  340         fle->f.version = IPVERSION;
  341         bcopy(r, &fle->f.r, sizeof(struct flow_rec));
  342         fle->f.bytes = plen;
  343         fle->f.packets = 1;
  344         fle->f.tcp_flags = tcp_flags;
  345 
  346         fle->f.first = fle->f.last = time_uptime;
  347 
  348         /*
  349          * First we do route table lookup on destination address. So we can
  350          * fill in out_ifx, dst_mask, nexthop, and dst_as in future releases.
  351          */
  352         if ((flags & NG_NETFLOW_CONF_NODSTLOOKUP) == 0) {
  353                 struct rtentry *rt;
  354                 struct route_nhop_data rnd;
  355 
  356                 rt = fib4_lookup_rt(r->fib, fle->f.r.r_dst, 0, NHR_NONE, &rnd);
  357                 if (rt != NULL) {
  358                         struct in_addr addr;
  359                         uint32_t scopeid;
  360                         struct nhop_object *nh = nhop_select_func(rnd.rnd_nhop, 0);
  361                         int plen;
  362 
  363                         rt_get_inet_prefix_plen(rt, &addr, &plen, &scopeid);
  364                         fle->f.fle_o_ifx = nh->nh_ifp->if_index;
  365                         if (nh->gw_sa.sa_family == AF_INET)
  366                                 fle->f.next_hop = nh->gw4_sa.sin_addr;
  367                         /*
  368                          * XXX we're leaving an empty gateway here for
  369                          * IPv6 nexthops.
  370                          */
  371                         fle->f.dst_mask = plen;
  372                 }
  373         }
  374 
  375         /* Do route lookup on source address, to fill in src_mask. */
  376         if ((flags & NG_NETFLOW_CONF_NOSRCLOOKUP) == 0) {
  377                 struct rtentry *rt;
  378                 struct route_nhop_data rnd;
  379 
  380                 rt = fib4_lookup_rt(r->fib, fle->f.r.r_src, 0, NHR_NONE, &rnd);
  381                 if (rt != NULL) {
  382                         struct in_addr addr;
  383                         uint32_t scopeid;
  384                         int plen;
  385 
  386                         rt_get_inet_prefix_plen(rt, &addr, &plen, &scopeid);
  387                         fle->f.src_mask = plen;
  388                 }
  389         }
  390 
  391         /* Push new flow at the and of hash. */
  392         TAILQ_INSERT_TAIL(&hsh->head, fle, fle_hash);
  393 
  394         return (0);
  395 }
  396 #endif
  397 
  398 #ifdef INET6
  399 static int
  400 hash6_insert(priv_p priv, struct flow_hash_entry *hsh6, struct flow6_rec *r,
  401         int plen, uint8_t flags, uint8_t tcp_flags)
  402 {
  403         struct flow6_entry *fle6;
  404 
  405         mtx_assert(&hsh6->mtx, MA_OWNED);
  406 
  407         fle6 = uma_zalloc_arg(priv->zone6, priv, M_NOWAIT);
  408         if (fle6 == NULL) {
  409                 priv->nfinfo_alloc_failed++;
  410                 return (ENOMEM);
  411         }
  412 
  413         /*
  414          * Now fle is totally ours. It is detached from all lists,
  415          * we can safely edit it.
  416          */
  417 
  418         fle6->f.version = IP6VERSION;
  419         bcopy(r, &fle6->f.r, sizeof(struct flow6_rec));
  420         fle6->f.bytes = plen;
  421         fle6->f.packets = 1;
  422         fle6->f.tcp_flags = tcp_flags;
  423 
  424         fle6->f.first = fle6->f.last = time_uptime;
  425 
  426         /*
  427          * First we do route table lookup on destination address. So we can
  428          * fill in out_ifx, dst_mask, nexthop, and dst_as in future releases.
  429          */
  430         if ((flags & NG_NETFLOW_CONF_NODSTLOOKUP) == 0) {
  431                 struct rtentry *rt;
  432                 struct route_nhop_data rnd;
  433 
  434                 rt = fib6_lookup_rt(r->fib, &fle6->f.r.dst.r_dst6, 0, NHR_NONE, &rnd);
  435                 if (rt != NULL) {
  436                         struct in6_addr addr;
  437                         uint32_t scopeid;
  438                         struct nhop_object *nh = nhop_select_func(rnd.rnd_nhop, 0);
  439                         int plen;
  440 
  441                         rt_get_inet6_prefix_plen(rt, &addr, &plen, &scopeid);
  442                         fle6->f.fle_o_ifx = nh->nh_ifp->if_index;
  443                         if (nh->gw_sa.sa_family == AF_INET6)
  444                                 fle6->f.n.next_hop6 = nh->gw6_sa.sin6_addr;
  445                         fle6->f.dst_mask = plen;
  446                 }
  447         }
  448 
  449         if ((flags & NG_NETFLOW_CONF_NOSRCLOOKUP) == 0) {
  450                 /* Do route lookup on source address, to fill in src_mask. */
  451                 struct rtentry *rt;
  452                 struct route_nhop_data rnd;
  453 
  454                 rt = fib6_lookup_rt(r->fib, &fle6->f.r.src.r_src6, 0, NHR_NONE, &rnd);
  455                 if (rt != NULL) {
  456                         struct in6_addr addr;
  457                         uint32_t scopeid;
  458                         int plen;
  459 
  460                         rt_get_inet6_prefix_plen(rt, &addr, &plen, &scopeid);
  461                         fle6->f.src_mask = plen;
  462                 }
  463         }
  464 
  465         /* Push new flow at the and of hash. */
  466         TAILQ_INSERT_TAIL(&hsh6->head, (struct flow_entry *)fle6, fle_hash);
  467 
  468         return (0);
  469 }
  470 #endif
  471 
  472 /*
  473  * Non-static functions called from ng_netflow.c
  474  */
  475 
  476 /* Allocate memory and set up flow cache */
  477 void
  478 ng_netflow_cache_init(priv_p priv)
  479 {
  480         struct flow_hash_entry *hsh;
  481         int i;
  482 
  483         /* Initialize cache UMA zone. */
  484         priv->zone = uma_zcreate("NetFlow IPv4 cache",
  485             sizeof(struct flow_entry), NULL, NULL, NULL, NULL,
  486             UMA_ALIGN_CACHE, 0);
  487         uma_zone_set_max(priv->zone, CACHESIZE);
  488 #ifdef INET6    
  489         priv->zone6 = uma_zcreate("NetFlow IPv6 cache",
  490             sizeof(struct flow6_entry), NULL, NULL, NULL, NULL,
  491             UMA_ALIGN_CACHE, 0);
  492         uma_zone_set_max(priv->zone6, CACHESIZE);
  493 #endif  
  494 
  495         /* Allocate hash. */
  496         priv->hash = malloc(NBUCKETS * sizeof(struct flow_hash_entry),
  497             M_NETFLOW_HASH, M_WAITOK | M_ZERO);
  498 
  499         /* Initialize hash. */
  500         for (i = 0, hsh = priv->hash; i < NBUCKETS; i++, hsh++) {
  501                 mtx_init(&hsh->mtx, "hash mutex", NULL, MTX_DEF);
  502                 TAILQ_INIT(&hsh->head);
  503         }
  504 
  505 #ifdef INET6
  506         /* Allocate hash. */
  507         priv->hash6 = malloc(NBUCKETS * sizeof(struct flow_hash_entry),
  508             M_NETFLOW_HASH, M_WAITOK | M_ZERO);
  509 
  510         /* Initialize hash. */
  511         for (i = 0, hsh = priv->hash6; i < NBUCKETS; i++, hsh++) {
  512                 mtx_init(&hsh->mtx, "hash mutex", NULL, MTX_DEF);
  513                 TAILQ_INIT(&hsh->head);
  514         }
  515 #endif
  516 
  517         priv->nfinfo_bytes = counter_u64_alloc(M_WAITOK);
  518         priv->nfinfo_packets = counter_u64_alloc(M_WAITOK);
  519         priv->nfinfo_bytes6 = counter_u64_alloc(M_WAITOK);
  520         priv->nfinfo_packets6 = counter_u64_alloc(M_WAITOK);
  521         priv->nfinfo_sbytes = counter_u64_alloc(M_WAITOK);
  522         priv->nfinfo_spackets = counter_u64_alloc(M_WAITOK);
  523         priv->nfinfo_sbytes6 = counter_u64_alloc(M_WAITOK);
  524         priv->nfinfo_spackets6 = counter_u64_alloc(M_WAITOK);
  525         priv->nfinfo_act_exp = counter_u64_alloc(M_WAITOK);
  526         priv->nfinfo_inact_exp = counter_u64_alloc(M_WAITOK);
  527 
  528         ng_netflow_v9_cache_init(priv);
  529         CTR0(KTR_NET, "ng_netflow startup()");
  530 }
  531 
  532 /* Initialize new FIB table for v5 and v9 */
  533 int
  534 ng_netflow_fib_init(priv_p priv, int fib)
  535 {
  536         fib_export_p    fe = priv_to_fib(priv, fib);
  537 
  538         CTR1(KTR_NET, "ng_netflow(): fib init: %d", fib);
  539 
  540         if (fe != NULL)
  541                 return (0);
  542 
  543         if ((fe = malloc(sizeof(struct fib_export), M_NETGRAPH,
  544             M_NOWAIT | M_ZERO)) == NULL)
  545                 return (ENOMEM);
  546 
  547         mtx_init(&fe->export_mtx, "export dgram lock", NULL, MTX_DEF);
  548         mtx_init(&fe->export9_mtx, "export9 dgram lock", NULL, MTX_DEF);
  549         fe->fib = fib;
  550         fe->domain_id = fib;
  551 
  552         if (atomic_cmpset_ptr((volatile uintptr_t *)&priv->fib_data[fib],
  553             (uintptr_t)NULL, (uintptr_t)fe) == 0) {
  554                 /* FIB already set up by other ISR */
  555                 CTR3(KTR_NET, "ng_netflow(): fib init: %d setup %p but got %p",
  556                     fib, fe, priv_to_fib(priv, fib));
  557                 mtx_destroy(&fe->export_mtx);
  558                 mtx_destroy(&fe->export9_mtx);
  559                 free(fe, M_NETGRAPH);
  560         } else {
  561                 /* Increase counter for statistics */
  562                 CTR3(KTR_NET, "ng_netflow(): fib %d setup to %p (%p)",
  563                     fib, fe, priv_to_fib(priv, fib));
  564                 priv->nfinfo_alloc_fibs++;
  565         }
  566 
  567         return (0);
  568 }
  569 
  570 /* Free all flow cache memory. Called from node close method. */
  571 void
  572 ng_netflow_cache_flush(priv_p priv)
  573 {
  574         struct flow_entry       *fle, *fle1;
  575         struct flow_hash_entry  *hsh;
  576         struct netflow_export_item exp;
  577         fib_export_p fe;
  578         int i;
  579 
  580         bzero(&exp, sizeof(exp));
  581 
  582         /*
  583          * We are going to free probably billable data.
  584          * Expire everything before freeing it.
  585          * No locking is required since callout is already drained.
  586          */
  587         for (hsh = priv->hash, i = 0; i < NBUCKETS; hsh++, i++)
  588                 TAILQ_FOREACH_SAFE(fle, &hsh->head, fle_hash, fle1) {
  589                         TAILQ_REMOVE(&hsh->head, fle, fle_hash);
  590                         fe = priv_to_fib(priv, fle->f.r.fib);
  591                         expire_flow(priv, fe, fle, NG_QUEUE);
  592                 }
  593 #ifdef INET6
  594         for (hsh = priv->hash6, i = 0; i < NBUCKETS; hsh++, i++)
  595                 TAILQ_FOREACH_SAFE(fle, &hsh->head, fle_hash, fle1) {
  596                         TAILQ_REMOVE(&hsh->head, fle, fle_hash);
  597                         fe = priv_to_fib(priv, fle->f.r.fib);
  598                         expire_flow(priv, fe, fle, NG_QUEUE);
  599                 }
  600 #endif
  601 
  602         uma_zdestroy(priv->zone);
  603         /* Destroy hash mutexes. */
  604         for (i = 0, hsh = priv->hash; i < NBUCKETS; i++, hsh++)
  605                 mtx_destroy(&hsh->mtx);
  606 
  607         /* Free hash memory. */
  608         if (priv->hash != NULL)
  609                 free(priv->hash, M_NETFLOW_HASH);
  610 #ifdef INET6
  611         uma_zdestroy(priv->zone6);
  612         /* Destroy hash mutexes. */
  613         for (i = 0, hsh = priv->hash6; i < NBUCKETS; i++, hsh++)
  614                 mtx_destroy(&hsh->mtx);
  615 
  616         /* Free hash memory. */
  617         if (priv->hash6 != NULL)
  618                 free(priv->hash6, M_NETFLOW_HASH);
  619 #endif
  620 
  621         for (i = 0; i < priv->maxfibs; i++) {
  622                 if ((fe = priv_to_fib(priv, i)) == NULL)
  623                         continue;
  624 
  625                 if (fe->exp.item != NULL)
  626                         export_send(priv, fe, fe->exp.item, NG_QUEUE);
  627 
  628                 if (fe->exp.item9 != NULL)
  629                         export9_send(priv, fe, fe->exp.item9,
  630                             fe->exp.item9_opt, NG_QUEUE);
  631 
  632                 mtx_destroy(&fe->export_mtx);
  633                 mtx_destroy(&fe->export9_mtx);
  634                 free(fe, M_NETGRAPH);
  635         }
  636 
  637         counter_u64_free(priv->nfinfo_bytes);
  638         counter_u64_free(priv->nfinfo_packets);
  639         counter_u64_free(priv->nfinfo_bytes6);
  640         counter_u64_free(priv->nfinfo_packets6);
  641         counter_u64_free(priv->nfinfo_sbytes);
  642         counter_u64_free(priv->nfinfo_spackets);
  643         counter_u64_free(priv->nfinfo_sbytes6);
  644         counter_u64_free(priv->nfinfo_spackets6);
  645         counter_u64_free(priv->nfinfo_act_exp);
  646         counter_u64_free(priv->nfinfo_inact_exp);
  647 
  648         ng_netflow_v9_cache_flush(priv);
  649 }
  650 
  651 #ifdef INET
  652 /* Insert packet from into flow cache. */
  653 int
  654 ng_netflow_flow_add(priv_p priv, fib_export_p fe, struct ip *ip,
  655     caddr_t upper_ptr, uint8_t upper_proto, uint8_t flags,
  656     unsigned int src_if_index)
  657 {
  658         struct flow_entry       *fle, *fle1;
  659         struct flow_hash_entry  *hsh;
  660         struct flow_rec         r;
  661         int                     hlen, plen;
  662         int                     error = 0;
  663         uint8_t                 tcp_flags = 0;
  664 
  665         bzero(&r, sizeof(r));
  666 
  667         if (ip->ip_v != IPVERSION)
  668                 return (EINVAL);
  669 
  670         hlen = ip->ip_hl << 2;
  671         if (hlen < sizeof(struct ip))
  672                 return (EINVAL);
  673 
  674         /* Assume L4 template by default */
  675         r.flow_type = NETFLOW_V9_FLOW_V4_L4;
  676 
  677         r.r_src = ip->ip_src;
  678         r.r_dst = ip->ip_dst;
  679         r.fib = fe->fib;
  680 
  681         plen = ntohs(ip->ip_len);
  682 
  683         r.r_ip_p = ip->ip_p;
  684         r.r_tos = ip->ip_tos;
  685 
  686         r.r_i_ifx = src_if_index;
  687 
  688         /*
  689          * XXX NOTE: only first fragment of fragmented TCP, UDP and
  690          * ICMP packet will be recorded with proper s_port and d_port.
  691          * Following fragments will be recorded simply as IP packet with
  692          * ip_proto = ip->ip_p and s_port, d_port set to zero.
  693          * I know, it looks like bug. But I don't want to re-implement
  694          * ip packet assebmling here. Anyway, (in)famous trafd works this way -
  695          * and nobody complains yet :)
  696          */
  697         if ((ip->ip_off & htons(IP_OFFMASK)) == 0)
  698                 switch(r.r_ip_p) {
  699                 case IPPROTO_TCP:
  700                     {
  701                         struct tcphdr *tcp;
  702 
  703                         tcp = (struct tcphdr *)((caddr_t )ip + hlen);
  704                         r.r_sport = tcp->th_sport;
  705                         r.r_dport = tcp->th_dport;
  706                         tcp_flags = tcp->th_flags;
  707                         break;
  708                     }
  709                 case IPPROTO_UDP:
  710                         r.r_ports = *(uint32_t *)((caddr_t )ip + hlen);
  711                         break;
  712                 }
  713 
  714         counter_u64_add(priv->nfinfo_packets, 1);
  715         counter_u64_add(priv->nfinfo_bytes, plen);
  716 
  717         /* Find hash slot. */
  718         hsh = &priv->hash[ip_hash(&r)];
  719 
  720         mtx_lock(&hsh->mtx);
  721 
  722         /*
  723          * Go through hash and find our entry. If we encounter an
  724          * entry, that should be expired, purge it. We do a reverse
  725          * search since most active entries are first, and most
  726          * searches are done on most active entries.
  727          */
  728         TAILQ_FOREACH_REVERSE_SAFE(fle, &hsh->head, fhead, fle_hash, fle1) {
  729                 if (bcmp(&r, &fle->f.r, sizeof(struct flow_rec)) == 0)
  730                         break;
  731                 if ((INACTIVE(fle) && SMALL(fle)) || AGED(fle)) {
  732                         TAILQ_REMOVE(&hsh->head, fle, fle_hash);
  733                         expire_flow(priv, priv_to_fib(priv, fle->f.r.fib),
  734                             fle, NG_QUEUE);
  735                         counter_u64_add(priv->nfinfo_act_exp, 1);
  736                 }
  737         }
  738 
  739         if (fle) {                      /* An existent entry. */
  740 
  741                 fle->f.bytes += plen;
  742                 fle->f.packets ++;
  743                 fle->f.tcp_flags |= tcp_flags;
  744                 fle->f.last = time_uptime;
  745 
  746                 /*
  747                  * We have the following reasons to expire flow in active way:
  748                  * - it hit active timeout
  749                  * - a TCP connection closed
  750                  * - it is going to overflow counter
  751                  */
  752                 if (tcp_flags & TH_FIN || tcp_flags & TH_RST || AGED(fle) ||
  753                     (fle->f.bytes >= (CNTR_MAX - IF_MAXMTU)) ) {
  754                         TAILQ_REMOVE(&hsh->head, fle, fle_hash);
  755                         expire_flow(priv, priv_to_fib(priv, fle->f.r.fib),
  756                             fle, NG_QUEUE);
  757                         counter_u64_add(priv->nfinfo_act_exp, 1);
  758                 } else {
  759                         /*
  760                          * It is the newest, move it to the tail,
  761                          * if it isn't there already. Next search will
  762                          * locate it quicker.
  763                          */
  764                         if (fle != TAILQ_LAST(&hsh->head, fhead)) {
  765                                 TAILQ_REMOVE(&hsh->head, fle, fle_hash);
  766                                 TAILQ_INSERT_TAIL(&hsh->head, fle, fle_hash);
  767                         }
  768                 }
  769         } else                          /* A new flow entry. */
  770                 error = hash_insert(priv, hsh, &r, plen, flags, tcp_flags);
  771 
  772         mtx_unlock(&hsh->mtx);
  773 
  774         return (error);
  775 }
  776 #endif
  777 
  778 #ifdef INET6
  779 /* Insert IPv6 packet from into flow cache. */
  780 int
  781 ng_netflow_flow6_add(priv_p priv, fib_export_p fe, struct ip6_hdr *ip6,
  782     caddr_t upper_ptr, uint8_t upper_proto, uint8_t flags,
  783     unsigned int src_if_index)
  784 {
  785         struct flow_entry       *fle = NULL, *fle1;
  786         struct flow6_entry      *fle6;
  787         struct flow_hash_entry  *hsh;
  788         struct flow6_rec        r;
  789         int                     plen;
  790         int                     error = 0;
  791         uint8_t                 tcp_flags = 0;
  792 
  793         /* check version */
  794         if ((ip6->ip6_vfc & IPV6_VERSION_MASK) != IPV6_VERSION)
  795                 return (EINVAL);
  796 
  797         bzero(&r, sizeof(r));
  798 
  799         r.src.r_src6 = ip6->ip6_src;
  800         r.dst.r_dst6 = ip6->ip6_dst;
  801         r.fib = fe->fib;
  802 
  803         /* Assume L4 template by default */
  804         r.flow_type = NETFLOW_V9_FLOW_V6_L4;
  805 
  806         plen = ntohs(ip6->ip6_plen) + sizeof(struct ip6_hdr);
  807 
  808 #if 0
  809         /* XXX: set DSCP/CoS value */
  810         r.r_tos = ip->ip_tos;
  811 #endif
  812         if ((flags & NG_NETFLOW_IS_FRAG) == 0) {
  813                 switch(upper_proto) {
  814                 case IPPROTO_TCP:
  815                     {
  816                         struct tcphdr *tcp;
  817 
  818                         tcp = (struct tcphdr *)upper_ptr;
  819                         r.r_ports = *(uint32_t *)upper_ptr;
  820                         tcp_flags = tcp->th_flags;
  821                         break;
  822                     }
  823                 case IPPROTO_UDP:
  824                 case IPPROTO_SCTP:
  825                         r.r_ports = *(uint32_t *)upper_ptr;
  826                         break;
  827                 }
  828         }       
  829 
  830         r.r_ip_p = upper_proto;
  831         r.r_i_ifx = src_if_index;
  832 
  833         counter_u64_add(priv->nfinfo_packets6, 1);
  834         counter_u64_add(priv->nfinfo_bytes6, plen);
  835 
  836         /* Find hash slot. */
  837         hsh = &priv->hash6[ip6_hash(&r)];
  838 
  839         mtx_lock(&hsh->mtx);
  840 
  841         /*
  842          * Go through hash and find our entry. If we encounter an
  843          * entry, that should be expired, purge it. We do a reverse
  844          * search since most active entries are first, and most
  845          * searches are done on most active entries.
  846          */
  847         TAILQ_FOREACH_REVERSE_SAFE(fle, &hsh->head, fhead, fle_hash, fle1) {
  848                 if (fle->f.version != IP6VERSION)
  849                         continue;
  850                 fle6 = (struct flow6_entry *)fle;
  851                 if (bcmp(&r, &fle6->f.r, sizeof(struct flow6_rec)) == 0)
  852                         break;
  853                 if ((INACTIVE(fle6) && SMALL(fle6)) || AGED(fle6)) {
  854                         TAILQ_REMOVE(&hsh->head, fle, fle_hash);
  855                         expire_flow(priv, priv_to_fib(priv, fle->f.r.fib), fle,
  856                             NG_QUEUE);
  857                         counter_u64_add(priv->nfinfo_act_exp, 1);
  858                 }
  859         }
  860 
  861         if (fle != NULL) {                      /* An existent entry. */
  862                 fle6 = (struct flow6_entry *)fle;
  863 
  864                 fle6->f.bytes += plen;
  865                 fle6->f.packets ++;
  866                 fle6->f.tcp_flags |= tcp_flags;
  867                 fle6->f.last = time_uptime;
  868 
  869                 /*
  870                  * We have the following reasons to expire flow in active way:
  871                  * - it hit active timeout
  872                  * - a TCP connection closed
  873                  * - it is going to overflow counter
  874                  */
  875                 if (tcp_flags & TH_FIN || tcp_flags & TH_RST || AGED(fle6) ||
  876                     (fle6->f.bytes >= (CNTR_MAX - IF_MAXMTU)) ) {
  877                         TAILQ_REMOVE(&hsh->head, fle, fle_hash);
  878                         expire_flow(priv, priv_to_fib(priv, fle->f.r.fib), fle,
  879                             NG_QUEUE);
  880                         counter_u64_add(priv->nfinfo_act_exp, 1);
  881                 } else {
  882                         /*
  883                          * It is the newest, move it to the tail,
  884                          * if it isn't there already. Next search will
  885                          * locate it quicker.
  886                          */
  887                         if (fle != TAILQ_LAST(&hsh->head, fhead)) {
  888                                 TAILQ_REMOVE(&hsh->head, fle, fle_hash);
  889                                 TAILQ_INSERT_TAIL(&hsh->head, fle, fle_hash);
  890                         }
  891                 }
  892         } else                          /* A new flow entry. */
  893                 error = hash6_insert(priv, hsh, &r, plen, flags, tcp_flags);
  894 
  895         mtx_unlock(&hsh->mtx);
  896 
  897         return (error);
  898 }
  899 #endif
  900 
  901 /*
  902  * Return records from cache to userland.
  903  *
  904  * TODO: matching particular IP should be done in kernel, here.
  905  */
  906 int
  907 ng_netflow_flow_show(priv_p priv, struct ngnf_show_header *req,
  908 struct ngnf_show_header *resp)
  909 {
  910         struct flow_hash_entry  *hsh;
  911         struct flow_entry       *fle;
  912         struct flow_entry_data  *data = (struct flow_entry_data *)(resp + 1);
  913 #ifdef INET6
  914         struct flow6_entry_data *data6 = (struct flow6_entry_data *)(resp + 1);
  915 #endif
  916         int     i, max;
  917 
  918         i = req->hash_id;
  919         if (i > NBUCKETS-1)
  920                 return (EINVAL);
  921 
  922 #ifdef INET6
  923         if (req->version == 6) {
  924                 resp->version = 6;
  925                 hsh = priv->hash6 + i;
  926                 max = NREC6_AT_ONCE;
  927         } else
  928 #endif
  929         if (req->version == 4) {
  930                 resp->version = 4;
  931                 hsh = priv->hash + i;
  932                 max = NREC_AT_ONCE;
  933         } else
  934                 return (EINVAL);
  935 
  936         /*
  937          * We will transfer not more than NREC_AT_ONCE. More data
  938          * will come in next message.
  939          * We send current hash index and current record number in list 
  940          * to userland, and userland should return it back to us. 
  941          * Then, we will restart with new entry.
  942          *
  943          * The resulting cache snapshot can be inaccurate if flow expiration
  944          * is taking place on hash item between userland data requests for 
  945          * this hash item id.
  946          */
  947         resp->nentries = 0;
  948         for (; i < NBUCKETS; hsh++, i++) {
  949                 int list_id;
  950 
  951                 if (mtx_trylock(&hsh->mtx) == 0) {
  952                         /* 
  953                          * Requested hash index is not available,
  954                          * relay decision to skip or re-request data
  955                          * to userland.
  956                          */
  957                         resp->hash_id = i;
  958                         resp->list_id = 0;
  959                         return (0);
  960                 }
  961 
  962                 list_id = 0;
  963                 TAILQ_FOREACH(fle, &hsh->head, fle_hash) {
  964                         if (hsh->mtx.mtx_lock & MTX_CONTESTED) {
  965                                 resp->hash_id = i;
  966                                 resp->list_id = list_id;
  967                                 mtx_unlock(&hsh->mtx);
  968                                 return (0);
  969                         }
  970 
  971                         list_id++;
  972                         /* Search for particular record in list. */
  973                         if (req->list_id > 0) {
  974                                 if (list_id < req->list_id)
  975                                         continue;
  976 
  977                                 /* Requested list position found. */
  978                                 req->list_id = 0;
  979                         }
  980 #ifdef INET6
  981                         if (req->version == 6) {
  982                                 struct flow6_entry *fle6;
  983 
  984                                 fle6 = (struct flow6_entry *)fle;
  985                                 bcopy(&fle6->f, data6 + resp->nentries,
  986                                     sizeof(fle6->f));
  987                         } else
  988 #endif
  989                                 bcopy(&fle->f, data + resp->nentries,
  990                                     sizeof(fle->f));
  991                         resp->nentries++;
  992                         if (resp->nentries == max) {
  993                                 resp->hash_id = i;
  994                                 /* 
  995                                  * If it was the last item in list
  996                                  * we simply skip to next hash_id.
  997                                  */
  998                                 resp->list_id = list_id + 1;
  999                                 mtx_unlock(&hsh->mtx);
 1000                                 return (0);
 1001                         }
 1002                 }
 1003                 mtx_unlock(&hsh->mtx);
 1004         }
 1005 
 1006         resp->hash_id = resp->list_id = 0;
 1007 
 1008         return (0);
 1009 }
 1010 
 1011 /* We have full datagram in privdata. Send it to export hook. */
 1012 static int
 1013 export_send(priv_p priv, fib_export_p fe, item_p item, int flags)
 1014 {
 1015         struct mbuf *m = NGI_M(item);
 1016         struct netflow_v5_export_dgram *dgram = mtod(m,
 1017                                         struct netflow_v5_export_dgram *);
 1018         struct netflow_v5_header *header = &dgram->header;
 1019         struct timespec ts;
 1020         int error = 0;
 1021 
 1022         /* Fill mbuf header. */
 1023         m->m_len = m->m_pkthdr.len = sizeof(struct netflow_v5_record) *
 1024            header->count + sizeof(struct netflow_v5_header);
 1025 
 1026         /* Fill export header. */
 1027         header->sys_uptime = htonl(MILLIUPTIME(time_uptime));
 1028         getnanotime(&ts);
 1029         header->unix_secs  = htonl(ts.tv_sec);
 1030         header->unix_nsecs = htonl(ts.tv_nsec);
 1031         header->engine_type = 0;
 1032         header->engine_id = fe->domain_id;
 1033         header->pad = 0;
 1034         header->flow_seq = htonl(atomic_fetchadd_32(&fe->flow_seq,
 1035             header->count));
 1036         header->count = htons(header->count);
 1037 
 1038         if (priv->export != NULL)
 1039                 NG_FWD_ITEM_HOOK_FLAGS(error, item, priv->export, flags);
 1040         else
 1041                 NG_FREE_ITEM(item);
 1042 
 1043         return (error);
 1044 }
 1045 
 1046 /* Add export record to dgram. */
 1047 static int
 1048 export_add(item_p item, struct flow_entry *fle)
 1049 {
 1050         struct netflow_v5_export_dgram *dgram = mtod(NGI_M(item),
 1051                                         struct netflow_v5_export_dgram *);
 1052         struct netflow_v5_header *header = &dgram->header;
 1053         struct netflow_v5_record *rec;
 1054 
 1055         rec = &dgram->r[header->count];
 1056         header->count ++;
 1057 
 1058         KASSERT(header->count <= NETFLOW_V5_MAX_RECORDS,
 1059             ("ng_netflow: export too big"));
 1060 
 1061         /* Fill in export record. */
 1062         rec->src_addr = fle->f.r.r_src.s_addr;
 1063         rec->dst_addr = fle->f.r.r_dst.s_addr;
 1064         rec->next_hop = fle->f.next_hop.s_addr;
 1065         rec->i_ifx    = htons(fle->f.fle_i_ifx);
 1066         rec->o_ifx    = htons(fle->f.fle_o_ifx);
 1067         rec->packets  = htonl(fle->f.packets);
 1068         rec->octets   = htonl(fle->f.bytes);
 1069         rec->first    = htonl(MILLIUPTIME(fle->f.first));
 1070         rec->last     = htonl(MILLIUPTIME(fle->f.last));
 1071         rec->s_port   = fle->f.r.r_sport;
 1072         rec->d_port   = fle->f.r.r_dport;
 1073         rec->flags    = fle->f.tcp_flags;
 1074         rec->prot     = fle->f.r.r_ip_p;
 1075         rec->tos      = fle->f.r.r_tos;
 1076         rec->dst_mask = fle->f.dst_mask;
 1077         rec->src_mask = fle->f.src_mask;
 1078         rec->pad1     = 0;
 1079         rec->pad2     = 0;
 1080 
 1081         /* Not supported fields. */
 1082         rec->src_as = rec->dst_as = 0;
 1083 
 1084         if (header->count == NETFLOW_V5_MAX_RECORDS)
 1085                 return (1); /* end of datagram */
 1086         else
 1087                 return (0);     
 1088 }
 1089 
 1090 /* Periodic flow expiry run. */
 1091 void
 1092 ng_netflow_expire(void *arg)
 1093 {
 1094         struct flow_entry       *fle, *fle1;
 1095         struct flow_hash_entry  *hsh;
 1096         priv_p                  priv = (priv_p )arg;
 1097         int                     used, i;
 1098 
 1099         /*
 1100          * Going through all the cache.
 1101          */
 1102         used = uma_zone_get_cur(priv->zone);
 1103         for (hsh = priv->hash, i = 0; i < NBUCKETS; hsh++, i++) {
 1104                 /*
 1105                  * Skip entries, that are already being worked on.
 1106                  */
 1107                 if (mtx_trylock(&hsh->mtx) == 0)
 1108                         continue;
 1109 
 1110                 TAILQ_FOREACH_SAFE(fle, &hsh->head, fle_hash, fle1) {
 1111                         /*
 1112                          * Interrupt thread wants this entry!
 1113                          * Quick! Quick! Bail out!
 1114                          */
 1115                         if (hsh->mtx.mtx_lock & MTX_CONTESTED)
 1116                                 break;
 1117 
 1118                         /*
 1119                          * Don't expire aggressively while hash collision
 1120                          * ratio is predicted small.
 1121                          */
 1122                         if (used <= (NBUCKETS*2) && !INACTIVE(fle))
 1123                                 break;
 1124 
 1125                         if ((INACTIVE(fle) && (SMALL(fle) ||
 1126                             (used > (NBUCKETS*2)))) || AGED(fle)) {
 1127                                 TAILQ_REMOVE(&hsh->head, fle, fle_hash);
 1128                                 expire_flow(priv, priv_to_fib(priv,
 1129                                     fle->f.r.fib), fle, NG_NOFLAGS);
 1130                                 used--;
 1131                                 counter_u64_add(priv->nfinfo_inact_exp, 1);
 1132                         }
 1133                 }
 1134                 mtx_unlock(&hsh->mtx);
 1135         }
 1136 
 1137 #ifdef INET6
 1138         used = uma_zone_get_cur(priv->zone6);
 1139         for (hsh = priv->hash6, i = 0; i < NBUCKETS; hsh++, i++) {
 1140                 struct flow6_entry      *fle6;
 1141 
 1142                 /*
 1143                  * Skip entries, that are already being worked on.
 1144                  */
 1145                 if (mtx_trylock(&hsh->mtx) == 0)
 1146                         continue;
 1147 
 1148                 TAILQ_FOREACH_SAFE(fle, &hsh->head, fle_hash, fle1) {
 1149                         fle6 = (struct flow6_entry *)fle;
 1150                         /*
 1151                          * Interrupt thread wants this entry!
 1152                          * Quick! Quick! Bail out!
 1153                          */
 1154                         if (hsh->mtx.mtx_lock & MTX_CONTESTED)
 1155                                 break;
 1156 
 1157                         /*
 1158                          * Don't expire aggressively while hash collision
 1159                          * ratio is predicted small.
 1160                          */
 1161                         if (used <= (NBUCKETS*2) && !INACTIVE(fle6))
 1162                                 break;
 1163 
 1164                         if ((INACTIVE(fle6) && (SMALL(fle6) ||
 1165                             (used > (NBUCKETS*2)))) || AGED(fle6)) {
 1166                                 TAILQ_REMOVE(&hsh->head, fle, fle_hash);
 1167                                 expire_flow(priv, priv_to_fib(priv,
 1168                                     fle->f.r.fib), fle, NG_NOFLAGS);
 1169                                 used--;
 1170                                 counter_u64_add(priv->nfinfo_inact_exp, 1);
 1171                         }
 1172                 }
 1173                 mtx_unlock(&hsh->mtx);
 1174         }
 1175 #endif
 1176 
 1177         /* Schedule next expire. */
 1178         callout_reset(&priv->exp_callout, (1*hz), &ng_netflow_expire,
 1179             (void *)priv);
 1180 }

Cache object: 19543b408534947ebcad412bbf4b1ab6


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