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

Cache object: 793685680681d7749453541c4b5f2879


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