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: releng/12.0/sys/netgraph/netflow/ng_netflow.c 326272 2017-11-27 15:23:17Z pfg $");
   35 
   36 #include "opt_inet6.h"
   37 #include "opt_route.h"
   38 
   39 #include <sys/param.h>
   40 #include <sys/systm.h>
   41 #include <sys/counter.h>
   42 #include <sys/kernel.h>
   43 #include <sys/ktr.h>
   44 #include <sys/limits.h>
   45 #include <sys/malloc.h>
   46 #include <sys/mbuf.h>
   47 #include <sys/socket.h>
   48 #include <sys/syslog.h>
   49 #include <sys/ctype.h>
   50 #include <vm/uma.h>
   51 
   52 #include <net/if.h>
   53 #include <net/ethernet.h>
   54 #include <net/route.h>
   55 #include <net/if_arp.h>
   56 #include <net/if_var.h>
   57 #include <net/if_vlan_var.h>
   58 #include <net/bpf.h>
   59 #include <netinet/in.h>
   60 #include <netinet/in_systm.h>
   61 #include <netinet/ip.h>
   62 #include <netinet/ip6.h>
   63 #include <netinet/tcp.h>
   64 #include <netinet/udp.h>
   65 #include <netinet/sctp.h>
   66 
   67 #include <netgraph/ng_message.h>
   68 #include <netgraph/ng_parse.h>
   69 #include <netgraph/netgraph.h>
   70 #include <netgraph/netflow/netflow.h>
   71 #include <netgraph/netflow/netflow_v9.h>
   72 #include <netgraph/netflow/ng_netflow.h>
   73 
   74 /* Netgraph methods */
   75 static ng_constructor_t ng_netflow_constructor;
   76 static ng_rcvmsg_t      ng_netflow_rcvmsg;
   77 static ng_close_t       ng_netflow_close;
   78 static ng_shutdown_t    ng_netflow_rmnode;
   79 static ng_newhook_t     ng_netflow_newhook;
   80 static ng_rcvdata_t     ng_netflow_rcvdata;
   81 static ng_disconnect_t  ng_netflow_disconnect;
   82 
   83 /* Parse type for struct ng_netflow_info */
   84 static const struct ng_parse_struct_field ng_netflow_info_type_fields[]
   85         = NG_NETFLOW_INFO_TYPE;
   86 static const struct ng_parse_type ng_netflow_info_type = {
   87         &ng_parse_struct_type,
   88         &ng_netflow_info_type_fields
   89 };
   90 
   91 /*  Parse type for struct ng_netflow_ifinfo */
   92 static const struct ng_parse_struct_field ng_netflow_ifinfo_type_fields[]
   93         = NG_NETFLOW_IFINFO_TYPE;
   94 static const struct ng_parse_type ng_netflow_ifinfo_type = {
   95         &ng_parse_struct_type,
   96         &ng_netflow_ifinfo_type_fields
   97 };
   98 
   99 /* Parse type for struct ng_netflow_setdlt */
  100 static const struct ng_parse_struct_field ng_netflow_setdlt_type_fields[]
  101         = NG_NETFLOW_SETDLT_TYPE;
  102 static const struct ng_parse_type ng_netflow_setdlt_type = {
  103         &ng_parse_struct_type,
  104         &ng_netflow_setdlt_type_fields
  105 };
  106 
  107 /* Parse type for ng_netflow_setifindex */
  108 static const struct ng_parse_struct_field ng_netflow_setifindex_type_fields[]
  109         = NG_NETFLOW_SETIFINDEX_TYPE;
  110 static const struct ng_parse_type ng_netflow_setifindex_type = {
  111         &ng_parse_struct_type,
  112         &ng_netflow_setifindex_type_fields
  113 };
  114 
  115 /* Parse type for ng_netflow_settimeouts */
  116 static const struct ng_parse_struct_field ng_netflow_settimeouts_type_fields[]
  117         = NG_NETFLOW_SETTIMEOUTS_TYPE;
  118 static const struct ng_parse_type ng_netflow_settimeouts_type = {
  119         &ng_parse_struct_type,
  120         &ng_netflow_settimeouts_type_fields
  121 };
  122 
  123 /* Parse type for ng_netflow_setconfig */
  124 static const struct ng_parse_struct_field ng_netflow_setconfig_type_fields[]
  125         = NG_NETFLOW_SETCONFIG_TYPE;
  126 static const struct ng_parse_type ng_netflow_setconfig_type = {
  127         &ng_parse_struct_type,
  128         &ng_netflow_setconfig_type_fields
  129 };
  130 
  131 /* Parse type for ng_netflow_settemplate */
  132 static const struct ng_parse_struct_field ng_netflow_settemplate_type_fields[]
  133         = NG_NETFLOW_SETTEMPLATE_TYPE;
  134 static const struct ng_parse_type ng_netflow_settemplate_type = {
  135         &ng_parse_struct_type,
  136         &ng_netflow_settemplate_type_fields
  137 };
  138 
  139 /* Parse type for ng_netflow_setmtu */
  140 static const struct ng_parse_struct_field ng_netflow_setmtu_type_fields[]
  141         = NG_NETFLOW_SETMTU_TYPE;
  142 static const struct ng_parse_type ng_netflow_setmtu_type = {
  143         &ng_parse_struct_type,
  144         &ng_netflow_setmtu_type_fields
  145 };
  146 
  147 /* Parse type for struct ng_netflow_v9info */
  148 static const struct ng_parse_struct_field ng_netflow_v9info_type_fields[]
  149         = NG_NETFLOW_V9INFO_TYPE;
  150 static const struct ng_parse_type ng_netflow_v9info_type = {
  151         &ng_parse_struct_type,
  152         &ng_netflow_v9info_type_fields
  153 };
  154 
  155 /* List of commands and how to convert arguments to/from ASCII */
  156 static const struct ng_cmdlist ng_netflow_cmds[] = {
  157        {
  158          NGM_NETFLOW_COOKIE,
  159          NGM_NETFLOW_INFO,
  160          "info",
  161          NULL,
  162          &ng_netflow_info_type
  163        },
  164        {
  165         NGM_NETFLOW_COOKIE,
  166         NGM_NETFLOW_IFINFO,
  167         "ifinfo",
  168         &ng_parse_uint16_type,
  169         &ng_netflow_ifinfo_type
  170        },
  171        {
  172         NGM_NETFLOW_COOKIE,
  173         NGM_NETFLOW_SETDLT,
  174         "setdlt",
  175         &ng_netflow_setdlt_type,
  176         NULL
  177        },
  178        {
  179         NGM_NETFLOW_COOKIE,
  180         NGM_NETFLOW_SETIFINDEX,
  181         "setifindex",
  182         &ng_netflow_setifindex_type,
  183         NULL
  184        },
  185        {
  186         NGM_NETFLOW_COOKIE,
  187         NGM_NETFLOW_SETTIMEOUTS,
  188         "settimeouts",
  189         &ng_netflow_settimeouts_type,
  190         NULL
  191        },
  192        {
  193         NGM_NETFLOW_COOKIE,
  194         NGM_NETFLOW_SETCONFIG,
  195         "setconfig",
  196         &ng_netflow_setconfig_type,
  197         NULL
  198        },
  199        {
  200         NGM_NETFLOW_COOKIE,
  201         NGM_NETFLOW_SETTEMPLATE,
  202         "settemplate",
  203         &ng_netflow_settemplate_type,
  204         NULL
  205        },
  206        {
  207         NGM_NETFLOW_COOKIE,
  208         NGM_NETFLOW_SETMTU,
  209         "setmtu",
  210         &ng_netflow_setmtu_type,
  211         NULL
  212        },
  213        {
  214          NGM_NETFLOW_COOKIE,
  215          NGM_NETFLOW_V9INFO,
  216          "v9info",
  217          NULL,
  218          &ng_netflow_v9info_type
  219        },
  220        { 0 }
  221 };
  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 
  344                 if (priv->export != NULL)
  345                         return (EISCONN);
  346 
  347                 /* Netflow version 5 supports 32-bit counters only */
  348                 if (CNTR_MAX == UINT64_MAX)
  349                         return (EINVAL);
  350 
  351                 priv->export = hook;
  352 
  353                 /* Exporter is ready. Let's schedule expiry. */
  354                 callout_reset(&priv->exp_callout, (1*hz), &ng_netflow_expire,
  355                     (void *)priv);
  356         } else if (strcmp(name, NG_NETFLOW_HOOK_EXPORT9) == 0) {
  357 
  358                 if (priv->export9 != NULL)
  359                         return (EISCONN);
  360 
  361                 priv->export9 = hook;
  362 
  363                 /* Exporter is ready. Let's schedule expiry. */
  364                 callout_reset(&priv->exp_callout, (1*hz), &ng_netflow_expire,
  365                     (void *)priv);
  366         } else
  367                 return (EINVAL);
  368 
  369         return (0);
  370 }
  371 
  372 /* Get a netgraph control message. */
  373 static int
  374 ng_netflow_rcvmsg (node_p node, item_p item, hook_p lasthook)
  375 {
  376         const priv_p priv = NG_NODE_PRIVATE(node);
  377         struct ng_mesg *resp = NULL;
  378         int error = 0;
  379         struct ng_mesg *msg;
  380 
  381         NGI_GET_MSG(item, msg);
  382 
  383         /* Deal with message according to cookie and command */
  384         switch (msg->header.typecookie) {
  385         case NGM_NETFLOW_COOKIE:
  386                 switch (msg->header.cmd) {
  387                 case NGM_NETFLOW_INFO:
  388                     {
  389                         struct ng_netflow_info *i;
  390 
  391                         NG_MKRESPONSE(resp, msg, sizeof(struct ng_netflow_info),
  392                             M_NOWAIT);
  393                         i = (struct ng_netflow_info *)resp->data;
  394                         ng_netflow_copyinfo(priv, i);
  395 
  396                         break;
  397                     }
  398                 case NGM_NETFLOW_IFINFO:
  399                     {
  400                         struct ng_netflow_ifinfo *i;
  401                         const uint16_t *index;
  402 
  403                         if (msg->header.arglen != sizeof(uint16_t))
  404                                  ERROUT(EINVAL);
  405 
  406                         index  = (uint16_t *)msg->data;
  407                         if (*index >= NG_NETFLOW_MAXIFACES)
  408                                 ERROUT(EINVAL);
  409 
  410                         /* connected iface? */
  411                         if (priv->ifaces[*index].hook == NULL)
  412                                  ERROUT(EINVAL);
  413 
  414                         NG_MKRESPONSE(resp, msg,
  415                              sizeof(struct ng_netflow_ifinfo), M_NOWAIT);
  416                         i = (struct ng_netflow_ifinfo *)resp->data;
  417                         memcpy((void *)i, (void *)&priv->ifaces[*index].info,
  418                             sizeof(priv->ifaces[*index].info));
  419 
  420                         break;
  421                     }
  422                 case NGM_NETFLOW_SETDLT:
  423                     {
  424                         struct ng_netflow_setdlt *set;
  425                         struct ng_netflow_iface *iface;
  426 
  427                         if (msg->header.arglen !=
  428                             sizeof(struct ng_netflow_setdlt))
  429                                 ERROUT(EINVAL);
  430 
  431                         set = (struct ng_netflow_setdlt *)msg->data;
  432                         if (set->iface >= NG_NETFLOW_MAXIFACES)
  433                                 ERROUT(EINVAL);
  434                         iface = &priv->ifaces[set->iface];
  435 
  436                         /* connected iface? */
  437                         if (iface->hook == NULL)
  438                                 ERROUT(EINVAL);
  439 
  440                         switch (set->dlt) {
  441                         case    DLT_EN10MB:
  442                                 iface->info.ifinfo_dlt = DLT_EN10MB;
  443                                 break;
  444                         case    DLT_RAW:
  445                                 iface->info.ifinfo_dlt = DLT_RAW;
  446                                 break;
  447                         default:
  448                                 ERROUT(EINVAL);
  449                         }
  450                         break;
  451                     }
  452                 case NGM_NETFLOW_SETIFINDEX:
  453                     {
  454                         struct ng_netflow_setifindex *set;
  455                         struct ng_netflow_iface *iface;
  456 
  457                         if (msg->header.arglen !=
  458                             sizeof(struct ng_netflow_setifindex))
  459                                 ERROUT(EINVAL);
  460 
  461                         set = (struct ng_netflow_setifindex *)msg->data;
  462                         if (set->iface >= NG_NETFLOW_MAXIFACES)
  463                                 ERROUT(EINVAL);
  464                         iface = &priv->ifaces[set->iface];
  465 
  466                         /* connected iface? */
  467                         if (iface->hook == NULL)
  468                                 ERROUT(EINVAL);
  469 
  470                         iface->info.ifinfo_index = set->index;
  471 
  472                         break;
  473                     }
  474                 case NGM_NETFLOW_SETTIMEOUTS:
  475                     {
  476                         struct ng_netflow_settimeouts *set;
  477 
  478                         if (msg->header.arglen !=
  479                             sizeof(struct ng_netflow_settimeouts))
  480                                 ERROUT(EINVAL);
  481 
  482                         set = (struct ng_netflow_settimeouts *)msg->data;
  483 
  484                         priv->nfinfo_inact_t = set->inactive_timeout;
  485                         priv->nfinfo_act_t = set->active_timeout;
  486 
  487                         break;
  488                     }
  489                 case NGM_NETFLOW_SETCONFIG:
  490                     {
  491                         struct ng_netflow_setconfig *set;
  492 
  493                         if (msg->header.arglen !=
  494                             sizeof(struct ng_netflow_setconfig))
  495                                 ERROUT(EINVAL);
  496 
  497                         set = (struct ng_netflow_setconfig *)msg->data;
  498 
  499                         if (set->iface >= NG_NETFLOW_MAXIFACES)
  500                                 ERROUT(EINVAL);
  501                         
  502                         priv->ifaces[set->iface].info.conf = set->conf;
  503         
  504                         break;
  505                     }
  506                 case NGM_NETFLOW_SETTEMPLATE:
  507                     {
  508                         struct ng_netflow_settemplate *set;
  509 
  510                         if (msg->header.arglen !=
  511                             sizeof(struct ng_netflow_settemplate))
  512                                 ERROUT(EINVAL);
  513 
  514                         set = (struct ng_netflow_settemplate *)msg->data;
  515 
  516                         priv->templ_packets = set->packets;
  517                         priv->templ_time = set->time;
  518 
  519                         break;
  520                     }
  521                 case NGM_NETFLOW_SETMTU:
  522                     {
  523                         struct ng_netflow_setmtu *set;
  524 
  525                         if (msg->header.arglen !=
  526                             sizeof(struct ng_netflow_setmtu))
  527                                 ERROUT(EINVAL);
  528 
  529                         set = (struct ng_netflow_setmtu *)msg->data;
  530                         if ((set->mtu < MIN_MTU) || (set->mtu > MAX_MTU))
  531                                 ERROUT(EINVAL);
  532 
  533                         priv->mtu = set->mtu;
  534 
  535                         break;
  536                     }
  537                 case NGM_NETFLOW_SHOW:
  538                         if (msg->header.arglen !=
  539                             sizeof(struct ngnf_show_header))
  540                                 ERROUT(EINVAL);
  541 
  542                         NG_MKRESPONSE(resp, msg, NGRESP_SIZE, M_NOWAIT);
  543 
  544                         if (!resp)
  545                                 ERROUT(ENOMEM);
  546 
  547                         error = ng_netflow_flow_show(priv,
  548                             (struct ngnf_show_header *)msg->data,
  549                             (struct ngnf_show_header *)resp->data);
  550 
  551                         if (error)
  552                                 NG_FREE_MSG(resp);
  553 
  554                         break;
  555                 case NGM_NETFLOW_V9INFO:
  556                     {
  557                         struct ng_netflow_v9info *i;
  558 
  559                         NG_MKRESPONSE(resp, msg,
  560                             sizeof(struct ng_netflow_v9info), M_NOWAIT);
  561                         i = (struct ng_netflow_v9info *)resp->data;
  562                         ng_netflow_copyv9info(priv, i);
  563 
  564                         break;
  565                     }
  566                 default:
  567                         ERROUT(EINVAL);         /* unknown command */
  568                         break;
  569                 }
  570                 break;
  571         default:
  572                 ERROUT(EINVAL);         /* incorrect cookie */
  573                 break;
  574         }
  575 
  576         /*
  577          * Take care of synchronous response, if any.
  578          * Free memory and return.
  579          */
  580 done:
  581         NG_RESPOND_MSG(error, node, item, resp);
  582         NG_FREE_MSG(msg);
  583 
  584         return (error);
  585 }
  586 
  587 /* Receive data on hook. */
  588 static int
  589 ng_netflow_rcvdata (hook_p hook, item_p item)
  590 {
  591         const node_p node = NG_HOOK_NODE(hook);
  592         const priv_p priv = NG_NODE_PRIVATE(node);
  593         const iface_p iface = NG_HOOK_PRIVATE(hook);
  594         hook_p out;
  595         struct mbuf *m = NULL, *m_old = NULL;
  596         struct ip *ip = NULL;
  597         struct ip6_hdr *ip6 = NULL;
  598         struct m_tag *mtag;
  599         int pullup_len = 0, off;
  600         uint8_t acct = 0, bypass = 0, flags = 0, upper_proto = 0;
  601         int error = 0, l3_off = 0;
  602         unsigned int src_if_index;
  603         caddr_t upper_ptr = NULL;
  604         fib_export_p fe;        
  605         uint32_t fib;
  606 
  607         if ((hook == priv->export) || (hook == priv->export9)) {
  608                 /*
  609                  * Data arrived on export hook.
  610                  * This must not happen.
  611                  */
  612                 log(LOG_ERR, "ng_netflow: incoming data on export hook!\n");
  613                 ERROUT(EINVAL);
  614         }
  615 
  616         if (hook == iface->hook) {
  617                 if ((iface->info.conf & NG_NETFLOW_CONF_INGRESS) == 0)
  618                         bypass = 1;
  619                 out = iface->out;
  620         } else if (hook == iface->out) {
  621                 if ((iface->info.conf & NG_NETFLOW_CONF_EGRESS) == 0)
  622                         bypass = 1;
  623                 out = iface->hook;
  624         } else
  625                 ERROUT(EINVAL);
  626 
  627         if ((!bypass) && (iface->info.conf &
  628             (NG_NETFLOW_CONF_ONCE | NG_NETFLOW_CONF_THISONCE))) {
  629                 mtag = m_tag_locate(NGI_M(item), MTAG_NETFLOW,
  630                     MTAG_NETFLOW_CALLED, NULL);
  631                 while (mtag != NULL) {
  632                         if ((iface->info.conf & NG_NETFLOW_CONF_ONCE) ||
  633                             ((ng_ID_t *)(mtag + 1))[0] == NG_NODE_ID(node)) {
  634                                 bypass = 1;
  635                                 break;
  636                         }
  637                         mtag = m_tag_locate(NGI_M(item), MTAG_NETFLOW,
  638                             MTAG_NETFLOW_CALLED, mtag);
  639                 }
  640         }
  641         
  642         if (bypass) {
  643                 if (out == NULL)
  644                         ERROUT(ENOTCONN);
  645 
  646                 NG_FWD_ITEM_HOOK(error, item, out);
  647                 return (error);
  648         }
  649         
  650         if (iface->info.conf &
  651             (NG_NETFLOW_CONF_ONCE | NG_NETFLOW_CONF_THISONCE)) {
  652                 mtag = m_tag_alloc(MTAG_NETFLOW, MTAG_NETFLOW_CALLED,
  653                     sizeof(ng_ID_t), M_NOWAIT);
  654                 if (mtag) {
  655                         ((ng_ID_t *)(mtag + 1))[0] = NG_NODE_ID(node);
  656                         m_tag_prepend(NGI_M(item), mtag);
  657                 }
  658         }
  659 
  660         /* Import configuration flags related to flow creation */
  661         flags = iface->info.conf & NG_NETFLOW_FLOW_FLAGS;
  662 
  663         NGI_GET_M(item, m);
  664         m_old = m;
  665 
  666         /* Increase counters. */
  667         iface->info.ifinfo_packets++;
  668 
  669         /*
  670          * Depending on interface data link type and packet contents
  671          * we pullup enough data, so that ng_netflow_flow_add() does not
  672          * need to know about mbuf at all. We keep current length of data
  673          * needed to be contiguous in pullup_len. mtod() is done at the
  674          * very end one more time, since m can had changed after pulluping.
  675          *
  676          * In case of unrecognized data we don't return error, but just
  677          * pass data to downstream hook, if it is available.
  678          */
  679 
  680 #define M_CHECK(length) do {                                    \
  681         pullup_len += length;                                   \
  682         if (((m)->m_pkthdr.len < (pullup_len)) ||               \
  683            ((pullup_len) > MHLEN)) {                            \
  684                 error = EINVAL;                                 \
  685                 goto bypass;                                    \
  686         }                                                       \
  687         if ((m)->m_len < (pullup_len) &&                        \
  688            (((m) = m_pullup((m),(pullup_len))) == NULL)) {      \
  689                 error = ENOBUFS;                                \
  690                 goto done;                                      \
  691         }                                                       \
  692 } while (0)
  693 
  694         switch (iface->info.ifinfo_dlt) {
  695         case DLT_EN10MB:        /* Ethernet */
  696             {
  697                 struct ether_header *eh;
  698                 uint16_t etype;
  699 
  700                 M_CHECK(sizeof(struct ether_header));
  701                 eh = mtod(m, struct ether_header *);
  702 
  703                 /* Make sure this is IP frame. */
  704                 etype = ntohs(eh->ether_type);
  705                 switch (etype) {
  706                 case ETHERTYPE_IP:
  707                         M_CHECK(sizeof(struct ip));
  708                         eh = mtod(m, struct ether_header *);
  709                         ip = (struct ip *)(eh + 1);
  710                         l3_off = sizeof(struct ether_header);
  711                         break;
  712 #ifdef INET6
  713                 case ETHERTYPE_IPV6:
  714                         /*
  715                          * m_pullup() called by M_CHECK() pullups
  716                          * kern.ipc.max_protohdr (default 60 bytes)
  717                          * which is enough.
  718                          */
  719                         M_CHECK(sizeof(struct ip6_hdr));
  720                         eh = mtod(m, struct ether_header *);
  721                         ip6 = (struct ip6_hdr *)(eh + 1);
  722                         l3_off = sizeof(struct ether_header);
  723                         break;
  724 #endif
  725                 case ETHERTYPE_VLAN:
  726                     {
  727                         struct ether_vlan_header *evh;
  728 
  729                         M_CHECK(sizeof(struct ether_vlan_header) -
  730                             sizeof(struct ether_header));
  731                         evh = mtod(m, struct ether_vlan_header *);
  732                         etype = ntohs(evh->evl_proto);
  733                         l3_off = sizeof(struct ether_vlan_header);
  734 
  735                         if (etype == ETHERTYPE_IP) {
  736                                 M_CHECK(sizeof(struct ip));
  737                                 ip = (struct ip *)(evh + 1);
  738                                 break;
  739 #ifdef INET6
  740                         } else if (etype == ETHERTYPE_IPV6) {
  741                                 M_CHECK(sizeof(struct ip6_hdr));
  742                                 ip6 = (struct ip6_hdr *)(evh + 1);
  743                                 break;
  744 #endif
  745                         }
  746                     }
  747                 default:
  748                         goto bypass;    /* pass this frame */
  749                 }
  750                 break;
  751             }
  752         case DLT_RAW:           /* IP packets */
  753                 M_CHECK(sizeof(struct ip));
  754                 ip = mtod(m, struct ip *);
  755                 /* l3_off is already zero */
  756 #ifdef INET6
  757                 /*
  758                  * If INET6 is not defined IPv6 packets
  759                  * will be discarded in ng_netflow_flow_add().
  760                  */
  761                 if (ip->ip_v == IP6VERSION) {
  762                         ip = NULL;
  763                         M_CHECK(sizeof(struct ip6_hdr) - sizeof(struct ip));
  764                         ip6 = mtod(m, struct ip6_hdr *);
  765                 }
  766 #endif
  767                 break;
  768         default:
  769                 goto bypass;
  770                 break;
  771         }
  772 
  773         off = pullup_len;
  774 
  775         if ((ip != NULL) && ((ip->ip_off & htons(IP_OFFMASK)) == 0)) {
  776                 if ((ip->ip_v != IPVERSION) ||
  777                     ((ip->ip_hl << 2) < sizeof(struct ip)))
  778                         goto bypass;
  779                 /*
  780                  * In case of IPv4 header with options, we haven't pulled
  781                  * up enough, yet.
  782                  */
  783                 M_CHECK((ip->ip_hl << 2) - sizeof(struct ip));
  784 
  785                 /* Save upper layer offset and proto */
  786                 off = pullup_len;
  787                 upper_proto = ip->ip_p;
  788 
  789                 /*
  790                  * XXX: in case of wrong upper layer header we will
  791                  * forward this packet but skip this record in netflow.
  792                  */
  793                 switch (ip->ip_p) {
  794                 case IPPROTO_TCP:
  795                         M_CHECK(sizeof(struct tcphdr));
  796                         break;
  797                 case IPPROTO_UDP:
  798                         M_CHECK(sizeof(struct udphdr));
  799                         break;
  800                 case IPPROTO_SCTP:
  801                         M_CHECK(sizeof(struct sctphdr));
  802                         break;
  803                 }
  804         } else if (ip != NULL) {
  805                 /*
  806                  * Nothing to save except upper layer proto,
  807                  * since this is a packet fragment.
  808                  */
  809                 flags |= NG_NETFLOW_IS_FRAG;
  810                 upper_proto = ip->ip_p;
  811                 if ((ip->ip_v != IPVERSION) ||
  812                     ((ip->ip_hl << 2) < sizeof(struct ip)))
  813                         goto bypass;
  814 #ifdef INET6
  815         } else if (ip6 != NULL) {
  816                 int cur = ip6->ip6_nxt, hdr_off = 0;
  817                 struct ip6_ext *ip6e;
  818                 struct ip6_frag *ip6f;
  819 
  820                 if (priv->export9 == NULL)
  821                         goto bypass;
  822 
  823                 /* Save upper layer info. */
  824                 off = pullup_len;
  825                 upper_proto = cur;
  826 
  827                 if ((ip6->ip6_vfc & IPV6_VERSION_MASK) != IPV6_VERSION)
  828                         goto bypass;
  829 
  830                 /*
  831                  * Loop through IPv6 extended headers to get upper
  832                  * layer header / frag.
  833                  */
  834                 for (;;) {
  835                         switch (cur) {
  836                         /*
  837                          * Same as in IPv4, we can forward a 'bad'
  838                          * packet without accounting.
  839                          */
  840                         case IPPROTO_TCP:
  841                                 M_CHECK(sizeof(struct tcphdr));
  842                                 goto loopend;
  843                         case IPPROTO_UDP:
  844                                 M_CHECK(sizeof(struct udphdr));
  845                                 goto loopend;
  846                         case IPPROTO_SCTP:
  847                                 M_CHECK(sizeof(struct sctphdr));
  848                                 goto loopend;
  849 
  850                         /* Loop until 'real' upper layer headers */
  851                         case IPPROTO_HOPOPTS:
  852                         case IPPROTO_ROUTING:
  853                         case IPPROTO_DSTOPTS:
  854                                 M_CHECK(sizeof(struct ip6_ext));
  855                                 ip6e = (struct ip6_ext *)(mtod(m, caddr_t) +
  856                                     off);
  857                                 upper_proto = ip6e->ip6e_nxt;
  858                                 hdr_off = (ip6e->ip6e_len + 1) << 3;
  859                                 break;
  860 
  861                         /* RFC4302, can be before DSTOPTS */
  862                         case IPPROTO_AH:
  863                                 M_CHECK(sizeof(struct ip6_ext));
  864                                 ip6e = (struct ip6_ext *)(mtod(m, caddr_t) +
  865                                     off);
  866                                 upper_proto = ip6e->ip6e_nxt;
  867                                 hdr_off = (ip6e->ip6e_len + 2) << 2;
  868                                 break;
  869 
  870                         case IPPROTO_FRAGMENT:
  871                                 M_CHECK(sizeof(struct ip6_frag));
  872                                 ip6f = (struct ip6_frag *)(mtod(m, caddr_t) +
  873                                     off);
  874                                 upper_proto = ip6f->ip6f_nxt;
  875                                 hdr_off = sizeof(struct ip6_frag);
  876                                 off += hdr_off;
  877                                 flags |= NG_NETFLOW_IS_FRAG;
  878                                 goto loopend;
  879 
  880 #if 0                           
  881                         case IPPROTO_NONE:
  882                                 goto loopend;
  883 #endif
  884                         /*
  885                          * Any unknown header (new extension or IPv6/IPv4
  886                          * header for tunnels) ends loop.
  887                          */
  888                         default:
  889                                 goto loopend;
  890                         }
  891 
  892                         off += hdr_off;
  893                         cur = upper_proto;
  894                 }
  895 #endif
  896         }
  897 #undef  M_CHECK
  898 
  899 #ifdef INET6
  900 loopend:
  901 #endif
  902         /* Just in case of real reallocation in M_CHECK() / m_pullup() */
  903         if (m != m_old) {
  904                 priv->nfinfo_realloc_mbuf++;
  905                 /* Restore ip/ipv6 pointer */
  906                 if (ip != NULL)
  907                         ip = (struct ip *)(mtod(m, caddr_t) + l3_off);
  908                 else if (ip6 != NULL)
  909                         ip6 = (struct ip6_hdr *)(mtod(m, caddr_t) + l3_off);
  910         }
  911 
  912         upper_ptr = (caddr_t)(mtod(m, caddr_t) + off);
  913 
  914         /* Determine packet input interface. Prefer configured. */
  915         src_if_index = 0;
  916         if (hook == iface->out || iface->info.ifinfo_index == 0) {
  917                 if (m->m_pkthdr.rcvif != NULL)
  918                         src_if_index = m->m_pkthdr.rcvif->if_index;
  919         } else
  920                 src_if_index = iface->info.ifinfo_index;
  921         
  922         /* Check packet FIB */
  923         fib = M_GETFIB(m);
  924         if (fib >= priv->maxfibs) {
  925                 CTR2(KTR_NET, "ng_netflow_rcvdata(): packet fib %d is out of "
  926                     "range of available fibs: 0 .. %d",
  927                     fib, priv->maxfibs);
  928                 goto bypass;
  929         }
  930 
  931         if ((fe = priv_to_fib(priv, fib)) == NULL) {
  932                 /* Setup new FIB */
  933                 if (ng_netflow_fib_init(priv, fib) != 0) {
  934                         /* malloc() failed */
  935                         goto bypass;
  936                 }
  937 
  938                 fe = priv_to_fib(priv, fib);
  939         }
  940 
  941         if (ip != NULL)
  942                 error = ng_netflow_flow_add(priv, fe, ip, upper_ptr,
  943                     upper_proto, flags, src_if_index);
  944 #ifdef INET6            
  945         else if (ip6 != NULL)
  946                 error = ng_netflow_flow6_add(priv, fe, ip6, upper_ptr,
  947                     upper_proto, flags, src_if_index);
  948 #endif
  949         else
  950                 goto bypass;
  951         
  952         acct = 1;
  953 bypass:
  954         if (out != NULL) {
  955                 if (acct == 0) {
  956                         /* Accounting failure */
  957                         if (ip != NULL) {
  958                                 counter_u64_add(priv->nfinfo_spackets, 1);
  959                                 counter_u64_add(priv->nfinfo_sbytes,
  960                                     m->m_pkthdr.len);
  961                         } else if (ip6 != NULL) {
  962                                 counter_u64_add(priv->nfinfo_spackets6, 1);
  963                                 counter_u64_add(priv->nfinfo_sbytes6,
  964                                     m->m_pkthdr.len);
  965                         }
  966                 }
  967 
  968                 /* XXX: error gets overwritten here */
  969                 NG_FWD_NEW_DATA(error, item, out, m);
  970                 return (error);
  971         }
  972 done:
  973         if (item)
  974                 NG_FREE_ITEM(item);
  975         if (m)
  976                 NG_FREE_M(m);
  977 
  978         return (error); 
  979 }
  980 
  981 /* We will be shut down in a moment */
  982 static int
  983 ng_netflow_close(node_p node)
  984 {
  985         const priv_p priv = NG_NODE_PRIVATE(node);
  986 
  987         callout_drain(&priv->exp_callout);
  988         ng_netflow_cache_flush(priv);
  989 
  990         return (0);
  991 }
  992 
  993 /* Do local shutdown processing. */
  994 static int
  995 ng_netflow_rmnode(node_p node)
  996 {
  997         const priv_p priv = NG_NODE_PRIVATE(node);
  998 
  999         NG_NODE_SET_PRIVATE(node, NULL);
 1000         NG_NODE_UNREF(priv->node);
 1001 
 1002         free(priv->fib_data, M_NETGRAPH);
 1003         free(priv, M_NETGRAPH);
 1004 
 1005         return (0);
 1006 }
 1007 
 1008 /* Hook disconnection. */
 1009 static int
 1010 ng_netflow_disconnect(hook_p hook)
 1011 {
 1012         node_p node = NG_HOOK_NODE(hook);
 1013         priv_p priv = NG_NODE_PRIVATE(node);
 1014         iface_p iface = NG_HOOK_PRIVATE(hook);
 1015 
 1016         if (iface != NULL) {
 1017                 if (iface->hook == hook)
 1018                         iface->hook = NULL;
 1019                 if (iface->out == hook)
 1020                         iface->out = NULL;
 1021         }
 1022 
 1023         /* if export hook disconnected stop running expire(). */
 1024         if (hook == priv->export) {
 1025                 if (priv->export9 == NULL)
 1026                         callout_drain(&priv->exp_callout);
 1027                 priv->export = NULL;
 1028         }
 1029 
 1030         if (hook == priv->export9) {
 1031                 if (priv->export == NULL)
 1032                         callout_drain(&priv->exp_callout);
 1033                 priv->export9 = NULL;
 1034         }
 1035 
 1036         /* Removal of the last link destroys the node. */
 1037         if (NG_NODE_NUMHOOKS(node) == 0)
 1038                 ng_rmnode_self(node);
 1039 
 1040         return (0);
 1041 }

Cache object: 065ee1c834e07d092d4c6cd9d7878f1f


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