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/9.0/sys/netgraph/netflow/ng_netflow.c 223787 2011-07-05 14:48:39Z glebius $";
   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_WAITOK | M_ZERO);
  227 
  228         /* Make node and its data point at each other */
  229         NG_NODE_SET_PRIVATE(node, priv);
  230         priv->node = node;
  231 
  232         /* Initialize timeouts to default values */
  233         priv->info.nfinfo_inact_t = INACTIVE_TIMEOUT;
  234         priv->info.nfinfo_act_t = ACTIVE_TIMEOUT;
  235 
  236         /* Set default config */
  237         for (i = 0; i < NG_NETFLOW_MAXIFACES; i++)
  238                 priv->ifaces[i].info.conf = NG_NETFLOW_CONF_INGRESS;
  239 
  240         /* Initialize callout handle */
  241         callout_init(&priv->exp_callout, CALLOUT_MPSAFE);
  242 
  243         /* Allocate memory and set up flow cache */
  244         ng_netflow_cache_init(priv);
  245 
  246         return (0);
  247 }
  248 
  249 /*
  250  * ng_netflow supports two hooks: data and export.
  251  * Incoming traffic is expected on data, and expired
  252  * netflow datagrams are sent to export.
  253  */
  254 static int
  255 ng_netflow_newhook(node_p node, hook_p hook, const char *name)
  256 {
  257         const priv_p priv = NG_NODE_PRIVATE(node);
  258 
  259         if (strncmp(name, NG_NETFLOW_HOOK_DATA, /* an iface hook? */
  260             strlen(NG_NETFLOW_HOOK_DATA)) == 0) {
  261                 iface_p iface;
  262                 int ifnum = -1;
  263                 const char *cp;
  264                 char *eptr;
  265 
  266                 cp = name + strlen(NG_NETFLOW_HOOK_DATA);
  267                 if (!isdigit(*cp) || (cp[0] == '' && cp[1] != '\0'))
  268                         return (EINVAL);
  269 
  270                 ifnum = (int)strtoul(cp, &eptr, 10);
  271                 if (*eptr != '\0' || ifnum < 0 || ifnum >= NG_NETFLOW_MAXIFACES)
  272                         return (EINVAL);
  273 
  274                 /* See if hook is already connected */
  275                 if (priv->ifaces[ifnum].hook != NULL)
  276                         return (EISCONN);
  277 
  278                 iface = &priv->ifaces[ifnum];
  279 
  280                 /* Link private info and hook together */
  281                 NG_HOOK_SET_PRIVATE(hook, iface);
  282                 iface->hook = hook;
  283 
  284                 /*
  285                  * In most cases traffic accounting is done on an
  286                  * Ethernet interface, so default data link type
  287                  * will be DLT_EN10MB.
  288                  */
  289                 iface->info.ifinfo_dlt = DLT_EN10MB;
  290 
  291         } else if (strncmp(name, NG_NETFLOW_HOOK_OUT,
  292             strlen(NG_NETFLOW_HOOK_OUT)) == 0) {
  293                 iface_p iface;
  294                 int ifnum = -1;
  295                 const char *cp;
  296                 char *eptr;
  297 
  298                 cp = name + strlen(NG_NETFLOW_HOOK_OUT);
  299                 if (!isdigit(*cp) || (cp[0] == '' && cp[1] != '\0'))
  300                         return (EINVAL);
  301 
  302                 ifnum = (int)strtoul(cp, &eptr, 10);
  303                 if (*eptr != '\0' || ifnum < 0 || ifnum >= NG_NETFLOW_MAXIFACES)
  304                         return (EINVAL);
  305 
  306                 /* See if hook is already connected */
  307                 if (priv->ifaces[ifnum].out != NULL)
  308                         return (EISCONN);
  309 
  310                 iface = &priv->ifaces[ifnum];
  311 
  312                 /* Link private info and hook together */
  313                 NG_HOOK_SET_PRIVATE(hook, iface);
  314                 iface->out = hook;
  315 
  316         } else if (strcmp(name, NG_NETFLOW_HOOK_EXPORT) == 0) {
  317 
  318                 if (priv->export != NULL)
  319                         return (EISCONN);
  320 
  321                 /* Netflow version 5 supports 32-bit counters only */
  322                 if (CNTR_MAX == UINT64_MAX)
  323                         return (EINVAL);
  324 
  325                 priv->export = hook;
  326 
  327                 /* Exporter is ready. Let's schedule expiry. */
  328                 callout_reset(&priv->exp_callout, (1*hz), &ng_netflow_expire,
  329                     (void *)priv);
  330         } else if (strcmp(name, NG_NETFLOW_HOOK_EXPORT9) == 0) {
  331 
  332                 if (priv->export9 != NULL)
  333                         return (EISCONN);
  334 
  335                 priv->export9 = hook;
  336 
  337                 /* Exporter is ready. Let's schedule expiry. */
  338                 callout_reset(&priv->exp_callout, (1*hz), &ng_netflow_expire,
  339                     (void *)priv);
  340         } else
  341                 return (EINVAL);
  342 
  343         return (0);
  344 }
  345 
  346 /* Get a netgraph control message. */
  347 static int
  348 ng_netflow_rcvmsg (node_p node, item_p item, hook_p lasthook)
  349 {
  350         const priv_p priv = NG_NODE_PRIVATE(node);
  351         struct ng_mesg *resp = NULL;
  352         int error = 0;
  353         struct ng_mesg *msg;
  354 
  355         NGI_GET_MSG(item, msg);
  356 
  357         /* Deal with message according to cookie and command */
  358         switch (msg->header.typecookie) {
  359         case NGM_NETFLOW_COOKIE:
  360                 switch (msg->header.cmd) {
  361                 case NGM_NETFLOW_INFO:
  362                 {
  363                         struct ng_netflow_info *i;
  364 
  365                         NG_MKRESPONSE(resp, msg, sizeof(struct ng_netflow_info),
  366                             M_NOWAIT);
  367                         i = (struct ng_netflow_info *)resp->data;
  368                         ng_netflow_copyinfo(priv, i);
  369 
  370                         break;
  371                 }
  372                 case NGM_NETFLOW_IFINFO:
  373                 {
  374                         struct ng_netflow_ifinfo *i;
  375                         const uint16_t *index;
  376 
  377                         if (msg->header.arglen != sizeof(uint16_t))
  378                                  ERROUT(EINVAL);
  379 
  380                         index  = (uint16_t *)msg->data;
  381                         if (*index >= NG_NETFLOW_MAXIFACES)
  382                                 ERROUT(EINVAL);
  383 
  384                         /* connected iface? */
  385                         if (priv->ifaces[*index].hook == NULL)
  386                                  ERROUT(EINVAL);
  387 
  388                         NG_MKRESPONSE(resp, msg,
  389                              sizeof(struct ng_netflow_ifinfo), M_NOWAIT);
  390                         i = (struct ng_netflow_ifinfo *)resp->data;
  391                         memcpy((void *)i, (void *)&priv->ifaces[*index].info,
  392                             sizeof(priv->ifaces[*index].info));
  393 
  394                         break;
  395                 }
  396                 case NGM_NETFLOW_SETDLT:
  397                 {
  398                         struct ng_netflow_setdlt *set;
  399                         struct ng_netflow_iface *iface;
  400 
  401                         if (msg->header.arglen != sizeof(struct ng_netflow_setdlt))
  402                                 ERROUT(EINVAL);
  403 
  404                         set = (struct ng_netflow_setdlt *)msg->data;
  405                         if (set->iface >= NG_NETFLOW_MAXIFACES)
  406                                 ERROUT(EINVAL);
  407                         iface = &priv->ifaces[set->iface];
  408 
  409                         /* connected iface? */
  410                         if (iface->hook == NULL)
  411                                 ERROUT(EINVAL);
  412 
  413                         switch (set->dlt) {
  414                         case    DLT_EN10MB:
  415                                 iface->info.ifinfo_dlt = DLT_EN10MB;
  416                                 break;
  417                         case    DLT_RAW:
  418                                 iface->info.ifinfo_dlt = DLT_RAW;
  419                                 break;
  420                         default:
  421                                 ERROUT(EINVAL);
  422                         }
  423                         break;
  424                 }
  425                 case NGM_NETFLOW_SETIFINDEX:
  426                 {
  427                         struct ng_netflow_setifindex *set;
  428                         struct ng_netflow_iface *iface;
  429 
  430                         if (msg->header.arglen != sizeof(struct ng_netflow_setifindex))
  431                                 ERROUT(EINVAL);
  432 
  433                         set = (struct ng_netflow_setifindex *)msg->data;
  434                         if (set->iface >= NG_NETFLOW_MAXIFACES)
  435                                 ERROUT(EINVAL);
  436                         iface = &priv->ifaces[set->iface];
  437 
  438                         /* connected iface? */
  439                         if (iface->hook == NULL)
  440                                 ERROUT(EINVAL);
  441 
  442                         iface->info.ifinfo_index = set->index;
  443 
  444                         break;
  445                 }
  446                 case NGM_NETFLOW_SETTIMEOUTS:
  447                 {
  448                         struct ng_netflow_settimeouts *set;
  449 
  450                         if (msg->header.arglen != sizeof(struct ng_netflow_settimeouts))
  451                                 ERROUT(EINVAL);
  452 
  453                         set = (struct ng_netflow_settimeouts *)msg->data;
  454 
  455                         priv->info.nfinfo_inact_t = set->inactive_timeout;
  456                         priv->info.nfinfo_act_t = set->active_timeout;
  457 
  458                         break;
  459                 }
  460                 case NGM_NETFLOW_SETCONFIG:
  461                 {
  462                         struct ng_netflow_setconfig *set;
  463 
  464                         if (msg->header.arglen != sizeof(struct ng_netflow_setconfig))
  465                                 ERROUT(EINVAL);
  466 
  467                         set = (struct ng_netflow_setconfig *)msg->data;
  468 
  469                         if (set->iface >= NG_NETFLOW_MAXIFACES)
  470                                 ERROUT(EINVAL);
  471                         
  472                         priv->ifaces[set->iface].info.conf = set->conf;
  473         
  474                         break;
  475                 }
  476                 case NGM_NETFLOW_SETTEMPLATE:
  477                 {
  478                         struct ng_netflow_settemplate *set;
  479 
  480                         if (msg->header.arglen != sizeof(struct ng_netflow_settemplate))
  481                                 ERROUT(EINVAL);
  482 
  483                         set = (struct ng_netflow_settemplate *)msg->data;
  484 
  485                         priv->templ_packets = set->packets;
  486                         priv->templ_time = set->time;
  487 
  488                         break;
  489                 }
  490                 case NGM_NETFLOW_SETMTU:
  491                 {
  492                         struct ng_netflow_setmtu *set;
  493 
  494                         if (msg->header.arglen != sizeof(struct ng_netflow_setmtu))
  495                                 ERROUT(EINVAL);
  496 
  497                         set = (struct ng_netflow_setmtu *)msg->data;
  498                         if ((set->mtu < MIN_MTU) || (set->mtu > MAX_MTU))
  499                                 ERROUT(EINVAL);
  500 
  501                         priv->mtu = set->mtu;
  502 
  503                         break;
  504                 }
  505                 case NGM_NETFLOW_SHOW:
  506                 {
  507                         if (msg->header.arglen != sizeof(struct ngnf_show_header))
  508                                 ERROUT(EINVAL);
  509 
  510                         NG_MKRESPONSE(resp, msg, NGRESP_SIZE, M_NOWAIT);
  511 
  512                         if (!resp)
  513                                 ERROUT(ENOMEM);
  514 
  515                         error = ng_netflow_flow_show(priv,
  516                             (struct ngnf_show_header *)msg->data,
  517                             (struct ngnf_show_header *)resp->data);
  518 
  519                         if (error)
  520                                 NG_FREE_MSG(resp);
  521 
  522                         break;
  523                 }
  524                 default:
  525                         ERROUT(EINVAL);         /* unknown command */
  526                         break;
  527                 }
  528                 break;
  529         default:
  530                 ERROUT(EINVAL);         /* incorrect cookie */
  531                 break;
  532         }
  533 
  534         /*
  535          * Take care of synchronous response, if any.
  536          * Free memory and return.
  537          */
  538 done:
  539         NG_RESPOND_MSG(error, node, item, resp);
  540         NG_FREE_MSG(msg);
  541 
  542         return (error);
  543 }
  544 
  545 /* Receive data on hook. */
  546 static int
  547 ng_netflow_rcvdata (hook_p hook, item_p item)
  548 {
  549         const node_p node = NG_HOOK_NODE(hook);
  550         const priv_p priv = NG_NODE_PRIVATE(node);
  551         const iface_p iface = NG_HOOK_PRIVATE(hook);
  552         hook_p out;
  553         struct mbuf *m = NULL, *m_old = NULL;
  554         struct ip *ip = NULL;
  555         struct ip6_hdr *ip6 = NULL;
  556         struct m_tag *mtag;
  557         int pullup_len = 0, off;
  558         uint8_t upper_proto = 0, is_frag = 0;
  559         int error = 0, bypass = 0, acct = 0;
  560         unsigned int src_if_index;
  561         caddr_t upper_ptr = NULL;
  562         fib_export_p fe;        
  563         uint32_t fib;
  564 
  565         if ((hook == priv->export) || (hook == priv->export9)) {
  566                 /*
  567                  * Data arrived on export hook.
  568                  * This must not happen.
  569                  */
  570                 log(LOG_ERR, "ng_netflow: incoming data on export hook!\n");
  571                 ERROUT(EINVAL);
  572         };
  573 
  574         if (hook == iface->hook) {
  575                 if ((iface->info.conf & NG_NETFLOW_CONF_INGRESS) == 0)
  576                         bypass = 1;
  577                 out = iface->out;
  578         } else if (hook == iface->out) {
  579                 if ((iface->info.conf & NG_NETFLOW_CONF_EGRESS) == 0)
  580                         bypass = 1;
  581                 out = iface->hook;
  582         } else
  583                 ERROUT(EINVAL);
  584 
  585         if ((!bypass) &&
  586             (iface->info.conf & (NG_NETFLOW_CONF_ONCE | NG_NETFLOW_CONF_THISONCE))) {
  587                 mtag = m_tag_locate(NGI_M(item), MTAG_NETFLOW,
  588                     MTAG_NETFLOW_CALLED, NULL);
  589                 while (mtag != NULL) {
  590                         if ((iface->info.conf & NG_NETFLOW_CONF_ONCE) ||
  591                             ((ng_ID_t *)(mtag + 1))[0] == NG_NODE_ID(node)) {
  592                                 bypass = 1;
  593                                 break;
  594                         }
  595                         mtag = m_tag_locate(NGI_M(item), MTAG_NETFLOW,
  596                             MTAG_NETFLOW_CALLED, mtag);
  597                 }
  598         }
  599         
  600         if (bypass) {
  601                 if (out == NULL)
  602                         ERROUT(ENOTCONN);
  603 
  604                 NG_FWD_ITEM_HOOK(error, item, out);
  605                 return (error);
  606         }
  607         
  608         if (iface->info.conf & (NG_NETFLOW_CONF_ONCE | NG_NETFLOW_CONF_THISONCE)) {
  609                 mtag = m_tag_alloc(MTAG_NETFLOW, MTAG_NETFLOW_CALLED,
  610                     sizeof(ng_ID_t), M_NOWAIT);
  611                 if (mtag) {
  612                         ((ng_ID_t *)(mtag + 1))[0] = NG_NODE_ID(node);
  613                         m_tag_prepend(NGI_M(item), mtag);
  614                 }
  615         }
  616 
  617         NGI_GET_M(item, m);
  618         m_old = m;
  619 
  620         /* Increase counters. */
  621         iface->info.ifinfo_packets++;
  622 
  623         /*
  624          * Depending on interface data link type and packet contents
  625          * we pullup enough data, so that ng_netflow_flow_add() does not
  626          * need to know about mbuf at all. We keep current length of data
  627          * needed to be contiguous in pullup_len. mtod() is done at the
  628          * very end one more time, since m can had changed after pulluping.
  629          *
  630          * In case of unrecognized data we don't return error, but just
  631          * pass data to downstream hook, if it is available.
  632          */
  633 
  634 #define M_CHECK(length) do {                                    \
  635         pullup_len += length;                                   \
  636         if (((m)->m_pkthdr.len < (pullup_len)) ||               \
  637            ((pullup_len) > MHLEN)) {                            \
  638                 error = EINVAL;                                 \
  639                 goto bypass;                                    \
  640         }                                                       \
  641         if ((m)->m_len < (pullup_len) &&                        \
  642            (((m) = m_pullup((m),(pullup_len))) == NULL)) {      \
  643                 error = ENOBUFS;                                \
  644                 goto done;                                      \
  645         }                                                       \
  646 } while (0)
  647 
  648         switch (iface->info.ifinfo_dlt) {
  649         case DLT_EN10MB:        /* Ethernet */
  650             {
  651                 struct ether_header *eh;
  652                 uint16_t etype;
  653 
  654                 M_CHECK(sizeof(struct ether_header));
  655                 eh = mtod(m, struct ether_header *);
  656 
  657                 /* Make sure this is IP frame. */
  658                 etype = ntohs(eh->ether_type);
  659                 switch (etype) {
  660                 case ETHERTYPE_IP:
  661                         M_CHECK(sizeof(struct ip));
  662                         eh = mtod(m, struct ether_header *);
  663                         ip = (struct ip *)(eh + 1);
  664                         break;
  665 #ifdef INET6
  666                 case ETHERTYPE_IPV6:
  667                         /*
  668                          * m_pullup() called by M_CHECK() pullups
  669                          * kern.ipc.max_protohdr (default 60 bytes) which is enough
  670                          */
  671                         M_CHECK(sizeof(struct ip6_hdr));
  672                         eh = mtod(m, struct ether_header *);
  673                         ip6 = (struct ip6_hdr *)(eh + 1);
  674                         break;
  675 #endif
  676                 case ETHERTYPE_VLAN:
  677                     {
  678                         struct ether_vlan_header *evh;
  679 
  680                         M_CHECK(sizeof(struct ether_vlan_header) -
  681                             sizeof(struct ether_header));
  682                         evh = mtod(m, struct ether_vlan_header *);
  683                         etype = ntohs(evh->evl_proto);
  684 
  685                         if (etype == ETHERTYPE_IP) {
  686                                 M_CHECK(sizeof(struct ip));
  687                                 ip = (struct ip *)(evh + 1);
  688                                 break;
  689 #ifdef INET6
  690                         } else if (etype == ETHERTYPE_IPV6) {
  691                                 M_CHECK(sizeof(struct ip6_hdr));
  692                                 ip6 = (struct ip6_hdr *)(evh + 1);
  693                                 break;
  694 #endif
  695                         }
  696                     }
  697                 default:
  698                         goto bypass;    /* pass this frame */
  699                 }
  700                 break;
  701             }
  702         case DLT_RAW:           /* IP packets */
  703                 M_CHECK(sizeof(struct ip));
  704                 ip = mtod(m, struct ip *);
  705 #ifdef INET6
  706                 /* If INET6 is not defined IPv6 packets will be discarded in ng_netflow_flow_add() */
  707                 if (ip->ip_v == IP6VERSION) {
  708                         /* IPv6 packet */
  709                         ip = NULL;
  710                         M_CHECK(sizeof(struct ip6_hdr));
  711                         ip6 = mtod(m, struct ip6_hdr *);
  712                 }
  713 #endif
  714                 break;
  715         default:
  716                 goto bypass;
  717                 break;
  718         }
  719 
  720         off = pullup_len;
  721 
  722         if ((ip != NULL) && ((ip->ip_off & htons(IP_OFFMASK)) == 0)) {
  723                 if ((ip->ip_v != IPVERSION) ||
  724                     ((ip->ip_hl << 2) < sizeof(struct ip)))
  725                         goto bypass;
  726                 /*
  727                  * In case of IPv4 header with options, we haven't pulled
  728                  * up enough, yet.
  729                  */
  730                 M_CHECK((ip->ip_hl << 2) - sizeof(struct ip));
  731 
  732                 /* Save upper layer offset and proto */
  733                 off = pullup_len;
  734                 upper_proto = ip->ip_p;
  735 
  736                 /*
  737                  * XXX: in case of wrong upper layer header we will forward this packet
  738                  * but skip this record in netflow
  739                  */
  740                 switch (ip->ip_p) {
  741                 case IPPROTO_TCP:
  742                         M_CHECK(sizeof(struct tcphdr));
  743                         break;
  744                 case IPPROTO_UDP:
  745                         M_CHECK(sizeof(struct udphdr));
  746                         break;
  747                 case IPPROTO_SCTP:
  748                         M_CHECK(sizeof(struct sctphdr));
  749                         break;
  750                 }
  751         } else if (ip != NULL) {
  752                 /* Nothing to save except upper layer proto, since this is packet fragment */
  753                 is_frag = 1;
  754                 upper_proto = ip->ip_p;
  755                 if ((ip->ip_v != IPVERSION) ||
  756                     ((ip->ip_hl << 2) < sizeof(struct ip)))
  757                         goto bypass;
  758 #ifdef INET6
  759         } else if (ip6 != NULL) {
  760                 /* Check if we can export */
  761                 if (priv->export9 == NULL)
  762                         goto bypass;
  763 
  764                 /* Loop thru IPv6 extended headers to get upper layer header / frag */
  765                 int cur = ip6->ip6_nxt, hdr_off = 0;
  766                 struct ip6_ext *ip6e;
  767                 struct ip6_frag *ip6f;
  768 
  769                 /* Save upper layer info */
  770                 off = pullup_len;
  771                 upper_proto = cur;
  772 
  773                 if ((ip6->ip6_vfc & IPV6_VERSION_MASK) != IPV6_VERSION)
  774                         goto bypass;
  775 
  776                 while (42) {
  777                         switch (cur) {
  778                         /*
  779                          * Same as in IPv4, we can forward 'bad' packet without accounting
  780                          */
  781                         case IPPROTO_TCP:
  782                                 M_CHECK(sizeof(struct tcphdr));
  783                                 goto loopend;
  784                         case IPPROTO_UDP:
  785                                 M_CHECK(sizeof(struct udphdr));
  786                                 goto loopend;
  787                         case IPPROTO_SCTP:
  788                                 M_CHECK(sizeof(struct sctphdr));
  789                                 goto loopend;
  790 
  791                         /* Loop until 'real' upper layer headers */
  792                         case IPPROTO_HOPOPTS:
  793                         case IPPROTO_ROUTING:
  794                         case IPPROTO_DSTOPTS:
  795                                 M_CHECK(sizeof(struct ip6_ext));
  796                                 ip6e = (struct ip6_ext *)(mtod(m, caddr_t) + off);
  797                                 upper_proto = ip6e->ip6e_nxt;
  798                                 hdr_off = (ip6e->ip6e_len + 1) << 3;
  799                                 break;
  800 
  801                         /* RFC4302, can be before DSTOPTS */
  802                         case IPPROTO_AH:
  803                                 M_CHECK(sizeof(struct ip6_ext));
  804                                 ip6e = (struct ip6_ext *)(mtod(m, caddr_t) + off);
  805                                 upper_proto = ip6e->ip6e_nxt;
  806                                 hdr_off = (ip6e->ip6e_len + 2) << 2;
  807                                 break;
  808 
  809                         case IPPROTO_FRAGMENT:
  810                                 M_CHECK(sizeof(struct ip6_frag));
  811                                 ip6f = (struct ip6_frag *)(mtod(m, caddr_t) + off);
  812                                 upper_proto = ip6f->ip6f_nxt;
  813                                 hdr_off = sizeof(struct ip6_frag);
  814                                 off += hdr_off;
  815                                 is_frag = 1;
  816                                 goto loopend;
  817 
  818 #if 0                           
  819                         case IPPROTO_NONE:
  820                                 goto loopend;
  821 #endif
  822                         /* Any unknow header (new extension or IPv6/IPv4 header for tunnels) */
  823                         default:
  824                                 goto loopend;
  825                         }
  826 
  827                         off += hdr_off;
  828                         cur = upper_proto;
  829                 }
  830 #endif
  831         }
  832 #undef  M_CHECK
  833 
  834 #ifdef INET6
  835 loopend:
  836 #endif
  837         /* Just in case of real reallocation in M_CHECK() / m_pullup() */
  838         if (m != m_old) {
  839                 atomic_fetchadd_32(&priv->info.nfinfo_realloc_mbuf, 1);
  840                 ip = NULL;
  841                 ip6 = NULL;
  842                 switch (iface->info.ifinfo_dlt) {
  843                 case DLT_EN10MB:        /* Ethernet */
  844                     {
  845                         struct ether_header *eh;
  846         
  847                         eh = mtod(m, struct ether_header *);
  848                         switch (ntohs(eh->ether_type)) {
  849                         case ETHERTYPE_IP:
  850                                 ip = (struct ip *)(eh + 1);
  851                                 break;
  852 #ifdef INET6
  853                         case ETHERTYPE_IPV6:
  854                                 ip6 = (struct ip6_hdr *)(eh + 1);
  855                                 break;
  856 #endif
  857                         case ETHERTYPE_VLAN:
  858                             {
  859                                 struct ether_vlan_header *evh;
  860         
  861                                 evh = mtod(m, struct ether_vlan_header *);
  862                                 if (ntohs(evh->evl_proto) == ETHERTYPE_IP) {
  863                                         ip = (struct ip *)(evh + 1);
  864                                         break;
  865 #ifdef INET6
  866                                 } else if (ntohs(evh->evl_proto) == ETHERTYPE_IPV6) {
  867                                         ip6 = (struct ip6_hdr *)(evh + 1);
  868                                         break;
  869 #endif                                  
  870                                 }
  871                             }
  872                         default:
  873                                 panic("ng_netflow entered deadcode");
  874                         }
  875                         break;
  876                     }
  877                 case DLT_RAW:           /* IP packets */
  878                         ip = mtod(m, struct ip *);
  879 #ifdef INET6                    
  880                         if (ip->ip_v == IP6VERSION) {
  881                                 /* IPv6 packet */
  882                                 ip = NULL;
  883                                 ip6 = mtod(m, struct ip6_hdr *);
  884                         }
  885 #endif                  
  886                         break;
  887                 default:
  888                         panic("ng_netflow entered deadcode");
  889                 }
  890         }
  891 
  892         upper_ptr = (caddr_t)(mtod(m, caddr_t) + off);
  893 
  894         /* Determine packet input interface. Prefer configured. */
  895         src_if_index = 0;
  896         if (hook == iface->out || iface->info.ifinfo_index == 0) {
  897                 if (m->m_pkthdr.rcvif != NULL)
  898                         src_if_index = m->m_pkthdr.rcvif->if_index;
  899         } else
  900                 src_if_index = iface->info.ifinfo_index;
  901         
  902         /* Check packet FIB */
  903         fib = M_GETFIB(m);
  904         if (fib >= RT_NUMFIBS) {
  905                 CTR2(KTR_NET, "ng_netflow_rcvdata(): packet fib %d is out of range of available fibs: 0 .. %d", fib, RT_NUMFIBS);
  906                 goto bypass;
  907         }
  908 
  909         if ((fe = priv_to_fib(priv, fib)) == NULL) {
  910                 /* Setup new FIB */
  911                 if (ng_netflow_fib_init(priv, fib) != 0) {
  912                         /* malloc() failed */
  913                         goto bypass;
  914                 }
  915 
  916                 fe = priv_to_fib(priv, fib);
  917         }
  918 
  919         if (ip != NULL)
  920                 error = ng_netflow_flow_add(priv, fe, ip, upper_ptr, upper_proto, is_frag, src_if_index);
  921 #ifdef INET6            
  922         else if (ip6 != NULL)
  923                 error = ng_netflow_flow6_add(priv, fe, ip6, upper_ptr, upper_proto, is_frag, src_if_index);
  924 #endif
  925         else
  926                 goto bypass;
  927         
  928         acct = 1;
  929 bypass:
  930         if (out != NULL) {
  931                 if (acct == 0) {
  932                         /* Accounting failure */
  933                         if (ip != NULL) {
  934                                 atomic_fetchadd_32(&priv->info.nfinfo_spackets, 1);
  935                                 priv->info.nfinfo_sbytes += m_length(m, NULL);
  936                         } else if (ip6 != NULL) {
  937                                 atomic_fetchadd_32(&priv->info.nfinfo_spackets6, 1);
  938                                 priv->info.nfinfo_sbytes6 += m_length(m, NULL);
  939                         }
  940                 }
  941 
  942                 /* XXX: error gets overwritten here */
  943                 NG_FWD_NEW_DATA(error, item, out, m);
  944                 return (error);
  945         }
  946 done:
  947         if (item)
  948                 NG_FREE_ITEM(item);
  949         if (m)
  950                 NG_FREE_M(m);
  951 
  952         return (error); 
  953 }
  954 
  955 /* We will be shut down in a moment */
  956 static int
  957 ng_netflow_close(node_p node)
  958 {
  959         const priv_p priv = NG_NODE_PRIVATE(node);
  960 
  961         callout_drain(&priv->exp_callout);
  962         ng_netflow_cache_flush(priv);
  963 
  964         return (0);
  965 }
  966 
  967 /* Do local shutdown processing. */
  968 static int
  969 ng_netflow_rmnode(node_p node)
  970 {
  971         const priv_p priv = NG_NODE_PRIVATE(node);
  972 
  973         NG_NODE_SET_PRIVATE(node, NULL);
  974         NG_NODE_UNREF(priv->node);
  975 
  976         free(priv, M_NETGRAPH);
  977 
  978         return (0);
  979 }
  980 
  981 /* Hook disconnection. */
  982 static int
  983 ng_netflow_disconnect(hook_p hook)
  984 {
  985         node_p node = NG_HOOK_NODE(hook);
  986         priv_p priv = NG_NODE_PRIVATE(node);
  987         iface_p iface = NG_HOOK_PRIVATE(hook);
  988 
  989         if (iface != NULL) {
  990                 if (iface->hook == hook)
  991                         iface->hook = NULL;
  992                 if (iface->out == hook)
  993                         iface->out = NULL;
  994         }
  995 
  996         /* if export hook disconnected stop running expire(). */
  997         if (hook == priv->export) {
  998                 if (priv->export9 == NULL)
  999                         callout_drain(&priv->exp_callout);
 1000                 priv->export = NULL;
 1001         }
 1002 
 1003         if (hook == priv->export9) {
 1004                 if (priv->export == NULL)
 1005                         callout_drain(&priv->exp_callout);
 1006                 priv->export9 = NULL;
 1007         }
 1008 
 1009         /* Removal of the last link destroys the node. */
 1010         if (NG_NODE_NUMHOOKS(node) == 0)
 1011                 ng_rmnode_self(node);
 1012 
 1013         return (0);
 1014 }

Cache object: 73f64af77b8b87d01bbbc81e27894d83


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