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/ng_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) 2010-2011 Alexander V. Chernikov <melifaro@ipfw.ru>
    3  * Copyright (c) 2004-2005 Gleb Smirnoff <glebius@FreeBSD.org>
    4  * Copyright (c) 2001-2003 Roman V. Palagin <romanp@unshadow.net>
    5  * All rights reserved.
    6  *
    7  * Redistribution and use in source and binary forms, with or without
    8  * modification, are permitted provided that the following conditions
    9  * are met:
   10  * 1. Redistributions of source code must retain the above copyright
   11  *    notice, this list of conditions and the following disclaimer.
   12  * 2. Redistributions in binary form must reproduce the above copyright
   13  *    notice, this list of conditions and the following disclaimer in the
   14  *    documentation and/or other materials provided with the distribution.
   15  *
   16  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
   17  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
   18  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
   19  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
   20  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
   21  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
   22  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
   23  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
   24  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
   25  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
   26  * SUCH DAMAGE.
   27  *
   28  * $SourceForge: ng_netflow.c,v 1.30 2004/09/05 11:37:43 glebius Exp $
   29  */
   30 
   31 static const char rcs_id[] =
   32     "@(#) $FreeBSD: releng/8.4/sys/netgraph/netflow/ng_netflow.c 238619 2012-07-19 11:43:31Z melifaro $";
   33 
   34 #include "opt_inet6.h"
   35 #include "opt_route.h"
   36 
   37 #include <sys/param.h>
   38 #include <sys/systm.h>
   39 #include <sys/kernel.h>
   40 #include <sys/limits.h>
   41 #include <sys/mbuf.h>
   42 #include <sys/socket.h>
   43 #include <sys/syslog.h>
   44 #include <sys/ctype.h>
   45 
   46 #include <net/if.h>
   47 #include <net/ethernet.h>
   48 #include <net/route.h>
   49 #include <net/if_arp.h>
   50 #include <net/if_var.h>
   51 #include <net/if_vlan_var.h>
   52 #include <net/bpf.h>
   53 #include <netinet/in.h>
   54 #include <netinet/in_systm.h>
   55 #include <netinet/ip.h>
   56 #include <netinet/ip6.h>
   57 #include <netinet/tcp.h>
   58 #include <netinet/udp.h>
   59 #include <netinet/sctp.h>
   60 
   61 #include <netgraph/ng_message.h>
   62 #include <netgraph/ng_parse.h>
   63 #include <netgraph/netgraph.h>
   64 #include <netgraph/netflow/netflow.h>
   65 #include <netgraph/netflow/netflow_v9.h>
   66 #include <netgraph/netflow/ng_netflow.h>
   67 
   68 /* Netgraph methods */
   69 static ng_constructor_t ng_netflow_constructor;
   70 static ng_rcvmsg_t      ng_netflow_rcvmsg;
   71 static ng_close_t       ng_netflow_close;
   72 static ng_shutdown_t    ng_netflow_rmnode;
   73 static ng_newhook_t     ng_netflow_newhook;
   74 static ng_rcvdata_t     ng_netflow_rcvdata;
   75 static ng_disconnect_t  ng_netflow_disconnect;
   76 
   77 /* Parse type for struct ng_netflow_info */
   78 static const struct ng_parse_struct_field ng_netflow_info_type_fields[]
   79         = NG_NETFLOW_INFO_TYPE;
   80 static const struct ng_parse_type ng_netflow_info_type = {
   81         &ng_parse_struct_type,
   82         &ng_netflow_info_type_fields
   83 };
   84 
   85 /*  Parse type for struct ng_netflow_ifinfo */
   86 static const struct ng_parse_struct_field ng_netflow_ifinfo_type_fields[]
   87         = NG_NETFLOW_IFINFO_TYPE;
   88 static const struct ng_parse_type ng_netflow_ifinfo_type = {
   89         &ng_parse_struct_type,
   90         &ng_netflow_ifinfo_type_fields
   91 };
   92 
   93 /* Parse type for struct ng_netflow_setdlt */
   94 static const struct ng_parse_struct_field ng_netflow_setdlt_type_fields[]
   95         = NG_NETFLOW_SETDLT_TYPE;
   96 static const struct ng_parse_type ng_netflow_setdlt_type = {
   97         &ng_parse_struct_type,
   98         &ng_netflow_setdlt_type_fields
   99 };
  100 
  101 /* Parse type for ng_netflow_setifindex */
  102 static const struct ng_parse_struct_field ng_netflow_setifindex_type_fields[]
  103         = NG_NETFLOW_SETIFINDEX_TYPE;
  104 static const struct ng_parse_type ng_netflow_setifindex_type = {
  105         &ng_parse_struct_type,
  106         &ng_netflow_setifindex_type_fields
  107 };
  108 
  109 /* Parse type for ng_netflow_settimeouts */
  110 static const struct ng_parse_struct_field ng_netflow_settimeouts_type_fields[]
  111         = NG_NETFLOW_SETTIMEOUTS_TYPE;
  112 static const struct ng_parse_type ng_netflow_settimeouts_type = {
  113         &ng_parse_struct_type,
  114         &ng_netflow_settimeouts_type_fields
  115 };
  116 
  117 /* Parse type for ng_netflow_setconfig */
  118 static const struct ng_parse_struct_field ng_netflow_setconfig_type_fields[]
  119         = NG_NETFLOW_SETCONFIG_TYPE;
  120 static const struct ng_parse_type ng_netflow_setconfig_type = {
  121         &ng_parse_struct_type,
  122         &ng_netflow_setconfig_type_fields
  123 };
  124 
  125 /* Parse type for ng_netflow_settemplate */
  126 static const struct ng_parse_struct_field ng_netflow_settemplate_type_fields[]
  127         = NG_NETFLOW_SETTEMPLATE_TYPE;
  128 static const struct ng_parse_type ng_netflow_settemplate_type = {
  129         &ng_parse_struct_type,
  130         &ng_netflow_settemplate_type_fields
  131 };
  132 
  133 /* Parse type for ng_netflow_setmtu */
  134 static const struct ng_parse_struct_field ng_netflow_setmtu_type_fields[]
  135         = NG_NETFLOW_SETMTU_TYPE;
  136 static const struct ng_parse_type ng_netflow_setmtu_type = {
  137         &ng_parse_struct_type,
  138         &ng_netflow_setmtu_type_fields
  139 };
  140 
  141 /* List of commands and how to convert arguments to/from ASCII */
  142 static const struct ng_cmdlist ng_netflow_cmds[] = {
  143        {
  144          NGM_NETFLOW_COOKIE,
  145          NGM_NETFLOW_INFO,
  146          "info",
  147          NULL,
  148          &ng_netflow_info_type
  149        },
  150        {
  151         NGM_NETFLOW_COOKIE,
  152         NGM_NETFLOW_IFINFO,
  153         "ifinfo",
  154         &ng_parse_uint16_type,
  155         &ng_netflow_ifinfo_type
  156        },
  157        {
  158         NGM_NETFLOW_COOKIE,
  159         NGM_NETFLOW_SETDLT,
  160         "setdlt",
  161         &ng_netflow_setdlt_type,
  162         NULL
  163        },
  164        {
  165         NGM_NETFLOW_COOKIE,
  166         NGM_NETFLOW_SETIFINDEX,
  167         "setifindex",
  168         &ng_netflow_setifindex_type,
  169         NULL
  170        },
  171        {
  172         NGM_NETFLOW_COOKIE,
  173         NGM_NETFLOW_SETTIMEOUTS,
  174         "settimeouts",
  175         &ng_netflow_settimeouts_type,
  176         NULL
  177        },
  178        {
  179         NGM_NETFLOW_COOKIE,
  180         NGM_NETFLOW_SETCONFIG,
  181         "setconfig",
  182         &ng_netflow_setconfig_type,
  183         NULL
  184        },
  185        {
  186         NGM_NETFLOW_COOKIE,
  187         NGM_NETFLOW_SETTEMPLATE,
  188         "settemplate",
  189         &ng_netflow_settemplate_type,
  190         NULL
  191        },
  192        {
  193         NGM_NETFLOW_COOKIE,
  194         NGM_NETFLOW_SETMTU,
  195         "setmtu",
  196         &ng_netflow_setmtu_type,
  197         NULL
  198        },
  199        { 0 }
  200 };
  201 
  202 
  203 /* Netgraph node type descriptor */
  204 static struct ng_type ng_netflow_typestruct = {
  205         .version =      NG_ABI_VERSION,
  206         .name =         NG_NETFLOW_NODE_TYPE,
  207         .constructor =  ng_netflow_constructor,
  208         .rcvmsg =       ng_netflow_rcvmsg,
  209         .close =        ng_netflow_close,
  210         .shutdown =     ng_netflow_rmnode,
  211         .newhook =      ng_netflow_newhook,
  212         .rcvdata =      ng_netflow_rcvdata,
  213         .disconnect =   ng_netflow_disconnect,
  214         .cmdlist =      ng_netflow_cmds,
  215 };
  216 NETGRAPH_INIT(netflow, &ng_netflow_typestruct);
  217 
  218 /* Called at node creation */
  219 static int
  220 ng_netflow_constructor(node_p node)
  221 {
  222         priv_p priv;
  223         int i;
  224 
  225         /* Initialize private data */
  226         priv = malloc(sizeof(*priv), M_NETGRAPH, M_NOWAIT);
  227         if (priv == NULL)
  228                 return (ENOMEM);
  229         bzero(priv, sizeof(*priv));
  230 
  231         /* Initialize fib data */
  232         priv->maxfibs = rt_numfibs;
  233         priv->fib_data = malloc(sizeof(fib_export_p) * priv->maxfibs,
  234             M_NETGRAPH, M_WAITOK | M_ZERO);
  235 
  236         /* Make node and its data point at each other */
  237         NG_NODE_SET_PRIVATE(node, priv);
  238         priv->node = node;
  239 
  240         /* Initialize timeouts to default values */
  241         priv->info.nfinfo_inact_t = INACTIVE_TIMEOUT;
  242         priv->info.nfinfo_act_t = ACTIVE_TIMEOUT;
  243 
  244         /* Set default config */
  245         for (i = 0; i < NG_NETFLOW_MAXIFACES; i++)
  246                 priv->ifaces[i].info.conf = NG_NETFLOW_CONF_INGRESS;
  247 
  248         /* Initialize callout handle */
  249         callout_init(&priv->exp_callout, CALLOUT_MPSAFE);
  250 
  251         /* Allocate memory and set up flow cache */
  252         ng_netflow_cache_init(priv);
  253 
  254         return (0);
  255 }
  256 
  257 /*
  258  * ng_netflow supports two hooks: data and export.
  259  * Incoming traffic is expected on data, and expired
  260  * netflow datagrams are sent to export.
  261  */
  262 static int
  263 ng_netflow_newhook(node_p node, hook_p hook, const char *name)
  264 {
  265         const priv_p priv = NG_NODE_PRIVATE(node);
  266 
  267         if (strncmp(name, NG_NETFLOW_HOOK_DATA, /* an iface hook? */
  268             strlen(NG_NETFLOW_HOOK_DATA)) == 0) {
  269                 iface_p iface;
  270                 int ifnum = -1;
  271                 const char *cp;
  272                 char *eptr;
  273 
  274                 cp = name + strlen(NG_NETFLOW_HOOK_DATA);
  275                 if (!isdigit(*cp) || (cp[0] == '' && cp[1] != '\0'))
  276                         return (EINVAL);
  277 
  278                 ifnum = (int)strtoul(cp, &eptr, 10);
  279                 if (*eptr != '\0' || ifnum < 0 || ifnum >= NG_NETFLOW_MAXIFACES)
  280                         return (EINVAL);
  281 
  282                 /* See if hook is already connected */
  283                 if (priv->ifaces[ifnum].hook != NULL)
  284                         return (EISCONN);
  285 
  286                 iface = &priv->ifaces[ifnum];
  287 
  288                 /* Link private info and hook together */
  289                 NG_HOOK_SET_PRIVATE(hook, iface);
  290                 iface->hook = hook;
  291 
  292                 /*
  293                  * In most cases traffic accounting is done on an
  294                  * Ethernet interface, so default data link type
  295                  * will be DLT_EN10MB.
  296                  */
  297                 iface->info.ifinfo_dlt = DLT_EN10MB;
  298 
  299         } else if (strncmp(name, NG_NETFLOW_HOOK_OUT,
  300             strlen(NG_NETFLOW_HOOK_OUT)) == 0) {
  301                 iface_p iface;
  302                 int ifnum = -1;
  303                 const char *cp;
  304                 char *eptr;
  305 
  306                 cp = name + strlen(NG_NETFLOW_HOOK_OUT);
  307                 if (!isdigit(*cp) || (cp[0] == '' && cp[1] != '\0'))
  308                         return (EINVAL);
  309 
  310                 ifnum = (int)strtoul(cp, &eptr, 10);
  311                 if (*eptr != '\0' || ifnum < 0 || ifnum >= NG_NETFLOW_MAXIFACES)
  312                         return (EINVAL);
  313 
  314                 /* See if hook is already connected */
  315                 if (priv->ifaces[ifnum].out != NULL)
  316                         return (EISCONN);
  317 
  318                 iface = &priv->ifaces[ifnum];
  319 
  320                 /* Link private info and hook together */
  321                 NG_HOOK_SET_PRIVATE(hook, iface);
  322                 iface->out = hook;
  323 
  324         } else if (strcmp(name, NG_NETFLOW_HOOK_EXPORT) == 0) {
  325 
  326                 if (priv->export != NULL)
  327                         return (EISCONN);
  328 
  329                 /* Netflow version 5 supports 32-bit counters only */
  330                 if (CNTR_MAX == UINT64_MAX)
  331                         return (EINVAL);
  332 
  333                 priv->export = hook;
  334 
  335 #if 0   /* TODO: profile & test first */
  336                 /*
  337                  * We send export dgrams in interrupt handlers and in
  338                  * callout threads. We'd better queue data for later
  339                  * netgraph ISR processing.
  340                  */
  341                 NG_HOOK_FORCE_QUEUE(NG_HOOK_PEER(hook));
  342 #endif
  343 
  344                 /* Exporter is ready. Let's schedule expiry. */
  345                 callout_reset(&priv->exp_callout, (1*hz), &ng_netflow_expire,
  346                     (void *)priv);
  347         } else if (strcmp(name, NG_NETFLOW_HOOK_EXPORT9) == 0) {
  348 
  349                 if (priv->export9 != NULL)
  350                         return (EISCONN);
  351 
  352                 priv->export9 = hook;
  353 
  354                 /* Exporter is ready. Let's schedule expiry. */
  355                 callout_reset(&priv->exp_callout, (1*hz), &ng_netflow_expire,
  356                     (void *)priv);
  357         } else
  358                 return (EINVAL);
  359 
  360         return (0);
  361 }
  362 
  363 /* Get a netgraph control message. */
  364 static int
  365 ng_netflow_rcvmsg (node_p node, item_p item, hook_p lasthook)
  366 {
  367         const priv_p priv = NG_NODE_PRIVATE(node);
  368         struct ng_mesg *resp = NULL;
  369         int error = 0;
  370         struct ng_mesg *msg;
  371 
  372         NGI_GET_MSG(item, msg);
  373 
  374         /* Deal with message according to cookie and command */
  375         switch (msg->header.typecookie) {
  376         case NGM_NETFLOW_COOKIE:
  377                 switch (msg->header.cmd) {
  378                 case NGM_NETFLOW_INFO:
  379                 {
  380                         struct ng_netflow_info *i;
  381 
  382                         NG_MKRESPONSE(resp, msg, sizeof(struct ng_netflow_info),
  383                             M_NOWAIT);
  384                         i = (struct ng_netflow_info *)resp->data;
  385                         ng_netflow_copyinfo(priv, i);
  386 
  387                         break;
  388                 }
  389                 case NGM_NETFLOW_IFINFO:
  390                 {
  391                         struct ng_netflow_ifinfo *i;
  392                         const uint16_t *index;
  393 
  394                         if (msg->header.arglen != sizeof(uint16_t))
  395                                  ERROUT(EINVAL);
  396 
  397                         index  = (uint16_t *)msg->data;
  398                         if (*index >= NG_NETFLOW_MAXIFACES)
  399                                 ERROUT(EINVAL);
  400 
  401                         /* connected iface? */
  402                         if (priv->ifaces[*index].hook == NULL)
  403                                  ERROUT(EINVAL);
  404 
  405                         NG_MKRESPONSE(resp, msg,
  406                              sizeof(struct ng_netflow_ifinfo), M_NOWAIT);
  407                         i = (struct ng_netflow_ifinfo *)resp->data;
  408                         memcpy((void *)i, (void *)&priv->ifaces[*index].info,
  409                             sizeof(priv->ifaces[*index].info));
  410 
  411                         break;
  412                 }
  413                 case NGM_NETFLOW_SETDLT:
  414                 {
  415                         struct ng_netflow_setdlt *set;
  416                         struct ng_netflow_iface *iface;
  417 
  418                         if (msg->header.arglen != sizeof(struct ng_netflow_setdlt))
  419                                 ERROUT(EINVAL);
  420 
  421                         set = (struct ng_netflow_setdlt *)msg->data;
  422                         if (set->iface >= NG_NETFLOW_MAXIFACES)
  423                                 ERROUT(EINVAL);
  424                         iface = &priv->ifaces[set->iface];
  425 
  426                         /* connected iface? */
  427                         if (iface->hook == NULL)
  428                                 ERROUT(EINVAL);
  429 
  430                         switch (set->dlt) {
  431                         case    DLT_EN10MB:
  432                                 iface->info.ifinfo_dlt = DLT_EN10MB;
  433                                 break;
  434                         case    DLT_RAW:
  435                                 iface->info.ifinfo_dlt = DLT_RAW;
  436                                 break;
  437                         default:
  438                                 ERROUT(EINVAL);
  439                         }
  440                         break;
  441                 }
  442                 case NGM_NETFLOW_SETIFINDEX:
  443                 {
  444                         struct ng_netflow_setifindex *set;
  445                         struct ng_netflow_iface *iface;
  446 
  447                         if (msg->header.arglen != sizeof(struct ng_netflow_setifindex))
  448                                 ERROUT(EINVAL);
  449 
  450                         set = (struct ng_netflow_setifindex *)msg->data;
  451                         if (set->iface >= NG_NETFLOW_MAXIFACES)
  452                                 ERROUT(EINVAL);
  453                         iface = &priv->ifaces[set->iface];
  454 
  455                         /* connected iface? */
  456                         if (iface->hook == NULL)
  457                                 ERROUT(EINVAL);
  458 
  459                         iface->info.ifinfo_index = set->index;
  460 
  461                         break;
  462                 }
  463                 case NGM_NETFLOW_SETTIMEOUTS:
  464                 {
  465                         struct ng_netflow_settimeouts *set;
  466 
  467                         if (msg->header.arglen != sizeof(struct ng_netflow_settimeouts))
  468                                 ERROUT(EINVAL);
  469 
  470                         set = (struct ng_netflow_settimeouts *)msg->data;
  471 
  472                         priv->info.nfinfo_inact_t = set->inactive_timeout;
  473                         priv->info.nfinfo_act_t = set->active_timeout;
  474 
  475                         break;
  476                 }
  477                 case NGM_NETFLOW_SETCONFIG:
  478                 {
  479                         struct ng_netflow_setconfig *set;
  480 
  481                         if (msg->header.arglen != sizeof(struct ng_netflow_setconfig))
  482                                 ERROUT(EINVAL);
  483 
  484                         set = (struct ng_netflow_setconfig *)msg->data;
  485 
  486                         if (set->iface >= NG_NETFLOW_MAXIFACES)
  487                                 ERROUT(EINVAL);
  488                         
  489                         priv->ifaces[set->iface].info.conf = set->conf;
  490         
  491                         break;
  492                 }
  493                 case NGM_NETFLOW_SETTEMPLATE:
  494                 {
  495                         struct ng_netflow_settemplate *set;
  496 
  497                         if (msg->header.arglen != sizeof(struct ng_netflow_settemplate))
  498                                 ERROUT(EINVAL);
  499 
  500                         set = (struct ng_netflow_settemplate *)msg->data;
  501 
  502                         priv->templ_packets = set->packets;
  503                         priv->templ_time = set->time;
  504 
  505                         break;
  506                 }
  507                 case NGM_NETFLOW_SETMTU:
  508                 {
  509                         struct ng_netflow_setmtu *set;
  510 
  511                         if (msg->header.arglen != sizeof(struct ng_netflow_setmtu))
  512                                 ERROUT(EINVAL);
  513 
  514                         set = (struct ng_netflow_setmtu *)msg->data;
  515                         if ((set->mtu < MIN_MTU) || (set->mtu > MAX_MTU))
  516                                 ERROUT(EINVAL);
  517 
  518                         priv->mtu = set->mtu;
  519 
  520                         break;
  521                 }
  522                 case NGM_NETFLOW_SHOW:
  523                 {
  524                         if (msg->header.arglen != sizeof(struct ngnf_show_header))
  525                                 ERROUT(EINVAL);
  526 
  527                         NG_MKRESPONSE(resp, msg, NGRESP_SIZE, M_NOWAIT);
  528 
  529                         if (!resp)
  530                                 ERROUT(ENOMEM);
  531 
  532                         error = ng_netflow_flow_show(priv,
  533                             (struct ngnf_show_header *)msg->data,
  534                             (struct ngnf_show_header *)resp->data);
  535 
  536                         if (error)
  537                                 NG_FREE_MSG(resp);
  538 
  539                         break;
  540                 }
  541                 default:
  542                         ERROUT(EINVAL);         /* unknown command */
  543                         break;
  544                 }
  545                 break;
  546         default:
  547                 ERROUT(EINVAL);         /* incorrect cookie */
  548                 break;
  549         }
  550 
  551         /*
  552          * Take care of synchronous response, if any.
  553          * Free memory and return.
  554          */
  555 done:
  556         NG_RESPOND_MSG(error, node, item, resp);
  557         NG_FREE_MSG(msg);
  558 
  559         return (error);
  560 }
  561 
  562 /* Receive data on hook. */
  563 static int
  564 ng_netflow_rcvdata (hook_p hook, item_p item)
  565 {
  566         const node_p node = NG_HOOK_NODE(hook);
  567         const priv_p priv = NG_NODE_PRIVATE(node);
  568         const iface_p iface = NG_HOOK_PRIVATE(hook);
  569         hook_p out;
  570         struct mbuf *m = NULL, *m_old = NULL;
  571         struct ip *ip = NULL;
  572         struct ip6_hdr *ip6 = NULL;
  573         struct m_tag *mtag;
  574         int pullup_len = 0, off;
  575         uint8_t acct = 0, bypass = 0, flags = 0, upper_proto = 0;
  576         int error = 0, l3_off = 0;
  577         unsigned int src_if_index;
  578         caddr_t upper_ptr = NULL;
  579         fib_export_p fe;        
  580         uint32_t fib;
  581 
  582         if ((hook == priv->export) || (hook == priv->export9)) {
  583                 /*
  584                  * Data arrived on export hook.
  585                  * This must not happen.
  586                  */
  587                 log(LOG_ERR, "ng_netflow: incoming data on export hook!\n");
  588                 ERROUT(EINVAL);
  589         };
  590 
  591         if (hook == iface->hook) {
  592                 if ((iface->info.conf & NG_NETFLOW_CONF_INGRESS) == 0)
  593                         bypass = 1;
  594                 out = iface->out;
  595         } else if (hook == iface->out) {
  596                 if ((iface->info.conf & NG_NETFLOW_CONF_EGRESS) == 0)
  597                         bypass = 1;
  598                 out = iface->hook;
  599         } else
  600                 ERROUT(EINVAL);
  601 
  602         if ((!bypass) &&
  603             (iface->info.conf & (NG_NETFLOW_CONF_ONCE | NG_NETFLOW_CONF_THISONCE))) {
  604                 mtag = m_tag_locate(NGI_M(item), MTAG_NETFLOW,
  605                     MTAG_NETFLOW_CALLED, NULL);
  606                 while (mtag != NULL) {
  607                         if ((iface->info.conf & NG_NETFLOW_CONF_ONCE) ||
  608                             ((ng_ID_t *)(mtag + 1))[0] == NG_NODE_ID(node)) {
  609                                 bypass = 1;
  610                                 break;
  611                         }
  612                         mtag = m_tag_locate(NGI_M(item), MTAG_NETFLOW,
  613                             MTAG_NETFLOW_CALLED, mtag);
  614                 }
  615         }
  616         
  617         if (bypass) {
  618                 if (out == NULL)
  619                         ERROUT(ENOTCONN);
  620 
  621                 NG_FWD_ITEM_HOOK(error, item, out);
  622                 return (error);
  623         }
  624         
  625         if (iface->info.conf & (NG_NETFLOW_CONF_ONCE | NG_NETFLOW_CONF_THISONCE)) {
  626                 mtag = m_tag_alloc(MTAG_NETFLOW, MTAG_NETFLOW_CALLED,
  627                     sizeof(ng_ID_t), M_NOWAIT);
  628                 if (mtag) {
  629                         ((ng_ID_t *)(mtag + 1))[0] = NG_NODE_ID(node);
  630                         m_tag_prepend(NGI_M(item), mtag);
  631                 }
  632         }
  633 
  634         /* Import configuration flags related to flow creation */
  635         flags = iface->info.conf & NG_NETFLOW_FLOW_FLAGS;
  636 
  637         NGI_GET_M(item, m);
  638         m_old = m;
  639 
  640         /* Increase counters. */
  641         iface->info.ifinfo_packets++;
  642 
  643         /*
  644          * Depending on interface data link type and packet contents
  645          * we pullup enough data, so that ng_netflow_flow_add() does not
  646          * need to know about mbuf at all. We keep current length of data
  647          * needed to be contiguous in pullup_len. mtod() is done at the
  648          * very end one more time, since m can had changed after pulluping.
  649          *
  650          * In case of unrecognized data we don't return error, but just
  651          * pass data to downstream hook, if it is available.
  652          */
  653 
  654 #define M_CHECK(length) do {                                    \
  655         pullup_len += length;                                   \
  656         if (((m)->m_pkthdr.len < (pullup_len)) ||               \
  657            ((pullup_len) > MHLEN)) {                            \
  658                 error = EINVAL;                                 \
  659                 goto bypass;                                    \
  660         }                                                       \
  661         if ((m)->m_len < (pullup_len) &&                        \
  662            (((m) = m_pullup((m),(pullup_len))) == NULL)) {      \
  663                 error = ENOBUFS;                                \
  664                 goto done;                                      \
  665         }                                                       \
  666 } while (0)
  667 
  668         switch (iface->info.ifinfo_dlt) {
  669         case DLT_EN10MB:        /* Ethernet */
  670             {
  671                 struct ether_header *eh;
  672                 uint16_t etype;
  673 
  674                 M_CHECK(sizeof(struct ether_header));
  675                 eh = mtod(m, struct ether_header *);
  676 
  677                 /* Make sure this is IP frame. */
  678                 etype = ntohs(eh->ether_type);
  679                 switch (etype) {
  680                 case ETHERTYPE_IP:
  681                         M_CHECK(sizeof(struct ip));
  682                         eh = mtod(m, struct ether_header *);
  683                         ip = (struct ip *)(eh + 1);
  684                         l3_off = sizeof(struct ether_header);
  685                         break;
  686 #ifdef INET6
  687                 case ETHERTYPE_IPV6:
  688                         /*
  689                          * m_pullup() called by M_CHECK() pullups
  690                          * kern.ipc.max_protohdr (default 60 bytes) which is enough
  691                          */
  692                         M_CHECK(sizeof(struct ip6_hdr));
  693                         eh = mtod(m, struct ether_header *);
  694                         ip6 = (struct ip6_hdr *)(eh + 1);
  695                         l3_off = sizeof(struct ether_header);
  696                         break;
  697 #endif
  698                 case ETHERTYPE_VLAN:
  699                     {
  700                         struct ether_vlan_header *evh;
  701 
  702                         M_CHECK(sizeof(struct ether_vlan_header) -
  703                             sizeof(struct ether_header));
  704                         evh = mtod(m, struct ether_vlan_header *);
  705                         etype = ntohs(evh->evl_proto);
  706                         l3_off = sizeof(struct ether_vlan_header);
  707 
  708                         if (etype == ETHERTYPE_IP) {
  709                                 M_CHECK(sizeof(struct ip));
  710                                 ip = (struct ip *)(evh + 1);
  711                                 break;
  712 #ifdef INET6
  713                         } else if (etype == ETHERTYPE_IPV6) {
  714                                 M_CHECK(sizeof(struct ip6_hdr));
  715                                 ip6 = (struct ip6_hdr *)(evh + 1);
  716                                 break;
  717 #endif
  718                         }
  719                     }
  720                 default:
  721                         goto bypass;    /* pass this frame */
  722                 }
  723                 break;
  724             }
  725         case DLT_RAW:           /* IP packets */
  726                 M_CHECK(sizeof(struct ip));
  727                 ip = mtod(m, struct ip *);
  728                 /* l3_off is already zero */
  729 #ifdef INET6
  730                 /* If INET6 is not defined IPv6 packets will be discarded in ng_netflow_flow_add() */
  731                 if (ip->ip_v == IP6VERSION) {
  732                         /* IPv6 packet */
  733                         ip = NULL;
  734                         M_CHECK(sizeof(struct ip6_hdr) - sizeof(struct ip));
  735                         ip6 = mtod(m, struct ip6_hdr *);
  736                 }
  737 #endif
  738                 break;
  739         default:
  740                 goto bypass;
  741                 break;
  742         }
  743 
  744         off = pullup_len;
  745 
  746         if ((ip != NULL) && ((ip->ip_off & htons(IP_OFFMASK)) == 0)) {
  747                 if ((ip->ip_v != IPVERSION) ||
  748                     ((ip->ip_hl << 2) < sizeof(struct ip)))
  749                         goto bypass;
  750                 /*
  751                  * In case of IPv4 header with options, we haven't pulled
  752                  * up enough, yet.
  753                  */
  754                 M_CHECK((ip->ip_hl << 2) - sizeof(struct ip));
  755 
  756                 /* Save upper layer offset and proto */
  757                 off = pullup_len;
  758                 upper_proto = ip->ip_p;
  759 
  760                 /*
  761                  * XXX: in case of wrong upper layer header we will forward this packet
  762                  * but skip this record in netflow
  763                  */
  764                 switch (ip->ip_p) {
  765                 case IPPROTO_TCP:
  766                         M_CHECK(sizeof(struct tcphdr));
  767                         break;
  768                 case IPPROTO_UDP:
  769                         M_CHECK(sizeof(struct udphdr));
  770                         break;
  771                 case IPPROTO_SCTP:
  772                         M_CHECK(sizeof(struct sctphdr));
  773                         break;
  774                 }
  775         } else if (ip != NULL) {
  776                 /* Nothing to save except upper layer proto, since this is packet fragment */
  777                 flags |= NG_NETFLOW_IS_FRAG;
  778                 upper_proto = ip->ip_p;
  779                 if ((ip->ip_v != IPVERSION) ||
  780                     ((ip->ip_hl << 2) < sizeof(struct ip)))
  781                         goto bypass;
  782 #ifdef INET6
  783         } else if (ip6 != NULL) {
  784                 /* Check if we can export */
  785                 if (priv->export9 == NULL)
  786                         goto bypass;
  787 
  788                 /* Loop thru IPv6 extended headers to get upper layer header / frag */
  789                 int cur = ip6->ip6_nxt, hdr_off = 0;
  790                 struct ip6_ext *ip6e;
  791                 struct ip6_frag *ip6f;
  792 
  793                 /* Save upper layer info */
  794                 off = pullup_len;
  795                 upper_proto = cur;
  796 
  797                 if ((ip6->ip6_vfc & IPV6_VERSION_MASK) != IPV6_VERSION)
  798                         goto bypass;
  799 
  800                 while (42) {
  801                         switch (cur) {
  802                         /*
  803                          * Same as in IPv4, we can forward 'bad' packet without accounting
  804                          */
  805                         case IPPROTO_TCP:
  806                                 M_CHECK(sizeof(struct tcphdr));
  807                                 goto loopend;
  808                         case IPPROTO_UDP:
  809                                 M_CHECK(sizeof(struct udphdr));
  810                                 goto loopend;
  811                         case IPPROTO_SCTP:
  812                                 M_CHECK(sizeof(struct sctphdr));
  813                                 goto loopend;
  814 
  815                         /* Loop until 'real' upper layer headers */
  816                         case IPPROTO_HOPOPTS:
  817                         case IPPROTO_ROUTING:
  818                         case IPPROTO_DSTOPTS:
  819                                 M_CHECK(sizeof(struct ip6_ext));
  820                                 ip6e = (struct ip6_ext *)(mtod(m, caddr_t) + off);
  821                                 upper_proto = ip6e->ip6e_nxt;
  822                                 hdr_off = (ip6e->ip6e_len + 1) << 3;
  823                                 break;
  824 
  825                         /* RFC4302, can be before DSTOPTS */
  826                         case IPPROTO_AH:
  827                                 M_CHECK(sizeof(struct ip6_ext));
  828                                 ip6e = (struct ip6_ext *)(mtod(m, caddr_t) + off);
  829                                 upper_proto = ip6e->ip6e_nxt;
  830                                 hdr_off = (ip6e->ip6e_len + 2) << 2;
  831                                 break;
  832 
  833                         case IPPROTO_FRAGMENT:
  834                                 M_CHECK(sizeof(struct ip6_frag));
  835                                 ip6f = (struct ip6_frag *)(mtod(m, caddr_t) + off);
  836                                 upper_proto = ip6f->ip6f_nxt;
  837                                 hdr_off = sizeof(struct ip6_frag);
  838                                 off += hdr_off;
  839                                 flags |= NG_NETFLOW_IS_FRAG;
  840                                 goto loopend;
  841 
  842 #if 0                           
  843                         case IPPROTO_NONE:
  844                                 goto loopend;
  845 #endif
  846                         /*
  847                          * Any unknow header (new extension or IPv6/IPv4
  848                          * header for tunnels) ends loop.
  849                          */
  850                         default:
  851                                 goto loopend;
  852                         }
  853 
  854                         off += hdr_off;
  855                         cur = upper_proto;
  856                 }
  857 #endif
  858         }
  859 #undef  M_CHECK
  860 
  861 #ifdef INET6
  862 loopend:
  863 #endif
  864         /* Just in case of real reallocation in M_CHECK() / m_pullup() */
  865         if (m != m_old) {
  866                 atomic_fetchadd_32(&priv->info.nfinfo_realloc_mbuf, 1);
  867                 /* Restore ip/ipv6 pointer */
  868                 if (ip != NULL)
  869                         ip = (struct ip *)(mtod(m, caddr_t) + l3_off);
  870                 else if (ip6 != NULL)
  871                         ip6 = (struct ip6_hdr *)(mtod(m, caddr_t) + l3_off);
  872         }
  873 
  874         upper_ptr = (caddr_t)(mtod(m, caddr_t) + off);
  875 
  876         /* Determine packet input interface. Prefer configured. */
  877         src_if_index = 0;
  878         if (hook == iface->out || iface->info.ifinfo_index == 0) {
  879                 if (m->m_pkthdr.rcvif != NULL)
  880                         src_if_index = m->m_pkthdr.rcvif->if_index;
  881         } else
  882                 src_if_index = iface->info.ifinfo_index;
  883         
  884         /* Check packet FIB */
  885         fib = M_GETFIB(m);
  886         if (fib >= priv->maxfibs) {
  887                 CTR2(KTR_NET, "ng_netflow_rcvdata(): packet fib %d is out of "
  888                     "range of available fibs: 0 .. %d",
  889                     fib, priv->maxfibs);
  890                 goto bypass;
  891         }
  892 
  893         if ((fe = priv_to_fib(priv, fib)) == NULL) {
  894                 /* Setup new FIB */
  895                 if (ng_netflow_fib_init(priv, fib) != 0) {
  896                         /* malloc() failed */
  897                         goto bypass;
  898                 }
  899 
  900                 fe = priv_to_fib(priv, fib);
  901         }
  902 
  903         if (ip != NULL)
  904                 error = ng_netflow_flow_add(priv, fe, ip, upper_ptr, upper_proto, flags, src_if_index);
  905 #ifdef INET6            
  906         else if (ip6 != NULL)
  907                 error = ng_netflow_flow6_add(priv, fe, ip6, upper_ptr, upper_proto, flags, src_if_index);
  908 #endif
  909         else
  910                 goto bypass;
  911         
  912         acct = 1;
  913 bypass:
  914         if (out != NULL) {
  915                 if (acct == 0) {
  916                         /* Accounting failure */
  917                         if (ip != NULL) {
  918                                 atomic_fetchadd_32(&priv->info.nfinfo_spackets, 1);
  919                                 priv->info.nfinfo_sbytes += m_length(m, NULL);
  920                         } else if (ip6 != NULL) {
  921                                 atomic_fetchadd_32(&priv->info.nfinfo_spackets6, 1);
  922                                 priv->info.nfinfo_sbytes6 += m_length(m, NULL);
  923                         }
  924                 }
  925 
  926                 /* XXX: error gets overwritten here */
  927                 NG_FWD_NEW_DATA(error, item, out, m);
  928                 return (error);
  929         }
  930 done:
  931         if (item)
  932                 NG_FREE_ITEM(item);
  933         if (m)
  934                 NG_FREE_M(m);
  935 
  936         return (error); 
  937 }
  938 
  939 /* We will be shut down in a moment */
  940 static int
  941 ng_netflow_close(node_p node)
  942 {
  943         const priv_p priv = NG_NODE_PRIVATE(node);
  944 
  945         callout_drain(&priv->exp_callout);
  946         ng_netflow_cache_flush(priv);
  947 
  948         return (0);
  949 }
  950 
  951 /* Do local shutdown processing. */
  952 static int
  953 ng_netflow_rmnode(node_p node)
  954 {
  955         const priv_p priv = NG_NODE_PRIVATE(node);
  956 
  957         NG_NODE_SET_PRIVATE(node, NULL);
  958         NG_NODE_UNREF(priv->node);
  959 
  960         free(priv->fib_data, M_NETGRAPH);
  961         free(priv, M_NETGRAPH);
  962 
  963         return (0);
  964 }
  965 
  966 /* Hook disconnection. */
  967 static int
  968 ng_netflow_disconnect(hook_p hook)
  969 {
  970         node_p node = NG_HOOK_NODE(hook);
  971         priv_p priv = NG_NODE_PRIVATE(node);
  972         iface_p iface = NG_HOOK_PRIVATE(hook);
  973 
  974         if (iface != NULL) {
  975                 if (iface->hook == hook)
  976                         iface->hook = NULL;
  977                 if (iface->out == hook)
  978                         iface->out = NULL;
  979         }
  980 
  981         /* if export hook disconnected stop running expire(). */
  982         if (hook == priv->export) {
  983                 if (priv->export9 == NULL)
  984                         callout_drain(&priv->exp_callout);
  985                 priv->export = NULL;
  986         }
  987 
  988         if (hook == priv->export9) {
  989                 if (priv->export == NULL)
  990                         callout_drain(&priv->exp_callout);
  991                 priv->export9 = NULL;
  992         }
  993 
  994         /* Removal of the last link destroys the node. */
  995         if (NG_NODE_NUMHOOKS(node) == 0)
  996                 ng_rmnode_self(node);
  997 
  998         return (0);
  999 }

Cache object: b54a6342feddd11694cf2254e83dd091


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