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  * Copyright (c) 2004-2005 Gleb Smirnoff <glebius@FreeBSD.org>
    3  * Copyright (c) 2001-2003 Roman V. Palagin <romanp@unshadow.net>
    4  * All rights reserved.
    5  *
    6  * Redistribution and use in source and binary forms, with or without
    7  * modification, are permitted provided that the following conditions
    8  * are met:
    9  * 1. Redistributions of source code must retain the above copyright
   10  *    notice, this list of conditions and the following disclaimer.
   11  * 2. Redistributions in binary form must reproduce the above copyright
   12  *    notice, this list of conditions and the following disclaimer in the
   13  *    documentation and/or other materials provided with the distribution.
   14  *
   15  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
   16  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
   17  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
   18  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
   19  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
   20  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
   21  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
   22  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
   23  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
   24  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
   25  * SUCH DAMAGE.
   26  *
   27  * $SourceForge: netflow.c,v 1.41 2004/09/05 11:41:10 glebius Exp $
   28  */
   29 
   30 static const char rcs_id[] =
   31     "@(#) $FreeBSD$";
   32 
   33 #include <sys/param.h>
   34 #include <sys/kernel.h>
   35 #include <sys/limits.h>
   36 #include <sys/mbuf.h>
   37 #include <sys/syslog.h>
   38 #include <sys/systm.h>
   39 #include <sys/socket.h>
   40 
   41 #include <machine/atomic.h>
   42 
   43 #include <net/if.h>
   44 #include <net/route.h>
   45 #include <netinet/in.h>
   46 #include <netinet/in_systm.h>
   47 #include <netinet/ip.h>
   48 #include <netinet/tcp.h>
   49 #include <netinet/udp.h>
   50 
   51 #include <netgraph/ng_message.h>
   52 #include <netgraph/netgraph.h>
   53 
   54 #include <netgraph/netflow/netflow.h>
   55 #include <netgraph/netflow/ng_netflow.h>
   56 
   57 #define NBUCKETS        (65536)         /* must be power of 2 */
   58 
   59 /* This hash is for TCP or UDP packets. */
   60 #define FULL_HASH(addr1, addr2, port1, port2)   \
   61         (((addr1 ^ (addr1 >> 16) ^              \
   62         htons(addr2 ^ (addr2 >> 16))) ^         \
   63         port1 ^ htons(port2)) &                 \
   64         (NBUCKETS - 1))
   65 
   66 /* This hash is for all other IP packets. */
   67 #define ADDR_HASH(addr1, addr2)                 \
   68         ((addr1 ^ (addr1 >> 16) ^               \
   69         htons(addr2 ^ (addr2 >> 16))) &         \
   70         (NBUCKETS - 1))
   71 
   72 /* Macros to shorten logical constructions */
   73 /* XXX: priv must exist in namespace */
   74 #define INACTIVE(fle)   (time_uptime - fle->f.last > priv->info.nfinfo_inact_t)
   75 #define AGED(fle)       (time_uptime - fle->f.first > priv->info.nfinfo_act_t)
   76 #define ISFREE(fle)     (fle->f.packets == 0)
   77 
   78 /*
   79  * 4 is a magical number: statistically number of 4-packet flows is
   80  * bigger than 5,6,7...-packet flows by an order of magnitude. Most UDP/ICMP
   81  * scans are 1 packet (~ 90% of flow cache). TCP scans are 2-packet in case
   82  * of reachable host and 4-packet otherwise.
   83  */
   84 #define SMALL(fle)      (fle->f.packets <= 4)
   85 
   86 /*
   87  * Cisco uses milliseconds for uptime. Bad idea, since it overflows
   88  * every 48+ days. But we will do same to keep compatibility. This macro
   89  * does overflowable multiplication to 1000.
   90  */
   91 #define MILLIUPTIME(t)  (((t) << 9) +   /* 512 */       \
   92                          ((t) << 8) +   /* 256 */       \
   93                          ((t) << 7) +   /* 128 */       \
   94                          ((t) << 6) +   /* 64  */       \
   95                          ((t) << 5) +   /* 32  */       \
   96                          ((t) << 3))    /* 8   */
   97 
   98 MALLOC_DECLARE(M_NETFLOW_HASH);
   99 MALLOC_DEFINE(M_NETFLOW_HASH, "netflow_hash", "NetFlow hash");
  100 
  101 static int export_add(item_p, struct flow_entry *);
  102 static int export_send(priv_p, item_p, int flags);
  103 
  104 /* Generate hash for a given flow record. */
  105 static __inline uint32_t
  106 ip_hash(struct flow_rec *r)
  107 {
  108         switch (r->r_ip_p) {
  109         case IPPROTO_TCP:
  110         case IPPROTO_UDP:
  111                 return FULL_HASH(r->r_src.s_addr, r->r_dst.s_addr,
  112                     r->r_sport, r->r_dport);
  113         default:
  114                 return ADDR_HASH(r->r_src.s_addr, r->r_dst.s_addr);
  115         }
  116 }
  117 
  118 /* This is callback from uma(9), called on alloc. */
  119 static int
  120 uma_ctor_flow(void *mem, int size, void *arg, int how)
  121 {
  122         priv_p priv = (priv_p )arg;
  123 
  124         if (atomic_load_acq_32(&priv->info.nfinfo_used) >= CACHESIZE)
  125                 return (ENOMEM);
  126 
  127         atomic_add_32(&priv->info.nfinfo_used, 1);
  128 
  129         return (0);
  130 }
  131 
  132 /* This is callback from uma(9), called on free. */
  133 static void
  134 uma_dtor_flow(void *mem, int size, void *arg)
  135 {
  136         priv_p priv = (priv_p )arg;
  137 
  138         atomic_subtract_32(&priv->info.nfinfo_used, 1);
  139 }
  140 
  141 /*
  142  * Detach export datagram from priv, if there is any.
  143  * If there is no, allocate a new one.
  144  */
  145 static item_p
  146 get_export_dgram(priv_p priv)
  147 {
  148         item_p  item = NULL;
  149 
  150         mtx_lock(&priv->export_mtx);
  151         if (priv->export_item != NULL) {
  152                 item = priv->export_item;
  153                 priv->export_item = NULL;
  154         }
  155         mtx_unlock(&priv->export_mtx);
  156 
  157         if (item == NULL) {
  158                 struct netflow_v5_export_dgram *dgram;
  159                 struct mbuf *m;
  160 
  161                 m = m_getcl(M_DONTWAIT, MT_DATA, M_PKTHDR);
  162                 if (m == NULL)
  163                         return (NULL);
  164                 item = ng_package_data(m, NG_NOFLAGS);
  165                 if (item == NULL)
  166                         return (NULL);
  167                 dgram = mtod(m, struct netflow_v5_export_dgram *);
  168                 dgram->header.count = 0;
  169                 dgram->header.version = htons(NETFLOW_V5);
  170 
  171         }
  172 
  173         return (item);
  174 }
  175 
  176 /*
  177  * Re-attach incomplete datagram back to priv.
  178  * If there is already another one, then send incomplete. */
  179 static void
  180 return_export_dgram(priv_p priv, item_p item, int flags)
  181 {
  182         /*
  183          * It may happen on SMP, that some thread has already
  184          * put its item there, in this case we bail out and
  185          * send what we have to collector.
  186          */
  187         mtx_lock(&priv->export_mtx);
  188         if (priv->export_item == NULL) {
  189                 priv->export_item = item;
  190                 mtx_unlock(&priv->export_mtx);
  191         } else {
  192                 mtx_unlock(&priv->export_mtx);
  193                 export_send(priv, item, flags);
  194         }
  195 }
  196 
  197 /*
  198  * The flow is over. Call export_add() and free it. If datagram is
  199  * full, then call export_send().
  200  */
  201 static __inline void
  202 expire_flow(priv_p priv, item_p *item, struct flow_entry *fle, int flags)
  203 {
  204         if (*item == NULL)
  205                 *item = get_export_dgram(priv);
  206         if (*item == NULL) {
  207                 atomic_add_32(&priv->info.nfinfo_export_failed, 1);
  208                 uma_zfree_arg(priv->zone, fle, priv);
  209                 return;
  210         }
  211         if (export_add(*item, fle) > 0) {
  212                 export_send(priv, *item, flags);
  213                 *item = NULL;
  214         }
  215         uma_zfree_arg(priv->zone, fle, priv);
  216 }
  217 
  218 /* Get a snapshot of node statistics */
  219 void
  220 ng_netflow_copyinfo(priv_p priv, struct ng_netflow_info *i)
  221 {
  222         /* XXX: atomic */
  223         memcpy((void *)i, (void *)&priv->info, sizeof(priv->info));
  224 }
  225 
  226 /*
  227  * Insert a record into defined slot.
  228  *
  229  * First we get for us a free flow entry, then fill in all
  230  * possible fields in it.
  231  *
  232  * TODO: consider dropping hash mutex while filling in datagram,
  233  * as this was done in previous version. Need to test & profile
  234  * to be sure.
  235  */
  236 static __inline int
  237 hash_insert(priv_p priv, struct flow_hash_entry  *hsh, struct flow_rec *r,
  238         int plen, uint8_t tcp_flags)
  239 {
  240         struct flow_entry       *fle;
  241         struct route ro;
  242         struct sockaddr_in *sin;
  243 
  244         mtx_assert(&hsh->mtx, MA_OWNED);
  245 
  246         fle = uma_zalloc_arg(priv->zone, priv, M_NOWAIT);
  247         if (fle == NULL) {
  248                 atomic_add_32(&priv->info.nfinfo_alloc_failed, 1);
  249                 return (ENOMEM);
  250         }
  251 
  252         /*
  253          * Now fle is totally ours. It is detached from all lists,
  254          * we can safely edit it.
  255          */
  256 
  257         bcopy(r, &fle->f.r, sizeof(struct flow_rec));
  258         fle->f.bytes = plen;
  259         fle->f.packets = 1;
  260         fle->f.tcp_flags = tcp_flags;
  261 
  262         fle->f.first = fle->f.last = time_uptime;
  263 
  264         /*
  265          * First we do route table lookup on destination address. So we can
  266          * fill in out_ifx, dst_mask, nexthop, and dst_as in future releases.
  267          */
  268         bzero((caddr_t)&ro, sizeof(ro));
  269         sin = (struct sockaddr_in *)&ro.ro_dst;
  270         sin->sin_len = sizeof(*sin);
  271         sin->sin_family = AF_INET;
  272         sin->sin_addr = fle->f.r.r_dst;
  273         /* XXX MRT 0 as a default.. need the m here to get fib */
  274         rtalloc_ign_fib(&ro, RTF_CLONING, 0);
  275         if (ro.ro_rt != NULL) {
  276                 struct rtentry *rt = ro.ro_rt;
  277 
  278                 fle->f.fle_o_ifx = rt->rt_ifp->if_index;
  279 
  280                 if (rt->rt_flags & RTF_GATEWAY &&
  281                     rt->rt_gateway->sa_family == AF_INET)
  282                         fle->f.next_hop =
  283                             ((struct sockaddr_in *)(rt->rt_gateway))->sin_addr;
  284 
  285                 if (rt_mask(rt))
  286                         fle->f.dst_mask = bitcount32(((struct sockaddr_in *)
  287                             rt_mask(rt))->sin_addr.s_addr);
  288                 else if (rt->rt_flags & RTF_HOST)
  289                         /* Give up. We can't determine mask :( */
  290                         fle->f.dst_mask = 32;
  291 
  292                 RTFREE(ro.ro_rt);
  293         }
  294 
  295         /* Do route lookup on source address, to fill in src_mask. */
  296 
  297         bzero((caddr_t)&ro, sizeof(ro));
  298         sin = (struct sockaddr_in *)&ro.ro_dst;
  299         sin->sin_len = sizeof(*sin);
  300         sin->sin_family = AF_INET;
  301         sin->sin_addr = fle->f.r.r_src;
  302         rtalloc_ign_fib(&ro, RTF_CLONING, 0); /* XXX MRT */
  303         if (ro.ro_rt != NULL) {
  304                 struct rtentry *rt = ro.ro_rt;
  305 
  306                 if (rt_mask(rt))
  307                         fle->f.src_mask = bitcount32(((struct sockaddr_in *)
  308                             rt_mask(rt))->sin_addr.s_addr);
  309                 else if (rt->rt_flags & RTF_HOST)
  310                         /* Give up. We can't determine mask :( */
  311                         fle->f.src_mask = 32;
  312 
  313                 RTFREE(ro.ro_rt);
  314         }
  315 
  316         /* Push new flow at the and of hash. */
  317         TAILQ_INSERT_TAIL(&hsh->head, fle, fle_hash);
  318 
  319         return (0);
  320 }
  321 
  322 
  323 /*
  324  * Non-static functions called from ng_netflow.c
  325  */
  326 
  327 /* Allocate memory and set up flow cache */
  328 int
  329 ng_netflow_cache_init(priv_p priv)
  330 {
  331         struct flow_hash_entry  *hsh;
  332         int i;
  333 
  334         /* Initialize cache UMA zone. */
  335         priv->zone = uma_zcreate("NetFlow cache", sizeof(struct flow_entry),
  336             uma_ctor_flow, uma_dtor_flow, NULL, NULL, UMA_ALIGN_CACHE, 0);
  337         uma_zone_set_max(priv->zone, CACHESIZE);
  338 
  339         /* Allocate hash. */
  340         MALLOC(priv->hash, struct flow_hash_entry *,
  341             NBUCKETS * sizeof(struct flow_hash_entry),
  342             M_NETFLOW_HASH, M_WAITOK | M_ZERO);
  343 
  344         if (priv->hash == NULL) {
  345                 uma_zdestroy(priv->zone);
  346                 return (ENOMEM);
  347         }
  348 
  349         /* Initialize hash. */
  350         for (i = 0, hsh = priv->hash; i < NBUCKETS; i++, hsh++) {
  351                 mtx_init(&hsh->mtx, "hash mutex", NULL, MTX_DEF);
  352                 TAILQ_INIT(&hsh->head);
  353         }
  354 
  355         mtx_init(&priv->export_mtx, "export dgram lock", NULL, MTX_DEF);
  356 
  357         return (0);
  358 }
  359 
  360 /* Free all flow cache memory. Called from node close method. */
  361 void
  362 ng_netflow_cache_flush(priv_p priv)
  363 {
  364         struct flow_entry       *fle, *fle1;
  365         struct flow_hash_entry  *hsh;
  366         item_p                  item = NULL;
  367         int i;
  368 
  369         /*
  370          * We are going to free probably billable data.
  371          * Expire everything before freeing it.
  372          * No locking is required since callout is already drained.
  373          */
  374         for (hsh = priv->hash, i = 0; i < NBUCKETS; hsh++, i++)
  375                 TAILQ_FOREACH_SAFE(fle, &hsh->head, fle_hash, fle1) {
  376                         TAILQ_REMOVE(&hsh->head, fle, fle_hash);
  377                         expire_flow(priv, &item, fle, NG_QUEUE);
  378                 }
  379 
  380         if (item != NULL)
  381                 export_send(priv, item, NG_QUEUE);
  382 
  383         uma_zdestroy(priv->zone);
  384 
  385         /* Destroy hash mutexes. */
  386         for (i = 0, hsh = priv->hash; i < NBUCKETS; i++, hsh++)
  387                 mtx_destroy(&hsh->mtx);
  388 
  389         /* Free hash memory. */
  390         if (priv->hash)
  391                 FREE(priv->hash, M_NETFLOW_HASH);
  392 
  393         mtx_destroy(&priv->export_mtx);
  394 }
  395 
  396 /* Insert packet from into flow cache. */
  397 int
  398 ng_netflow_flow_add(priv_p priv, struct ip *ip, unsigned int src_if_index)
  399 {
  400         register struct flow_entry      *fle, *fle1;
  401         struct flow_hash_entry          *hsh;
  402         struct flow_rec         r;
  403         item_p                  item = NULL;
  404         int                     hlen, plen;
  405         int                     error = 0;
  406         uint8_t                 tcp_flags = 0;
  407 
  408         /* Try to fill flow_rec r */
  409         bzero(&r, sizeof(r));
  410         /* check version */
  411         if (ip->ip_v != IPVERSION)
  412                 return (EINVAL);
  413 
  414         /* verify min header length */
  415         hlen = ip->ip_hl << 2;
  416 
  417         if (hlen < sizeof(struct ip))
  418                 return (EINVAL);
  419 
  420         r.r_src = ip->ip_src;
  421         r.r_dst = ip->ip_dst;
  422 
  423         /* save packet length */
  424         plen = ntohs(ip->ip_len);
  425 
  426         r.r_ip_p = ip->ip_p;
  427         r.r_tos = ip->ip_tos;
  428 
  429         r.r_i_ifx = src_if_index;
  430 
  431         /*
  432          * XXX NOTE: only first fragment of fragmented TCP, UDP and
  433          * ICMP packet will be recorded with proper s_port and d_port.
  434          * Following fragments will be recorded simply as IP packet with
  435          * ip_proto = ip->ip_p and s_port, d_port set to zero.
  436          * I know, it looks like bug. But I don't want to re-implement
  437          * ip packet assebmling here. Anyway, (in)famous trafd works this way -
  438          * and nobody complains yet :)
  439          */
  440         if ((ip->ip_off & htons(IP_OFFMASK)) == 0)
  441                 switch(r.r_ip_p) {
  442                 case IPPROTO_TCP:
  443                 {
  444                         register struct tcphdr *tcp;
  445 
  446                         tcp = (struct tcphdr *)((caddr_t )ip + hlen);
  447                         r.r_sport = tcp->th_sport;
  448                         r.r_dport = tcp->th_dport;
  449                         tcp_flags = tcp->th_flags;
  450                         break;
  451                 }
  452                         case IPPROTO_UDP:
  453                         r.r_ports = *(uint32_t *)((caddr_t )ip + hlen);
  454                         break;
  455                 }
  456 
  457         /* Update node statistics. XXX: race... */
  458         priv->info.nfinfo_packets ++;
  459         priv->info.nfinfo_bytes += plen;
  460 
  461         /* Find hash slot. */
  462         hsh = &priv->hash[ip_hash(&r)];
  463 
  464         mtx_lock(&hsh->mtx);
  465 
  466         /*
  467          * Go through hash and find our entry. If we encounter an
  468          * entry, that should be expired, purge it. We do a reverse
  469          * search since most active entries are first, and most
  470          * searches are done on most active entries.
  471          */
  472         TAILQ_FOREACH_REVERSE_SAFE(fle, &hsh->head, fhead, fle_hash, fle1) {
  473                 if (bcmp(&r, &fle->f.r, sizeof(struct flow_rec)) == 0)
  474                         break;
  475                 if ((INACTIVE(fle) && SMALL(fle)) || AGED(fle)) {
  476                         TAILQ_REMOVE(&hsh->head, fle, fle_hash);
  477                         expire_flow(priv, &item, fle, NG_QUEUE);
  478                         atomic_add_32(&priv->info.nfinfo_act_exp, 1);
  479                 }
  480         }
  481 
  482         if (fle) {                      /* An existent entry. */
  483 
  484                 fle->f.bytes += plen;
  485                 fle->f.packets ++;
  486                 fle->f.tcp_flags |= tcp_flags;
  487                 fle->f.last = time_uptime;
  488 
  489                 /*
  490                  * We have the following reasons to expire flow in active way:
  491                  * - it hit active timeout
  492                  * - a TCP connection closed
  493                  * - it is going to overflow counter
  494                  */
  495                 if (tcp_flags & TH_FIN || tcp_flags & TH_RST || AGED(fle) ||
  496                     (fle->f.bytes >= (UINT_MAX - IF_MAXMTU)) ) {
  497                         TAILQ_REMOVE(&hsh->head, fle, fle_hash);
  498                         expire_flow(priv, &item, fle, NG_QUEUE);
  499                         atomic_add_32(&priv->info.nfinfo_act_exp, 1);
  500                 } else {
  501                         /*
  502                          * It is the newest, move it to the tail,
  503                          * if it isn't there already. Next search will
  504                          * locate it quicker.
  505                          */
  506                         if (fle != TAILQ_LAST(&hsh->head, fhead)) {
  507                                 TAILQ_REMOVE(&hsh->head, fle, fle_hash);
  508                                 TAILQ_INSERT_TAIL(&hsh->head, fle, fle_hash);
  509                         }
  510                 }
  511         } else                          /* A new flow entry. */
  512                 error = hash_insert(priv, hsh, &r, plen, tcp_flags);
  513 
  514         mtx_unlock(&hsh->mtx);
  515 
  516         if (item != NULL)
  517                 return_export_dgram(priv, item, NG_QUEUE);
  518 
  519         return (error);
  520 }
  521 
  522 /*
  523  * Return records from cache to userland.
  524  *
  525  * TODO: matching particular IP should be done in kernel, here.
  526  */
  527 int
  528 ng_netflow_flow_show(priv_p priv, uint32_t last, struct ng_mesg *resp)
  529 {
  530         struct flow_hash_entry *hsh;
  531         struct flow_entry *fle;
  532         struct ngnf_flows *data;
  533         int i;
  534 
  535         data = (struct ngnf_flows *)resp->data;
  536         data->last = 0;
  537         data->nentries = 0;
  538 
  539         /* Check if this is a first run */
  540         if (last == 0) {
  541                 hsh = priv->hash;
  542                 i = 0;
  543         } else {
  544                 if (last > NBUCKETS-1)
  545                         return (EINVAL);
  546                 hsh = priv->hash + last;
  547                 i = last;
  548         }
  549 
  550         /*
  551          * We will transfer not more than NREC_AT_ONCE. More data
  552          * will come in next message.
  553          * We send current hash index to userland, and userland should
  554          * return it back to us. Then, we will restart with new entry.
  555          *
  556          * The resulting cache snapshot is inaccurate for the
  557          * following reasons:
  558          *  - we skip locked hash entries
  559          *  - we bail out, if someone wants our entry
  560          *  - we skip rest of entry, when hit NREC_AT_ONCE
  561          */
  562         for (; i < NBUCKETS; hsh++, i++) {
  563                 if (mtx_trylock(&hsh->mtx) == 0)
  564                         continue;
  565 
  566                 TAILQ_FOREACH(fle, &hsh->head, fle_hash) {
  567                         if (hsh->mtx.mtx_lock & MTX_CONTESTED)
  568                                 break;
  569 
  570                         bcopy(&fle->f, &(data->entries[data->nentries]),
  571                             sizeof(fle->f));
  572                         data->nentries++;
  573                         if (data->nentries == NREC_AT_ONCE) {
  574                                 mtx_unlock(&hsh->mtx);
  575                                 if (++i < NBUCKETS)
  576                                         data->last = i;
  577                                 return (0);
  578                         }
  579                 }
  580                 mtx_unlock(&hsh->mtx);
  581         }
  582 
  583         return (0);
  584 }
  585 
  586 /* We have full datagram in privdata. Send it to export hook. */
  587 static int
  588 export_send(priv_p priv, item_p item, int flags)
  589 {
  590         struct mbuf *m = NGI_M(item);
  591         struct netflow_v5_export_dgram *dgram = mtod(m,
  592                                         struct netflow_v5_export_dgram *);
  593         struct netflow_v5_header *header = &dgram->header;
  594         struct timespec ts;
  595         int error = 0;
  596 
  597         /* Fill mbuf header. */
  598         m->m_len = m->m_pkthdr.len = sizeof(struct netflow_v5_record) *
  599            header->count + sizeof(struct netflow_v5_header);
  600 
  601         /* Fill export header. */
  602         header->sys_uptime = htonl(MILLIUPTIME(time_uptime));
  603         getnanotime(&ts);
  604         header->unix_secs  = htonl(ts.tv_sec);
  605         header->unix_nsecs = htonl(ts.tv_nsec);
  606         header->engine_type = 0;
  607         header->engine_id = 0;
  608         header->pad = 0;
  609         header->flow_seq = htonl(atomic_fetchadd_32(&priv->flow_seq,
  610             header->count));
  611         header->count = htons(header->count);
  612 
  613         if (priv->export != NULL)
  614                 NG_FWD_ITEM_HOOK_FLAGS(error, item, priv->export, flags);
  615         else
  616                 NG_FREE_ITEM(item);
  617 
  618         return (error);
  619 }
  620 
  621 
  622 /* Add export record to dgram. */
  623 static int
  624 export_add(item_p item, struct flow_entry *fle)
  625 {
  626         struct netflow_v5_export_dgram *dgram = mtod(NGI_M(item),
  627                                         struct netflow_v5_export_dgram *);
  628         struct netflow_v5_header *header = &dgram->header;
  629         struct netflow_v5_record *rec;
  630 
  631         rec = &dgram->r[header->count];
  632         header->count ++;
  633 
  634         KASSERT(header->count <= NETFLOW_V5_MAX_RECORDS,
  635             ("ng_netflow: export too big"));
  636 
  637         /* Fill in export record. */
  638         rec->src_addr = fle->f.r.r_src.s_addr;
  639         rec->dst_addr = fle->f.r.r_dst.s_addr;
  640         rec->next_hop = fle->f.next_hop.s_addr;
  641         rec->i_ifx    = htons(fle->f.fle_i_ifx);
  642         rec->o_ifx    = htons(fle->f.fle_o_ifx);
  643         rec->packets  = htonl(fle->f.packets);
  644         rec->octets   = htonl(fle->f.bytes);
  645         rec->first    = htonl(MILLIUPTIME(fle->f.first));
  646         rec->last     = htonl(MILLIUPTIME(fle->f.last));
  647         rec->s_port   = fle->f.r.r_sport;
  648         rec->d_port   = fle->f.r.r_dport;
  649         rec->flags    = fle->f.tcp_flags;
  650         rec->prot     = fle->f.r.r_ip_p;
  651         rec->tos      = fle->f.r.r_tos;
  652         rec->dst_mask = fle->f.dst_mask;
  653         rec->src_mask = fle->f.src_mask;
  654 
  655         /* Not supported fields. */
  656         rec->src_as = rec->dst_as = 0;
  657 
  658         if (header->count == NETFLOW_V5_MAX_RECORDS)
  659                 return (1); /* end of datagram */
  660         else
  661                 return (0);     
  662 }
  663 
  664 /* Periodic flow expiry run. */
  665 void
  666 ng_netflow_expire(void *arg)
  667 {
  668         struct flow_entry       *fle, *fle1;
  669         struct flow_hash_entry  *hsh;
  670         priv_p                  priv = (priv_p )arg;
  671         item_p                  item = NULL;
  672         uint32_t                used;
  673         int                     i;
  674 
  675         /*
  676          * Going through all the cache.
  677          */
  678         for (hsh = priv->hash, i = 0; i < NBUCKETS; hsh++, i++) {
  679                 /*
  680                  * Skip entries, that are already being worked on.
  681                  */
  682                 if (mtx_trylock(&hsh->mtx) == 0)
  683                         continue;
  684 
  685                 used = atomic_load_acq_32(&priv->info.nfinfo_used);
  686                 TAILQ_FOREACH_SAFE(fle, &hsh->head, fle_hash, fle1) {
  687                         /*
  688                          * Interrupt thread wants this entry!
  689                          * Quick! Quick! Bail out!
  690                          */
  691                         if (hsh->mtx.mtx_lock & MTX_CONTESTED)
  692                                 break;
  693 
  694                         /*
  695                          * Don't expire aggressively while hash collision
  696                          * ratio is predicted small.
  697                          */
  698                         if (used <= (NBUCKETS*2) && !INACTIVE(fle))
  699                                 break;
  700 
  701                         if ((INACTIVE(fle) && (SMALL(fle) ||
  702                             (used > (NBUCKETS*2)))) || AGED(fle)) {
  703                                 TAILQ_REMOVE(&hsh->head, fle, fle_hash);
  704                                 expire_flow(priv, &item, fle, NG_NOFLAGS);
  705                                 used--;
  706                                 atomic_add_32(&priv->info.nfinfo_inact_exp, 1);
  707                         }
  708                 }
  709                 mtx_unlock(&hsh->mtx);
  710         }
  711 
  712         if (item != NULL)
  713                 return_export_dgram(priv, item, NG_NOFLAGS);
  714 
  715         /* Schedule next expire. */
  716         callout_reset(&priv->exp_callout, (1*hz), &ng_netflow_expire,
  717             (void *)priv);
  718 }

Cache object: 94d535385a0c7b9d22085338fc0b2293


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