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_v9.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) 2010 Alexander V. Chernikov <melifaro@ipfw.ru>
    3  * All rights reserved.
    4  *
    5  * Redistribution and use in source and binary forms, with or without
    6  * modification, are permitted provided that the following conditions
    7  * are met:
    8  * 1. Redistributions of source code must retain the above copyright
    9  *    notice, this list of conditions and the following disclaimer.
   10  * 2. Redistributions in binary form must reproduce the above copyright
   11  *    notice, this list of conditions and the following disclaimer in the
   12  *    documentation and/or other materials provided with the distribution.
   13  *
   14  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
   15  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
   16  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
   17  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
   18  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
   19  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
   20  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
   21  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
   22  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
   23  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
   24  * SUCH DAMAGE.
   25  *
   26  *      $FreeBSD: releng/10.2/sys/netgraph/netflow/netflow_v9.c 260278 2014-01-04 19:04:53Z dim $
   27  */
   28 
   29 #include <sys/cdefs.h>
   30 __FBSDID("$FreeBSD: releng/10.2/sys/netgraph/netflow/netflow_v9.c 260278 2014-01-04 19:04:53Z dim $");
   31 
   32 #include "opt_inet6.h"
   33 #include "opt_route.h"
   34 #include <sys/param.h>
   35 #include <sys/kernel.h>
   36 #include <sys/limits.h>
   37 #include <sys/mbuf.h>
   38 #include <sys/syslog.h>
   39 #include <sys/systm.h>
   40 #include <sys/socket.h>
   41 #include <sys/endian.h>
   42 
   43 #include <machine/atomic.h>
   44 #include <machine/stdarg.h>
   45 
   46 #include <net/if.h>
   47 #include <net/route.h>
   48 #include <net/ethernet.h>
   49 #include <netinet/in.h>
   50 #include <netinet/in_systm.h>
   51 #include <netinet/ip.h>
   52 #include <netinet/ip6.h>
   53 #include <netinet/tcp.h>
   54 #include <netinet/udp.h>
   55 
   56 #include <netgraph/ng_message.h>
   57 #include <netgraph/netgraph.h>
   58 
   59 #include <netgraph/netflow/netflow.h>
   60 #include <netgraph/netflow/ng_netflow.h>
   61 #include <netgraph/netflow/netflow_v9.h>
   62 
   63 MALLOC_DECLARE(M_NETFLOW_GENERAL);
   64 MALLOC_DEFINE(M_NETFLOW_GENERAL, "netflow_general", "plog, V9 templates data");
   65 
   66 /*
   67  * Base V9 templates for L4+ IPv4/IPv6 protocols
   68  */
   69 struct netflow_v9_template _netflow_v9_record_ipv4_tcp[] =
   70 {
   71         { NETFLOW_V9_FIELD_IPV4_SRC_ADDR, 4},
   72         { NETFLOW_V9_FIELD_IPV4_DST_ADDR, 4},
   73         { NETFLOW_V9_FIELD_IPV4_NEXT_HOP, 4},
   74         { NETFLOW_V9_FIELD_INPUT_SNMP, 2},
   75         { NETFLOW_V9_FIELD_OUTPUT_SNMP, 2},
   76         { NETFLOW_V9_FIELD_IN_PKTS, sizeof(CNTR)},
   77         { NETFLOW_V9_FIELD_IN_BYTES, sizeof(CNTR)},
   78         { NETFLOW_V9_FIELD_OUT_PKTS, sizeof(CNTR)},
   79         { NETFLOW_V9_FIELD_OUT_BYTES, sizeof(CNTR)},
   80         { NETFLOW_V9_FIELD_FIRST_SWITCHED, 4},
   81         { NETFLOW_V9_FIELD_LAST_SWITCHED, 4},
   82         { NETFLOW_V9_FIELD_L4_SRC_PORT, 2},
   83         { NETFLOW_V9_FIELD_L4_DST_PORT, 2},
   84         { NETFLOW_V9_FIELD_TCP_FLAGS, 1},
   85         { NETFLOW_V9_FIELD_PROTOCOL, 1},
   86         { NETFLOW_V9_FIELD_TOS, 1},
   87         { NETFLOW_V9_FIELD_SRC_AS, 4},
   88         { NETFLOW_V9_FIELD_DST_AS, 4},
   89         { NETFLOW_V9_FIELD_SRC_MASK, 1},
   90         { NETFLOW_V9_FIELD_DST_MASK, 1},
   91         {0, 0}
   92 };
   93 
   94 struct netflow_v9_template _netflow_v9_record_ipv6_tcp[] =
   95 {
   96         { NETFLOW_V9_FIELD_IPV6_SRC_ADDR, 16},
   97         { NETFLOW_V9_FIELD_IPV6_DST_ADDR, 16},
   98         { NETFLOW_V9_FIELD_IPV6_NEXT_HOP, 16},
   99         { NETFLOW_V9_FIELD_INPUT_SNMP, 2},
  100         { NETFLOW_V9_FIELD_OUTPUT_SNMP, 2},
  101         { NETFLOW_V9_FIELD_IN_PKTS, sizeof(CNTR)},
  102         { NETFLOW_V9_FIELD_IN_BYTES, sizeof(CNTR)},
  103         { NETFLOW_V9_FIELD_OUT_PKTS, sizeof(CNTR)},
  104         { NETFLOW_V9_FIELD_OUT_BYTES, sizeof(CNTR)},
  105         { NETFLOW_V9_FIELD_FIRST_SWITCHED, 4},
  106         { NETFLOW_V9_FIELD_LAST_SWITCHED, 4},
  107         { NETFLOW_V9_FIELD_L4_SRC_PORT, 2},
  108         { NETFLOW_V9_FIELD_L4_DST_PORT, 2},
  109         { NETFLOW_V9_FIELD_TCP_FLAGS, 1},
  110         { NETFLOW_V9_FIELD_PROTOCOL, 1},
  111         { NETFLOW_V9_FIELD_TOS, 1},
  112         { NETFLOW_V9_FIELD_SRC_AS, 4},
  113         { NETFLOW_V9_FIELD_DST_AS, 4},
  114         { NETFLOW_V9_FIELD_SRC_MASK, 1},
  115         { NETFLOW_V9_FIELD_DST_MASK, 1},
  116         {0, 0}
  117 };
  118 
  119 /*
  120  * Pre-compiles flow exporter for all possible FlowSets
  121  * so we can add flowset to packet via simple memcpy()
  122  */
  123 static void
  124 generate_v9_templates(priv_p priv)
  125 {
  126         uint16_t *p, *template_fields_cnt;
  127         int cnt;
  128 
  129         int flowset_size = sizeof(struct netflow_v9_flowset_header) +
  130                 _NETFLOW_V9_TEMPLATE_SIZE(_netflow_v9_record_ipv4_tcp) + /* netflow_v9_record_ipv4_tcp */
  131                 _NETFLOW_V9_TEMPLATE_SIZE(_netflow_v9_record_ipv6_tcp); /* netflow_v9_record_ipv6_tcp */
  132 
  133         priv->v9_flowsets[0] = malloc(flowset_size, M_NETFLOW_GENERAL, M_WAITOK | M_ZERO);
  134 
  135         if (flowset_size % 4)
  136                 flowset_size += 4 - (flowset_size % 4); /* Padding to 4-byte boundary */
  137 
  138         priv->flowsets_count = 1;
  139         p = (uint16_t *)priv->v9_flowsets[0];
  140         *p++ = 0; /* Flowset ID, 0 is reserved for Template FlowSets  */
  141         *p++ = htons(flowset_size); /* Total FlowSet length */
  142 
  143         /*
  144          * Most common TCP/UDP IPv4 template, ID = 256
  145          */
  146         *p++ = htons(NETFLOW_V9_MAX_RESERVED_FLOWSET + NETFLOW_V9_FLOW_V4_L4);
  147         template_fields_cnt = p++;
  148         for (cnt = 0; _netflow_v9_record_ipv4_tcp[cnt].field_id != 0; cnt++) {
  149                 *p++ = htons(_netflow_v9_record_ipv4_tcp[cnt].field_id);
  150                 *p++ = htons(_netflow_v9_record_ipv4_tcp[cnt].field_length);
  151         }
  152         *template_fields_cnt = htons(cnt);
  153 
  154         /*
  155          * TCP/UDP IPv6 template, ID = 257
  156          */
  157         *p++ = htons(NETFLOW_V9_MAX_RESERVED_FLOWSET + NETFLOW_V9_FLOW_V6_L4);
  158         template_fields_cnt = p++;
  159         for (cnt = 0; _netflow_v9_record_ipv6_tcp[cnt].field_id != 0; cnt++) {
  160                 *p++ = htons(_netflow_v9_record_ipv6_tcp[cnt].field_id);
  161                 *p++ = htons(_netflow_v9_record_ipv6_tcp[cnt].field_length);
  162         }
  163         *template_fields_cnt = htons(cnt);
  164 
  165         priv->flowset_records[0] = 2;
  166 }
  167 
  168 /* Closes current data flowset */
  169 static void inline
  170 close_flowset(struct mbuf *m, struct netflow_v9_packet_opt *t)
  171 {
  172         struct mbuf *m_old;
  173         uint32_t zero = 0;
  174         int offset = 0;
  175         uint16_t *flowset_length, len;
  176 
  177         /* Hack to ensure we are not crossing mbuf boundary, length is uint16_t  */
  178         m_old = m_getptr(m, t->flow_header + offsetof(struct netflow_v9_flowset_header, length), &offset);
  179         flowset_length = (uint16_t *)(mtod(m_old, char *) + offset);
  180 
  181         len = (uint16_t)(m_pktlen(m) - t->flow_header);
  182         /* Align on 4-byte boundary (RFC 3954, Clause 5.3) */
  183         if (len % 4) {
  184                 if (m_append(m, 4 - (len % 4), (void *)&zero) != 1)
  185                         panic("ng_netflow: m_append() failed!");
  186 
  187                 len += 4 - (len % 4);
  188         }
  189 
  190         *flowset_length = htons(len);
  191 }
  192 
  193 /*
  194  * Non-static functions called from ng_netflow.c
  195  */
  196 
  197 /* We have full datagram in fib data. Send it to export hook. */
  198 int
  199 export9_send(priv_p priv, fib_export_p fe, item_p item, struct netflow_v9_packet_opt *t, int flags)
  200 {
  201         struct mbuf *m = NGI_M(item);
  202         struct netflow_v9_export_dgram *dgram = mtod(m,
  203                                         struct netflow_v9_export_dgram *);
  204         struct netflow_v9_header *header = &dgram->header;
  205         struct timespec ts;
  206         int error = 0;
  207 
  208         if (t == NULL) {
  209                 CTR0(KTR_NET, "export9_send(): V9 export packet without tag");
  210                 NG_FREE_ITEM(item);
  211                 return (0);
  212         }
  213 
  214         /* Close flowset if not closed already */
  215         if (m_pktlen(m) != t->flow_header)
  216                 close_flowset(m, t);
  217 
  218         /* Fill export header. */
  219         header->count = t->count;
  220         header->sys_uptime = htonl(MILLIUPTIME(time_uptime));
  221         getnanotime(&ts);
  222         header->unix_secs  = htonl(ts.tv_sec);
  223         header->seq_num = htonl(atomic_fetchadd_32(&fe->flow9_seq, 1));
  224         header->count = htons(t->count);
  225         header->source_id = htonl(fe->domain_id);
  226 
  227         if (priv->export9 != NULL)
  228                 NG_FWD_ITEM_HOOK_FLAGS(error, item, priv->export9, flags);
  229         else
  230                 NG_FREE_ITEM(item);
  231 
  232         free(t, M_NETFLOW_GENERAL);
  233 
  234         return (error);
  235 }
  236 
  237 
  238 
  239 /* Add V9 record to dgram. */
  240 int
  241 export9_add(item_p item, struct netflow_v9_packet_opt *t, struct flow_entry *fle)
  242 {
  243         size_t len = 0;
  244         struct netflow_v9_flowset_header fsh;
  245         struct netflow_v9_record_general rg;
  246         struct mbuf *m = NGI_M(item);
  247         uint16_t flow_type;
  248         struct flow_entry_data *fed;
  249 #ifdef INET6    
  250         struct flow6_entry_data *fed6;
  251 #endif
  252         if (t == NULL) {
  253                 CTR0(KTR_NET, "ng_netflow: V9 export packet without tag!");
  254                 return (0);
  255         }
  256 
  257         /* Prepare flow record */
  258         fed = (struct flow_entry_data *)&fle->f;
  259 #ifdef INET6
  260         fed6 = (struct flow6_entry_data *)&fle->f;
  261 #endif
  262         /* We can use flow_type field since fle6 offset is equal to fle */
  263         flow_type = fed->r.flow_type;
  264 
  265         switch (flow_type) {
  266         case NETFLOW_V9_FLOW_V4_L4:
  267         {
  268                 /* IPv4 TCP/UDP/[SCTP] */
  269                 struct netflow_v9_record_ipv4_tcp *rec = &rg.rec.v4_tcp;
  270                 
  271                 rec->src_addr = fed->r.r_src.s_addr;
  272                 rec->dst_addr = fed->r.r_dst.s_addr;
  273                 rec->next_hop = fed->next_hop.s_addr;
  274                 rec->i_ifx    = htons(fed->fle_i_ifx);
  275                 rec->o_ifx    = htons(fed->fle_o_ifx);
  276                 rec->i_packets  = htonl(fed->packets);
  277                 rec->i_octets   = htonl(fed->bytes);
  278                 rec->o_packets  = htonl(0);
  279                 rec->o_octets   = htonl(0);
  280                 rec->first    = htonl(MILLIUPTIME(fed->first));
  281                 rec->last     = htonl(MILLIUPTIME(fed->last));
  282                 rec->s_port   = fed->r.r_sport;
  283                 rec->d_port   = fed->r.r_dport;
  284                 rec->flags    = fed->tcp_flags;
  285                 rec->prot     = fed->r.r_ip_p;
  286                 rec->tos      = fed->r.r_tos;
  287                 rec->dst_mask = fed->dst_mask;
  288                 rec->src_mask = fed->src_mask;
  289 
  290                 /* Not supported fields. */
  291                 rec->src_as = rec->dst_as = 0;
  292 
  293                 len = sizeof(struct netflow_v9_record_ipv4_tcp);
  294                 break;
  295         }
  296 #ifdef INET6    
  297         case NETFLOW_V9_FLOW_V6_L4:
  298         {
  299                 /* IPv6 TCP/UDP/[SCTP] */
  300                 struct netflow_v9_record_ipv6_tcp *rec = &rg.rec.v6_tcp;
  301 
  302                 rec->src_addr = fed6->r.src.r_src6;
  303                 rec->dst_addr = fed6->r.dst.r_dst6;
  304                 rec->next_hop = fed6->n.next_hop6;
  305                 rec->i_ifx    = htons(fed6->fle_i_ifx);
  306                 rec->o_ifx    = htons(fed6->fle_o_ifx);
  307                 rec->i_packets  = htonl(fed6->packets);
  308                 rec->i_octets   = htonl(fed6->bytes);
  309                 rec->o_packets  = htonl(0);
  310                 rec->o_octets   = htonl(0);
  311                 rec->first    = htonl(MILLIUPTIME(fed6->first));
  312                 rec->last     = htonl(MILLIUPTIME(fed6->last));
  313                 rec->s_port   = fed6->r.r_sport;
  314                 rec->d_port   = fed6->r.r_dport;
  315                 rec->flags    = fed6->tcp_flags;
  316                 rec->prot     = fed6->r.r_ip_p;
  317                 rec->tos      = fed6->r.r_tos;
  318                 rec->dst_mask = fed6->dst_mask;
  319                 rec->src_mask = fed6->src_mask;
  320 
  321                 /* Not supported fields. */
  322                 rec->src_as = rec->dst_as = 0;
  323 
  324                 len = sizeof(struct netflow_v9_record_ipv6_tcp);
  325                 break;
  326         }
  327 #endif  
  328         default:
  329         {
  330                 CTR1(KTR_NET, "export9_add(): Don't know what to do with %d flow type!", flow_type);
  331                 return (0);
  332         }
  333         }
  334 
  335         /* Check if new records has the same template */
  336         if (flow_type != t->flow_type) {
  337                 /* close old flowset */
  338                 if (t->flow_type != 0)
  339                         close_flowset(m, t);
  340 
  341                 t->flow_type = flow_type;
  342                 t->flow_header = m_pktlen(m);
  343 
  344                 /* Generate data flowset ID */
  345                 fsh.id = htons(NETFLOW_V9_MAX_RESERVED_FLOWSET + flow_type);
  346                 fsh.length = 0;
  347 
  348                 /* m_append should not fail since all data is already allocated */
  349                 if (m_append(m, sizeof(fsh), (void *)&fsh) != 1)
  350                         panic("ng_netflow: m_append() failed");
  351                 
  352         }
  353 
  354         if (m_append(m, len, (void *)&rg.rec) != 1)
  355                 panic("ng_netflow: m_append() failed");
  356 
  357         t->count++;
  358 
  359         if (m_pktlen(m) + sizeof(struct netflow_v9_record_general) + sizeof(struct netflow_v9_flowset_header) >= _NETFLOW_V9_MAX_SIZE(t->mtu))
  360                 return (1); /* end of datagram */
  361         return (0);
  362 }
  363 
  364 /*
  365  * Detach export datagram from fib instance, if there is any.
  366  * If there is no, allocate a new one.
  367  */
  368 item_p
  369 get_export9_dgram(priv_p priv, fib_export_p fe, struct netflow_v9_packet_opt **tt)
  370 {
  371         item_p  item = NULL;
  372         struct netflow_v9_packet_opt *t = NULL;
  373 
  374         mtx_lock(&fe->export9_mtx);
  375         if (fe->exp.item9 != NULL) {
  376                 item = fe->exp.item9;
  377                 fe->exp.item9 = NULL;
  378                 t = fe->exp.item9_opt;
  379                 fe->exp.item9_opt = NULL;
  380         }
  381         mtx_unlock(&fe->export9_mtx);
  382 
  383         if (item == NULL) {
  384                 struct netflow_v9_export_dgram *dgram;
  385                 struct mbuf *m;
  386                 uint16_t mtu = priv->mtu;
  387 
  388                 /* Allocate entire packet at once, allowing easy m_append() calls */
  389                 m = m_getm(NULL, mtu, M_NOWAIT, MT_DATA);
  390                 if (m == NULL)
  391                         return (NULL);
  392 
  393                 t = malloc(sizeof(struct netflow_v9_packet_opt), M_NETFLOW_GENERAL, M_NOWAIT | M_ZERO);
  394                 if (t == NULL) {
  395                         m_free(m);
  396                         return (NULL);
  397                 }
  398 
  399                 item = ng_package_data(m, NG_NOFLAGS);
  400                 if (item == NULL) {
  401                         free(t, M_NETFLOW_GENERAL);
  402                         return (NULL);
  403                 }
  404 
  405                 dgram = mtod(m, struct netflow_v9_export_dgram *);
  406                 dgram->header.count = 0;
  407                 dgram->header.version = htons(NETFLOW_V9);
  408                 /* Set mbuf current data length */
  409                 m->m_len = m->m_pkthdr.len = sizeof(struct netflow_v9_header);
  410 
  411                 t->count = 0;
  412                 t->mtu = mtu;
  413                 t->flow_header = m->m_len;
  414         
  415                 /*
  416                  * Check if we need to insert templates into packet
  417                  */
  418                 
  419                 struct netflow_v9_flowset_header        *fl;
  420         
  421                 if ((time_uptime >= priv->templ_time + fe->templ_last_ts) ||
  422                                 (fe->sent_packets >= priv->templ_packets + fe->templ_last_pkt)) {
  423 
  424                         fe->templ_last_ts = time_uptime;
  425                         fe->templ_last_pkt = fe->sent_packets;
  426 
  427                         fl = priv->v9_flowsets[0];
  428                         m_append(m, ntohs(fl->length), (void *)fl);
  429                         t->flow_header = m->m_len;
  430                         t->count += priv->flowset_records[0];
  431                 }
  432 
  433         }
  434 
  435         *tt = t;
  436         return (item);
  437 }
  438 
  439 /*
  440  * Re-attach incomplete datagram back to fib instance.
  441  * If there is already another one, then send incomplete.
  442  */
  443 void
  444 return_export9_dgram(priv_p priv, fib_export_p fe, item_p item, struct netflow_v9_packet_opt *t, int flags)
  445 {
  446         /*
  447          * It may happen on SMP, that some thread has already
  448          * put its item there, in this case we bail out and
  449          * send what we have to collector.
  450          */
  451         mtx_lock(&fe->export9_mtx);
  452         if (fe->exp.item9 == NULL) {
  453                 fe->exp.item9 = item;
  454                 fe->exp.item9_opt = t;
  455                 mtx_unlock(&fe->export9_mtx);
  456         } else {
  457                 mtx_unlock(&fe->export9_mtx);
  458                 export9_send(priv, fe, item, t, flags);
  459         }
  460 }
  461 
  462 /* Allocate memory and set up flow cache */
  463 void
  464 ng_netflow_v9_cache_init(priv_p priv)
  465 {
  466         generate_v9_templates(priv);
  467 
  468         priv->templ_time = NETFLOW_V9_MAX_TIME_TEMPL;
  469         priv->templ_packets = NETFLOW_V9_MAX_PACKETS_TEMPL;
  470         priv->mtu = BASE_MTU;
  471 }
  472 
  473 /* Free all flow cache memory. Called from ng_netflow_cache_flush() */
  474 void
  475 ng_netflow_v9_cache_flush(priv_p priv)
  476 {
  477         int i;
  478 
  479         /* Free flowsets*/
  480         for (i = 0; i < priv->flowsets_count; i++)
  481                 free(priv->v9_flowsets[i], M_NETFLOW_GENERAL);
  482 }
  483 
  484 /* Get a snapshot of NetFlow v9 settings */
  485 void
  486 ng_netflow_copyv9info(priv_p priv, struct ng_netflow_v9info *i)
  487 {
  488 
  489         i->templ_time = priv->templ_time;
  490         i->templ_packets = priv->templ_packets;
  491         i->mtu = priv->mtu;
  492 }
  493 

Cache object: 282fd3daa5000e400997fc15bb549a67


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