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

Cache object: a24352e15193b7533ec034f16d1a8fec


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