1135332Sglebius/*- 2219182Sglebius * Copyright (c) 2010-2011 Alexander V. Chernikov <melifaro@ipfw.ru> 3143923Sglebius * Copyright (c) 2004-2005 Gleb Smirnoff <glebius@FreeBSD.org> 4135332Sglebius * Copyright (c) 2001-2003 Roman V. Palagin <romanp@unshadow.net> 5135332Sglebius * All rights reserved. 6135332Sglebius * 7135332Sglebius * Redistribution and use in source and binary forms, with or without 8135332Sglebius * modification, are permitted provided that the following conditions 9135332Sglebius * are met: 10135332Sglebius * 1. Redistributions of source code must retain the above copyright 11135332Sglebius * notice, this list of conditions and the following disclaimer. 12135332Sglebius * 2. Redistributions in binary form must reproduce the above copyright 13135332Sglebius * notice, this list of conditions and the following disclaimer in the 14135332Sglebius * documentation and/or other materials provided with the distribution. 15135332Sglebius * 16135332Sglebius * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 17135332Sglebius * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 18135332Sglebius * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 19135332Sglebius * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 20135332Sglebius * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 21135332Sglebius * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 22135332Sglebius * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 23135332Sglebius * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 24135332Sglebius * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 25135332Sglebius * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 26135332Sglebius * SUCH DAMAGE. 27135332Sglebius * 28135332Sglebius * $SourceForge: ng_netflow.c,v 1.30 2004/09/05 11:37:43 glebius Exp $ 29135332Sglebius */ 30135332Sglebius 31260278Sdim#include <sys/cdefs.h> 32260278Sdim__FBSDID("$FreeBSD: releng/10.3/sys/netgraph/netflow/ng_netflow.c 260278 2014-01-04 19:04:53Z dim $"); 33135332Sglebius 34219182Sglebius#include "opt_inet6.h" 35219182Sglebius#include "opt_route.h" 36219182Sglebius 37135332Sglebius#include <sys/param.h> 38135332Sglebius#include <sys/systm.h> 39135332Sglebius#include <sys/kernel.h> 40167990Sglebius#include <sys/limits.h> 41135332Sglebius#include <sys/mbuf.h> 42135332Sglebius#include <sys/socket.h> 43140511Sglebius#include <sys/syslog.h> 44135332Sglebius#include <sys/ctype.h> 45135332Sglebius 46135332Sglebius#include <net/if.h> 47135332Sglebius#include <net/ethernet.h> 48219182Sglebius#include <net/route.h> 49135332Sglebius#include <net/if_arp.h> 50135332Sglebius#include <net/if_var.h> 51163247Sglebius#include <net/if_vlan_var.h> 52135332Sglebius#include <net/bpf.h> 53135332Sglebius#include <netinet/in.h> 54135332Sglebius#include <netinet/in_systm.h> 55135332Sglebius#include <netinet/ip.h> 56219182Sglebius#include <netinet/ip6.h> 57143923Sglebius#include <netinet/tcp.h> 58143923Sglebius#include <netinet/udp.h> 59219182Sglebius#include <netinet/sctp.h> 60135332Sglebius 61135332Sglebius#include <netgraph/ng_message.h> 62135332Sglebius#include <netgraph/ng_parse.h> 63135332Sglebius#include <netgraph/netgraph.h> 64135332Sglebius#include <netgraph/netflow/netflow.h> 65219182Sglebius#include <netgraph/netflow/netflow_v9.h> 66135332Sglebius#include <netgraph/netflow/ng_netflow.h> 67135332Sglebius 68135332Sglebius/* Netgraph methods */ 69135332Sglebiusstatic ng_constructor_t ng_netflow_constructor; 70135332Sglebiusstatic ng_rcvmsg_t ng_netflow_rcvmsg; 71135332Sglebiusstatic ng_close_t ng_netflow_close; 72135332Sglebiusstatic ng_shutdown_t ng_netflow_rmnode; 73135332Sglebiusstatic ng_newhook_t ng_netflow_newhook; 74135332Sglebiusstatic ng_rcvdata_t ng_netflow_rcvdata; 75135332Sglebiusstatic ng_disconnect_t ng_netflow_disconnect; 76135332Sglebius 77135332Sglebius/* Parse type for struct ng_netflow_info */ 78135332Sglebiusstatic const struct ng_parse_struct_field ng_netflow_info_type_fields[] 79135332Sglebius = NG_NETFLOW_INFO_TYPE; 80135332Sglebiusstatic const struct ng_parse_type ng_netflow_info_type = { 81135332Sglebius &ng_parse_struct_type, 82135332Sglebius &ng_netflow_info_type_fields 83135332Sglebius}; 84135332Sglebius 85135332Sglebius/* Parse type for struct ng_netflow_ifinfo */ 86135332Sglebiusstatic const struct ng_parse_struct_field ng_netflow_ifinfo_type_fields[] 87135332Sglebius = NG_NETFLOW_IFINFO_TYPE; 88135332Sglebiusstatic const struct ng_parse_type ng_netflow_ifinfo_type = { 89135332Sglebius &ng_parse_struct_type, 90135332Sglebius &ng_netflow_ifinfo_type_fields 91135332Sglebius}; 92135332Sglebius 93135332Sglebius/* Parse type for struct ng_netflow_setdlt */ 94135332Sglebiusstatic const struct ng_parse_struct_field ng_netflow_setdlt_type_fields[] 95135332Sglebius = NG_NETFLOW_SETDLT_TYPE; 96135332Sglebiusstatic const struct ng_parse_type ng_netflow_setdlt_type = { 97135332Sglebius &ng_parse_struct_type, 98135332Sglebius &ng_netflow_setdlt_type_fields 99135332Sglebius}; 100135332Sglebius 101135332Sglebius/* Parse type for ng_netflow_setifindex */ 102135332Sglebiusstatic const struct ng_parse_struct_field ng_netflow_setifindex_type_fields[] 103135332Sglebius = NG_NETFLOW_SETIFINDEX_TYPE; 104135332Sglebiusstatic const struct ng_parse_type ng_netflow_setifindex_type = { 105135332Sglebius &ng_parse_struct_type, 106135332Sglebius &ng_netflow_setifindex_type_fields 107135332Sglebius}; 108135332Sglebius 109135332Sglebius/* Parse type for ng_netflow_settimeouts */ 110135332Sglebiusstatic const struct ng_parse_struct_field ng_netflow_settimeouts_type_fields[] 111135332Sglebius = NG_NETFLOW_SETTIMEOUTS_TYPE; 112135332Sglebiusstatic const struct ng_parse_type ng_netflow_settimeouts_type = { 113135332Sglebius &ng_parse_struct_type, 114135332Sglebius &ng_netflow_settimeouts_type_fields 115135332Sglebius}; 116135332Sglebius 117183693Smav/* Parse type for ng_netflow_setconfig */ 118183693Smavstatic const struct ng_parse_struct_field ng_netflow_setconfig_type_fields[] 119183693Smav = NG_NETFLOW_SETCONFIG_TYPE; 120183693Smavstatic const struct ng_parse_type ng_netflow_setconfig_type = { 121183693Smav &ng_parse_struct_type, 122183693Smav &ng_netflow_setconfig_type_fields 123183693Smav}; 124183693Smav 125219182Sglebius/* Parse type for ng_netflow_settemplate */ 126219182Sglebiusstatic const struct ng_parse_struct_field ng_netflow_settemplate_type_fields[] 127219182Sglebius = NG_NETFLOW_SETTEMPLATE_TYPE; 128219182Sglebiusstatic const struct ng_parse_type ng_netflow_settemplate_type = { 129219182Sglebius &ng_parse_struct_type, 130219182Sglebius &ng_netflow_settemplate_type_fields 131219182Sglebius}; 132219182Sglebius 133219182Sglebius/* Parse type for ng_netflow_setmtu */ 134219182Sglebiusstatic const struct ng_parse_struct_field ng_netflow_setmtu_type_fields[] 135219182Sglebius = NG_NETFLOW_SETMTU_TYPE; 136219182Sglebiusstatic const struct ng_parse_type ng_netflow_setmtu_type = { 137219182Sglebius &ng_parse_struct_type, 138219182Sglebius &ng_netflow_setmtu_type_fields 139219182Sglebius}; 140219182Sglebius 141241446Smelifaro/* Parse type for struct ng_netflow_v9info */ 142241446Smelifarostatic const struct ng_parse_struct_field ng_netflow_v9info_type_fields[] 143241446Smelifaro = NG_NETFLOW_V9INFO_TYPE; 144241446Smelifarostatic const struct ng_parse_type ng_netflow_v9info_type = { 145241446Smelifaro &ng_parse_struct_type, 146241446Smelifaro &ng_netflow_v9info_type_fields 147241446Smelifaro}; 148241446Smelifaro 149135332Sglebius/* List of commands and how to convert arguments to/from ASCII */ 150135332Sglebiusstatic const struct ng_cmdlist ng_netflow_cmds[] = { 151135332Sglebius { 152135332Sglebius NGM_NETFLOW_COOKIE, 153135332Sglebius NGM_NETFLOW_INFO, 154135332Sglebius "info", 155135332Sglebius NULL, 156135332Sglebius &ng_netflow_info_type 157135332Sglebius }, 158135332Sglebius { 159135332Sglebius NGM_NETFLOW_COOKIE, 160135332Sglebius NGM_NETFLOW_IFINFO, 161135332Sglebius "ifinfo", 162138392Sglebius &ng_parse_uint16_type, 163135332Sglebius &ng_netflow_ifinfo_type 164135332Sglebius }, 165135332Sglebius { 166135332Sglebius NGM_NETFLOW_COOKIE, 167135332Sglebius NGM_NETFLOW_SETDLT, 168135332Sglebius "setdlt", 169135332Sglebius &ng_netflow_setdlt_type, 170135332Sglebius NULL 171135332Sglebius }, 172135332Sglebius { 173135332Sglebius NGM_NETFLOW_COOKIE, 174135332Sglebius NGM_NETFLOW_SETIFINDEX, 175135332Sglebius "setifindex", 176135332Sglebius &ng_netflow_setifindex_type, 177135332Sglebius NULL 178135332Sglebius }, 179135332Sglebius { 180135332Sglebius NGM_NETFLOW_COOKIE, 181135332Sglebius NGM_NETFLOW_SETTIMEOUTS, 182135332Sglebius "settimeouts", 183135332Sglebius &ng_netflow_settimeouts_type, 184135332Sglebius NULL 185135332Sglebius }, 186183693Smav { 187183693Smav NGM_NETFLOW_COOKIE, 188183693Smav NGM_NETFLOW_SETCONFIG, 189183693Smav "setconfig", 190183693Smav &ng_netflow_setconfig_type, 191183693Smav NULL 192183693Smav }, 193219182Sglebius { 194219182Sglebius NGM_NETFLOW_COOKIE, 195219182Sglebius NGM_NETFLOW_SETTEMPLATE, 196219182Sglebius "settemplate", 197219182Sglebius &ng_netflow_settemplate_type, 198219182Sglebius NULL 199219182Sglebius }, 200219182Sglebius { 201219182Sglebius NGM_NETFLOW_COOKIE, 202219182Sglebius NGM_NETFLOW_SETMTU, 203219182Sglebius "setmtu", 204219182Sglebius &ng_netflow_setmtu_type, 205219182Sglebius NULL 206219182Sglebius }, 207241446Smelifaro { 208241446Smelifaro NGM_NETFLOW_COOKIE, 209241446Smelifaro NGM_NETFLOW_V9INFO, 210241446Smelifaro "v9info", 211241446Smelifaro NULL, 212241446Smelifaro &ng_netflow_v9info_type 213241446Smelifaro }, 214135332Sglebius { 0 } 215135332Sglebius}; 216135332Sglebius 217135332Sglebius 218135332Sglebius/* Netgraph node type descriptor */ 219135332Sglebiusstatic struct ng_type ng_netflow_typestruct = { 220135332Sglebius .version = NG_ABI_VERSION, 221135332Sglebius .name = NG_NETFLOW_NODE_TYPE, 222135332Sglebius .constructor = ng_netflow_constructor, 223135332Sglebius .rcvmsg = ng_netflow_rcvmsg, 224135332Sglebius .close = ng_netflow_close, 225135332Sglebius .shutdown = ng_netflow_rmnode, 226135332Sglebius .newhook = ng_netflow_newhook, 227135332Sglebius .rcvdata = ng_netflow_rcvdata, 228135332Sglebius .disconnect = ng_netflow_disconnect, 229135332Sglebius .cmdlist = ng_netflow_cmds, 230135332Sglebius}; 231135332SglebiusNETGRAPH_INIT(netflow, &ng_netflow_typestruct); 232135332Sglebius 233135332Sglebius/* Called at node creation */ 234135332Sglebiusstatic int 235146092Sglebiusng_netflow_constructor(node_p node) 236135332Sglebius{ 237135332Sglebius priv_p priv; 238220769Sglebius int i; 239135332Sglebius 240135332Sglebius /* Initialize private data */ 241220768Sglebius priv = malloc(sizeof(*priv), M_NETGRAPH, M_WAITOK | M_ZERO); 242135332Sglebius 243232921Smelifaro /* Initialize fib data */ 244232921Smelifaro priv->maxfibs = rt_numfibs; 245232921Smelifaro priv->fib_data = malloc(sizeof(fib_export_p) * priv->maxfibs, 246232921Smelifaro M_NETGRAPH, M_WAITOK | M_ZERO); 247232921Smelifaro 248135332Sglebius /* Make node and its data point at each other */ 249135332Sglebius NG_NODE_SET_PRIVATE(node, priv); 250135332Sglebius priv->node = node; 251135332Sglebius 252135332Sglebius /* Initialize timeouts to default values */ 253135332Sglebius priv->info.nfinfo_inact_t = INACTIVE_TIMEOUT; 254135332Sglebius priv->info.nfinfo_act_t = ACTIVE_TIMEOUT; 255135332Sglebius 256183693Smav /* Set default config */ 257183693Smav for (i = 0; i < NG_NETFLOW_MAXIFACES; i++) 258183693Smav priv->ifaces[i].info.conf = NG_NETFLOW_CONF_INGRESS; 259183693Smav 260135332Sglebius /* Initialize callout handle */ 261178250Skris callout_init(&priv->exp_callout, CALLOUT_MPSAFE); 262135332Sglebius 263135332Sglebius /* Allocate memory and set up flow cache */ 264220769Sglebius ng_netflow_cache_init(priv); 265135332Sglebius 266135332Sglebius return (0); 267135332Sglebius} 268135332Sglebius 269135332Sglebius/* 270135332Sglebius * ng_netflow supports two hooks: data and export. 271135332Sglebius * Incoming traffic is expected on data, and expired 272135332Sglebius * netflow datagrams are sent to export. 273135332Sglebius */ 274135332Sglebiusstatic int 275135332Sglebiusng_netflow_newhook(node_p node, hook_p hook, const char *name) 276135332Sglebius{ 277135332Sglebius const priv_p priv = NG_NODE_PRIVATE(node); 278135332Sglebius 279135332Sglebius if (strncmp(name, NG_NETFLOW_HOOK_DATA, /* an iface hook? */ 280135332Sglebius strlen(NG_NETFLOW_HOOK_DATA)) == 0) { 281135332Sglebius iface_p iface; 282135332Sglebius int ifnum = -1; 283135332Sglebius const char *cp; 284135332Sglebius char *eptr; 285135332Sglebius 286135332Sglebius cp = name + strlen(NG_NETFLOW_HOOK_DATA); 287135332Sglebius if (!isdigit(*cp) || (cp[0] == '0' && cp[1] != '\0')) 288135332Sglebius return (EINVAL); 289135332Sglebius 290135332Sglebius ifnum = (int)strtoul(cp, &eptr, 10); 291135332Sglebius if (*eptr != '\0' || ifnum < 0 || ifnum >= NG_NETFLOW_MAXIFACES) 292135332Sglebius return (EINVAL); 293135332Sglebius 294135332Sglebius /* See if hook is already connected */ 295135332Sglebius if (priv->ifaces[ifnum].hook != NULL) 296135332Sglebius return (EISCONN); 297135332Sglebius 298135332Sglebius iface = &priv->ifaces[ifnum]; 299135332Sglebius 300135332Sglebius /* Link private info and hook together */ 301135332Sglebius NG_HOOK_SET_PRIVATE(hook, iface); 302135332Sglebius iface->hook = hook; 303135332Sglebius 304135332Sglebius /* 305135332Sglebius * In most cases traffic accounting is done on an 306135332Sglebius * Ethernet interface, so default data link type 307135332Sglebius * will be DLT_EN10MB. 308135332Sglebius */ 309135332Sglebius iface->info.ifinfo_dlt = DLT_EN10MB; 310135332Sglebius 311143988Sglebius } else if (strncmp(name, NG_NETFLOW_HOOK_OUT, 312143988Sglebius strlen(NG_NETFLOW_HOOK_OUT)) == 0) { 313143988Sglebius iface_p iface; 314143988Sglebius int ifnum = -1; 315143988Sglebius const char *cp; 316143988Sglebius char *eptr; 317143988Sglebius 318143988Sglebius cp = name + strlen(NG_NETFLOW_HOOK_OUT); 319143988Sglebius if (!isdigit(*cp) || (cp[0] == '0' && cp[1] != '\0')) 320143988Sglebius return (EINVAL); 321143988Sglebius 322143988Sglebius ifnum = (int)strtoul(cp, &eptr, 10); 323143988Sglebius if (*eptr != '\0' || ifnum < 0 || ifnum >= NG_NETFLOW_MAXIFACES) 324143988Sglebius return (EINVAL); 325143988Sglebius 326143988Sglebius /* See if hook is already connected */ 327143988Sglebius if (priv->ifaces[ifnum].out != NULL) 328143988Sglebius return (EISCONN); 329143988Sglebius 330143988Sglebius iface = &priv->ifaces[ifnum]; 331143988Sglebius 332143988Sglebius /* Link private info and hook together */ 333143988Sglebius NG_HOOK_SET_PRIVATE(hook, iface); 334143988Sglebius iface->out = hook; 335143988Sglebius 336135332Sglebius } else if (strcmp(name, NG_NETFLOW_HOOK_EXPORT) == 0) { 337135332Sglebius 338135332Sglebius if (priv->export != NULL) 339135332Sglebius return (EISCONN); 340135332Sglebius 341219182Sglebius /* Netflow version 5 supports 32-bit counters only */ 342219182Sglebius if (CNTR_MAX == UINT64_MAX) 343219182Sglebius return (EINVAL); 344219182Sglebius 345135332Sglebius priv->export = hook; 346135332Sglebius 347175934Smav /* Exporter is ready. Let's schedule expiry. */ 348175934Smav callout_reset(&priv->exp_callout, (1*hz), &ng_netflow_expire, 349175934Smav (void *)priv); 350219182Sglebius } else if (strcmp(name, NG_NETFLOW_HOOK_EXPORT9) == 0) { 351219182Sglebius 352219182Sglebius if (priv->export9 != NULL) 353219182Sglebius return (EISCONN); 354219182Sglebius 355219182Sglebius priv->export9 = hook; 356219182Sglebius 357219182Sglebius /* Exporter is ready. Let's schedule expiry. */ 358219182Sglebius callout_reset(&priv->exp_callout, (1*hz), &ng_netflow_expire, 359219182Sglebius (void *)priv); 360135332Sglebius } else 361135332Sglebius return (EINVAL); 362135332Sglebius 363135332Sglebius return (0); 364135332Sglebius} 365135332Sglebius 366135332Sglebius/* Get a netgraph control message. */ 367135332Sglebiusstatic int 368135332Sglebiusng_netflow_rcvmsg (node_p node, item_p item, hook_p lasthook) 369135332Sglebius{ 370135332Sglebius const priv_p priv = NG_NODE_PRIVATE(node); 371135332Sglebius struct ng_mesg *resp = NULL; 372135332Sglebius int error = 0; 373135332Sglebius struct ng_mesg *msg; 374135332Sglebius 375135332Sglebius NGI_GET_MSG(item, msg); 376135332Sglebius 377135332Sglebius /* Deal with message according to cookie and command */ 378135332Sglebius switch (msg->header.typecookie) { 379135332Sglebius case NGM_NETFLOW_COOKIE: 380135332Sglebius switch (msg->header.cmd) { 381135332Sglebius case NGM_NETFLOW_INFO: 382248724Sglebius { 383135332Sglebius struct ng_netflow_info *i; 384135332Sglebius 385135332Sglebius NG_MKRESPONSE(resp, msg, sizeof(struct ng_netflow_info), 386135332Sglebius M_NOWAIT); 387135332Sglebius i = (struct ng_netflow_info *)resp->data; 388135332Sglebius ng_netflow_copyinfo(priv, i); 389135332Sglebius 390135332Sglebius break; 391248724Sglebius } 392135332Sglebius case NGM_NETFLOW_IFINFO: 393248724Sglebius { 394135332Sglebius struct ng_netflow_ifinfo *i; 395138392Sglebius const uint16_t *index; 396135332Sglebius 397138392Sglebius if (msg->header.arglen != sizeof(uint16_t)) 398135332Sglebius ERROUT(EINVAL); 399135332Sglebius 400138392Sglebius index = (uint16_t *)msg->data; 401154354Sglebius if (*index >= NG_NETFLOW_MAXIFACES) 402138392Sglebius ERROUT(EINVAL); 403135332Sglebius 404135332Sglebius /* connected iface? */ 405135332Sglebius if (priv->ifaces[*index].hook == NULL) 406135332Sglebius ERROUT(EINVAL); 407135332Sglebius 408135332Sglebius NG_MKRESPONSE(resp, msg, 409135332Sglebius sizeof(struct ng_netflow_ifinfo), M_NOWAIT); 410135332Sglebius i = (struct ng_netflow_ifinfo *)resp->data; 411135332Sglebius memcpy((void *)i, (void *)&priv->ifaces[*index].info, 412135332Sglebius sizeof(priv->ifaces[*index].info)); 413135332Sglebius 414135332Sglebius break; 415248724Sglebius } 416135332Sglebius case NGM_NETFLOW_SETDLT: 417248724Sglebius { 418135332Sglebius struct ng_netflow_setdlt *set; 419135332Sglebius struct ng_netflow_iface *iface; 420135332Sglebius 421248724Sglebius if (msg->header.arglen != 422248724Sglebius sizeof(struct ng_netflow_setdlt)) 423135332Sglebius ERROUT(EINVAL); 424135332Sglebius 425135332Sglebius set = (struct ng_netflow_setdlt *)msg->data; 426154354Sglebius if (set->iface >= NG_NETFLOW_MAXIFACES) 427138392Sglebius ERROUT(EINVAL); 428135332Sglebius iface = &priv->ifaces[set->iface]; 429135332Sglebius 430135332Sglebius /* connected iface? */ 431135332Sglebius if (iface->hook == NULL) 432135332Sglebius ERROUT(EINVAL); 433135332Sglebius 434135332Sglebius switch (set->dlt) { 435135332Sglebius case DLT_EN10MB: 436135332Sglebius iface->info.ifinfo_dlt = DLT_EN10MB; 437135332Sglebius break; 438135332Sglebius case DLT_RAW: 439135332Sglebius iface->info.ifinfo_dlt = DLT_RAW; 440135332Sglebius break; 441135332Sglebius default: 442135332Sglebius ERROUT(EINVAL); 443135332Sglebius } 444135332Sglebius break; 445248724Sglebius } 446135332Sglebius case NGM_NETFLOW_SETIFINDEX: 447248724Sglebius { 448135332Sglebius struct ng_netflow_setifindex *set; 449135332Sglebius struct ng_netflow_iface *iface; 450135332Sglebius 451248724Sglebius if (msg->header.arglen != 452248724Sglebius sizeof(struct ng_netflow_setifindex)) 453135332Sglebius ERROUT(EINVAL); 454135332Sglebius 455135332Sglebius set = (struct ng_netflow_setifindex *)msg->data; 456154354Sglebius if (set->iface >= NG_NETFLOW_MAXIFACES) 457138392Sglebius ERROUT(EINVAL); 458135332Sglebius iface = &priv->ifaces[set->iface]; 459135332Sglebius 460135332Sglebius /* connected iface? */ 461135332Sglebius if (iface->hook == NULL) 462135332Sglebius ERROUT(EINVAL); 463135332Sglebius 464135332Sglebius iface->info.ifinfo_index = set->index; 465135332Sglebius 466135332Sglebius break; 467248724Sglebius } 468135332Sglebius case NGM_NETFLOW_SETTIMEOUTS: 469248724Sglebius { 470135332Sglebius struct ng_netflow_settimeouts *set; 471135332Sglebius 472248724Sglebius if (msg->header.arglen != 473248724Sglebius sizeof(struct ng_netflow_settimeouts)) 474135332Sglebius ERROUT(EINVAL); 475135332Sglebius 476135332Sglebius set = (struct ng_netflow_settimeouts *)msg->data; 477135332Sglebius 478135332Sglebius priv->info.nfinfo_inact_t = set->inactive_timeout; 479135332Sglebius priv->info.nfinfo_act_t = set->active_timeout; 480135332Sglebius 481135332Sglebius break; 482248724Sglebius } 483183693Smav case NGM_NETFLOW_SETCONFIG: 484248724Sglebius { 485183693Smav struct ng_netflow_setconfig *set; 486183693Smav 487248724Sglebius if (msg->header.arglen != 488248724Sglebius sizeof(struct ng_netflow_setconfig)) 489183693Smav ERROUT(EINVAL); 490183693Smav 491183693Smav set = (struct ng_netflow_setconfig *)msg->data; 492183693Smav 493183693Smav if (set->iface >= NG_NETFLOW_MAXIFACES) 494183693Smav ERROUT(EINVAL); 495183693Smav 496183693Smav priv->ifaces[set->iface].info.conf = set->conf; 497183693Smav 498183693Smav break; 499248724Sglebius } 500219182Sglebius case NGM_NETFLOW_SETTEMPLATE: 501248724Sglebius { 502219182Sglebius struct ng_netflow_settemplate *set; 503219182Sglebius 504248724Sglebius if (msg->header.arglen != 505248724Sglebius sizeof(struct ng_netflow_settemplate)) 506219182Sglebius ERROUT(EINVAL); 507219182Sglebius 508219182Sglebius set = (struct ng_netflow_settemplate *)msg->data; 509219182Sglebius 510219182Sglebius priv->templ_packets = set->packets; 511219182Sglebius priv->templ_time = set->time; 512219182Sglebius 513219182Sglebius break; 514248724Sglebius } 515219182Sglebius case NGM_NETFLOW_SETMTU: 516248724Sglebius { 517219182Sglebius struct ng_netflow_setmtu *set; 518219182Sglebius 519248724Sglebius if (msg->header.arglen != 520248724Sglebius sizeof(struct ng_netflow_setmtu)) 521219182Sglebius ERROUT(EINVAL); 522219182Sglebius 523219182Sglebius set = (struct ng_netflow_setmtu *)msg->data; 524219182Sglebius if ((set->mtu < MIN_MTU) || (set->mtu > MAX_MTU)) 525219182Sglebius ERROUT(EINVAL); 526219182Sglebius 527219182Sglebius priv->mtu = set->mtu; 528219182Sglebius 529219182Sglebius break; 530248724Sglebius } 531135332Sglebius case NGM_NETFLOW_SHOW: 532248724Sglebius if (msg->header.arglen != 533248724Sglebius sizeof(struct ngnf_show_header)) 534135332Sglebius ERROUT(EINVAL); 535135332Sglebius 536135332Sglebius NG_MKRESPONSE(resp, msg, NGRESP_SIZE, M_NOWAIT); 537135332Sglebius 538135332Sglebius if (!resp) 539135332Sglebius ERROUT(ENOMEM); 540135332Sglebius 541223787Sglebius error = ng_netflow_flow_show(priv, 542223787Sglebius (struct ngnf_show_header *)msg->data, 543223787Sglebius (struct ngnf_show_header *)resp->data); 544135332Sglebius 545223787Sglebius if (error) 546223787Sglebius NG_FREE_MSG(resp); 547223787Sglebius 548135332Sglebius break; 549241446Smelifaro case NGM_NETFLOW_V9INFO: 550248724Sglebius { 551241446Smelifaro struct ng_netflow_v9info *i; 552241446Smelifaro 553248724Sglebius NG_MKRESPONSE(resp, msg, 554248724Sglebius sizeof(struct ng_netflow_v9info), M_NOWAIT); 555241446Smelifaro i = (struct ng_netflow_v9info *)resp->data; 556241446Smelifaro ng_netflow_copyv9info(priv, i); 557241446Smelifaro 558241446Smelifaro break; 559248724Sglebius } 560135332Sglebius default: 561135332Sglebius ERROUT(EINVAL); /* unknown command */ 562135332Sglebius break; 563135332Sglebius } 564135332Sglebius break; 565135332Sglebius default: 566135332Sglebius ERROUT(EINVAL); /* incorrect cookie */ 567135332Sglebius break; 568135332Sglebius } 569135332Sglebius 570135332Sglebius /* 571135332Sglebius * Take care of synchronous response, if any. 572135332Sglebius * Free memory and return. 573135332Sglebius */ 574135332Sglebiusdone: 575135332Sglebius NG_RESPOND_MSG(error, node, item, resp); 576135332Sglebius NG_FREE_MSG(msg); 577135332Sglebius 578135332Sglebius return (error); 579135332Sglebius} 580135332Sglebius 581135332Sglebius/* Receive data on hook. */ 582135332Sglebiusstatic int 583135332Sglebiusng_netflow_rcvdata (hook_p hook, item_p item) 584135332Sglebius{ 585135332Sglebius const node_p node = NG_HOOK_NODE(hook); 586135332Sglebius const priv_p priv = NG_NODE_PRIVATE(node); 587135332Sglebius const iface_p iface = NG_HOOK_PRIVATE(hook); 588183693Smav hook_p out; 589219182Sglebius struct mbuf *m = NULL, *m_old = NULL; 590219182Sglebius struct ip *ip = NULL; 591219182Sglebius struct ip6_hdr *ip6 = NULL; 592183693Smav struct m_tag *mtag; 593219182Sglebius int pullup_len = 0, off; 594237227Smelifaro uint8_t acct = 0, bypass = 0, flags = 0, upper_proto = 0; 595237226Smelifaro int error = 0, l3_off = 0; 596183693Smav unsigned int src_if_index; 597219182Sglebius caddr_t upper_ptr = NULL; 598219182Sglebius fib_export_p fe; 599219182Sglebius uint32_t fib; 600135332Sglebius 601219182Sglebius if ((hook == priv->export) || (hook == priv->export9)) { 602135332Sglebius /* 603135332Sglebius * Data arrived on export hook. 604135332Sglebius * This must not happen. 605135332Sglebius */ 606140511Sglebius log(LOG_ERR, "ng_netflow: incoming data on export hook!\n"); 607135332Sglebius ERROUT(EINVAL); 608135332Sglebius }; 609135332Sglebius 610183693Smav if (hook == iface->hook) { 611183693Smav if ((iface->info.conf & NG_NETFLOW_CONF_INGRESS) == 0) 612183693Smav bypass = 1; 613183693Smav out = iface->out; 614183693Smav } else if (hook == iface->out) { 615183693Smav if ((iface->info.conf & NG_NETFLOW_CONF_EGRESS) == 0) 616183693Smav bypass = 1; 617183693Smav out = iface->hook; 618219182Sglebius } else 619183693Smav ERROUT(EINVAL); 620219182Sglebius 621248724Sglebius if ((!bypass) && (iface->info.conf & 622248724Sglebius (NG_NETFLOW_CONF_ONCE | NG_NETFLOW_CONF_THISONCE))) { 623183693Smav mtag = m_tag_locate(NGI_M(item), MTAG_NETFLOW, 624183693Smav MTAG_NETFLOW_CALLED, NULL); 625183693Smav while (mtag != NULL) { 626183693Smav if ((iface->info.conf & NG_NETFLOW_CONF_ONCE) || 627183693Smav ((ng_ID_t *)(mtag + 1))[0] == NG_NODE_ID(node)) { 628183693Smav bypass = 1; 629183693Smav break; 630183693Smav } 631183693Smav mtag = m_tag_locate(NGI_M(item), MTAG_NETFLOW, 632183693Smav MTAG_NETFLOW_CALLED, mtag); 633183693Smav } 634183693Smav } 635183693Smav 636183693Smav if (bypass) { 637183693Smav if (out == NULL) 638143988Sglebius ERROUT(ENOTCONN); 639143988Sglebius 640183693Smav NG_FWD_ITEM_HOOK(error, item, out); 641143988Sglebius return (error); 642143988Sglebius } 643183693Smav 644248724Sglebius if (iface->info.conf & 645248724Sglebius (NG_NETFLOW_CONF_ONCE | NG_NETFLOW_CONF_THISONCE)) { 646183693Smav mtag = m_tag_alloc(MTAG_NETFLOW, MTAG_NETFLOW_CALLED, 647183693Smav sizeof(ng_ID_t), M_NOWAIT); 648183693Smav if (mtag) { 649183693Smav ((ng_ID_t *)(mtag + 1))[0] = NG_NODE_ID(node); 650183693Smav m_tag_prepend(NGI_M(item), mtag); 651183693Smav } 652183693Smav } 653143988Sglebius 654237227Smelifaro /* Import configuration flags related to flow creation */ 655237227Smelifaro flags = iface->info.conf & NG_NETFLOW_FLOW_FLAGS; 656237227Smelifaro 657143988Sglebius NGI_GET_M(item, m); 658219182Sglebius m_old = m; 659143988Sglebius 660143924Sglebius /* Increase counters. */ 661135332Sglebius iface->info.ifinfo_packets++; 662135332Sglebius 663143924Sglebius /* 664143924Sglebius * Depending on interface data link type and packet contents 665143924Sglebius * we pullup enough data, so that ng_netflow_flow_add() does not 666143924Sglebius * need to know about mbuf at all. We keep current length of data 667143924Sglebius * needed to be contiguous in pullup_len. mtod() is done at the 668143924Sglebius * very end one more time, since m can had changed after pulluping. 669143924Sglebius * 670143924Sglebius * In case of unrecognized data we don't return error, but just 671143924Sglebius * pass data to downstream hook, if it is available. 672143924Sglebius */ 673143924Sglebius 674143924Sglebius#define M_CHECK(length) do { \ 675143924Sglebius pullup_len += length; \ 676219182Sglebius if (((m)->m_pkthdr.len < (pullup_len)) || \ 677219182Sglebius ((pullup_len) > MHLEN)) { \ 678143924Sglebius error = EINVAL; \ 679143988Sglebius goto bypass; \ 680143924Sglebius } \ 681143924Sglebius if ((m)->m_len < (pullup_len) && \ 682143924Sglebius (((m) = m_pullup((m),(pullup_len))) == NULL)) { \ 683143924Sglebius error = ENOBUFS; \ 684143924Sglebius goto done; \ 685143924Sglebius } \ 686143924Sglebius} while (0) 687143924Sglebius 688135332Sglebius switch (iface->info.ifinfo_dlt) { 689135332Sglebius case DLT_EN10MB: /* Ethernet */ 690143924Sglebius { 691135332Sglebius struct ether_header *eh; 692135332Sglebius uint16_t etype; 693135332Sglebius 694143924Sglebius M_CHECK(sizeof(struct ether_header)); 695135332Sglebius eh = mtod(m, struct ether_header *); 696135332Sglebius 697143924Sglebius /* Make sure this is IP frame. */ 698135332Sglebius etype = ntohs(eh->ether_type); 699135332Sglebius switch (etype) { 700135332Sglebius case ETHERTYPE_IP: 701143924Sglebius M_CHECK(sizeof(struct ip)); 702143924Sglebius eh = mtod(m, struct ether_header *); 703143924Sglebius ip = (struct ip *)(eh + 1); 704237226Smelifaro l3_off = sizeof(struct ether_header); 705135332Sglebius break; 706219182Sglebius#ifdef INET6 707219182Sglebius case ETHERTYPE_IPV6: 708219182Sglebius /* 709219182Sglebius * m_pullup() called by M_CHECK() pullups 710248724Sglebius * kern.ipc.max_protohdr (default 60 bytes) 711248724Sglebius * which is enough. 712219182Sglebius */ 713219182Sglebius M_CHECK(sizeof(struct ip6_hdr)); 714219182Sglebius eh = mtod(m, struct ether_header *); 715219182Sglebius ip6 = (struct ip6_hdr *)(eh + 1); 716237226Smelifaro l3_off = sizeof(struct ether_header); 717219182Sglebius break; 718219182Sglebius#endif 719163247Sglebius case ETHERTYPE_VLAN: 720163247Sglebius { 721163247Sglebius struct ether_vlan_header *evh; 722163247Sglebius 723163247Sglebius M_CHECK(sizeof(struct ether_vlan_header) - 724163247Sglebius sizeof(struct ether_header)); 725163247Sglebius evh = mtod(m, struct ether_vlan_header *); 726219182Sglebius etype = ntohs(evh->evl_proto); 727237226Smelifaro l3_off = sizeof(struct ether_vlan_header); 728219182Sglebius 729219182Sglebius if (etype == ETHERTYPE_IP) { 730163247Sglebius M_CHECK(sizeof(struct ip)); 731163247Sglebius ip = (struct ip *)(evh + 1); 732163247Sglebius break; 733219182Sglebius#ifdef INET6 734219182Sglebius } else if (etype == ETHERTYPE_IPV6) { 735219182Sglebius M_CHECK(sizeof(struct ip6_hdr)); 736219182Sglebius ip6 = (struct ip6_hdr *)(evh + 1); 737219182Sglebius break; 738219182Sglebius#endif 739163247Sglebius } 740163247Sglebius } 741135332Sglebius default: 742143988Sglebius goto bypass; /* pass this frame */ 743135332Sglebius } 744143924Sglebius break; 745143924Sglebius } 746143924Sglebius case DLT_RAW: /* IP packets */ 747143924Sglebius M_CHECK(sizeof(struct ip)); 748143924Sglebius ip = mtod(m, struct ip *); 749237226Smelifaro /* l3_off is already zero */ 750219182Sglebius#ifdef INET6 751248724Sglebius /* 752248724Sglebius * If INET6 is not defined IPv6 packets 753248724Sglebius * will be discarded in ng_netflow_flow_add(). 754248724Sglebius */ 755219182Sglebius if (ip->ip_v == IP6VERSION) { 756219182Sglebius ip = NULL; 757237162Smelifaro M_CHECK(sizeof(struct ip6_hdr) - sizeof(struct ip)); 758219182Sglebius ip6 = mtod(m, struct ip6_hdr *); 759219182Sglebius } 760219182Sglebius#endif 761143924Sglebius break; 762143924Sglebius default: 763143988Sglebius goto bypass; 764143924Sglebius break; 765143924Sglebius } 766135332Sglebius 767219182Sglebius off = pullup_len; 768219182Sglebius 769219182Sglebius if ((ip != NULL) && ((ip->ip_off & htons(IP_OFFMASK)) == 0)) { 770219182Sglebius if ((ip->ip_v != IPVERSION) || 771219182Sglebius ((ip->ip_hl << 2) < sizeof(struct ip))) 772219182Sglebius goto bypass; 773148091Sglebius /* 774219182Sglebius * In case of IPv4 header with options, we haven't pulled 775148091Sglebius * up enough, yet. 776148091Sglebius */ 777219182Sglebius M_CHECK((ip->ip_hl << 2) - sizeof(struct ip)); 778143924Sglebius 779219182Sglebius /* Save upper layer offset and proto */ 780219182Sglebius off = pullup_len; 781219182Sglebius upper_proto = ip->ip_p; 782219182Sglebius 783219182Sglebius /* 784248724Sglebius * XXX: in case of wrong upper layer header we will 785248724Sglebius * forward this packet but skip this record in netflow. 786219182Sglebius */ 787148091Sglebius switch (ip->ip_p) { 788148091Sglebius case IPPROTO_TCP: 789148091Sglebius M_CHECK(sizeof(struct tcphdr)); 790148091Sglebius break; 791148091Sglebius case IPPROTO_UDP: 792148091Sglebius M_CHECK(sizeof(struct udphdr)); 793148091Sglebius break; 794219182Sglebius case IPPROTO_SCTP: 795219182Sglebius M_CHECK(sizeof(struct sctphdr)); 796219182Sglebius break; 797148091Sglebius } 798219182Sglebius } else if (ip != NULL) { 799248724Sglebius /* 800248724Sglebius * Nothing to save except upper layer proto, 801248724Sglebius * since this is a packet fragment. 802248724Sglebius */ 803237227Smelifaro flags |= NG_NETFLOW_IS_FRAG; 804219182Sglebius upper_proto = ip->ip_p; 805219182Sglebius if ((ip->ip_v != IPVERSION) || 806219182Sglebius ((ip->ip_hl << 2) < sizeof(struct ip))) 807219182Sglebius goto bypass; 808219182Sglebius#ifdef INET6 809219182Sglebius } else if (ip6 != NULL) { 810219182Sglebius int cur = ip6->ip6_nxt, hdr_off = 0; 811219182Sglebius struct ip6_ext *ip6e; 812219182Sglebius struct ip6_frag *ip6f; 813143924Sglebius 814248724Sglebius if (priv->export9 == NULL) 815248724Sglebius goto bypass; 816248724Sglebius 817248724Sglebius /* Save upper layer info. */ 818219182Sglebius off = pullup_len; 819219182Sglebius upper_proto = cur; 820163247Sglebius 821219182Sglebius if ((ip6->ip6_vfc & IPV6_VERSION_MASK) != IPV6_VERSION) 822219182Sglebius goto bypass; 823219182Sglebius 824248724Sglebius /* 825248724Sglebius * Loop thru IPv6 extended headers to get upper 826248724Sglebius * layer header / frag. 827248724Sglebius */ 828248724Sglebius for (;;) { 829219182Sglebius switch (cur) { 830219182Sglebius /* 831248724Sglebius * Same as in IPv4, we can forward a 'bad' 832248724Sglebius * packet without accounting. 833219182Sglebius */ 834219182Sglebius case IPPROTO_TCP: 835219182Sglebius M_CHECK(sizeof(struct tcphdr)); 836219182Sglebius goto loopend; 837219182Sglebius case IPPROTO_UDP: 838219182Sglebius M_CHECK(sizeof(struct udphdr)); 839219182Sglebius goto loopend; 840219182Sglebius case IPPROTO_SCTP: 841219182Sglebius M_CHECK(sizeof(struct sctphdr)); 842219182Sglebius goto loopend; 843219182Sglebius 844219182Sglebius /* Loop until 'real' upper layer headers */ 845219182Sglebius case IPPROTO_HOPOPTS: 846219182Sglebius case IPPROTO_ROUTING: 847219182Sglebius case IPPROTO_DSTOPTS: 848219182Sglebius M_CHECK(sizeof(struct ip6_ext)); 849248724Sglebius ip6e = (struct ip6_ext *)(mtod(m, caddr_t) + 850248724Sglebius off); 851219182Sglebius upper_proto = ip6e->ip6e_nxt; 852219182Sglebius hdr_off = (ip6e->ip6e_len + 1) << 3; 853219182Sglebius break; 854219182Sglebius 855219182Sglebius /* RFC4302, can be before DSTOPTS */ 856219182Sglebius case IPPROTO_AH: 857219182Sglebius M_CHECK(sizeof(struct ip6_ext)); 858248724Sglebius ip6e = (struct ip6_ext *)(mtod(m, caddr_t) + 859248724Sglebius off); 860219182Sglebius upper_proto = ip6e->ip6e_nxt; 861219182Sglebius hdr_off = (ip6e->ip6e_len + 2) << 2; 862219182Sglebius break; 863219182Sglebius 864219182Sglebius case IPPROTO_FRAGMENT: 865219182Sglebius M_CHECK(sizeof(struct ip6_frag)); 866248724Sglebius ip6f = (struct ip6_frag *)(mtod(m, caddr_t) + 867248724Sglebius off); 868219182Sglebius upper_proto = ip6f->ip6f_nxt; 869219182Sglebius hdr_off = sizeof(struct ip6_frag); 870219182Sglebius off += hdr_off; 871237227Smelifaro flags |= NG_NETFLOW_IS_FRAG; 872219182Sglebius goto loopend; 873219182Sglebius 874219182Sglebius#if 0 875219182Sglebius case IPPROTO_NONE: 876219182Sglebius goto loopend; 877219182Sglebius#endif 878237226Smelifaro /* 879241369Skevlo * Any unknown header (new extension or IPv6/IPv4 880237226Smelifaro * header for tunnels) ends loop. 881237226Smelifaro */ 882219182Sglebius default: 883219182Sglebius goto loopend; 884219182Sglebius } 885219182Sglebius 886219182Sglebius off += hdr_off; 887219182Sglebius cur = upper_proto; 888163247Sglebius } 889219182Sglebius#endif 890135332Sglebius } 891143924Sglebius#undef M_CHECK 892135332Sglebius 893219182Sglebius#ifdef INET6 894219182Sglebiusloopend: 895219182Sglebius#endif 896219182Sglebius /* Just in case of real reallocation in M_CHECK() / m_pullup() */ 897219182Sglebius if (m != m_old) { 898219182Sglebius atomic_fetchadd_32(&priv->info.nfinfo_realloc_mbuf, 1); 899237226Smelifaro /* Restore ip/ipv6 pointer */ 900237226Smelifaro if (ip != NULL) 901237226Smelifaro ip = (struct ip *)(mtod(m, caddr_t) + l3_off); 902237226Smelifaro else if (ip6 != NULL) 903237226Smelifaro ip6 = (struct ip6_hdr *)(mtod(m, caddr_t) + l3_off); 904219182Sglebius } 905219182Sglebius 906219182Sglebius upper_ptr = (caddr_t)(mtod(m, caddr_t) + off); 907219182Sglebius 908183693Smav /* Determine packet input interface. Prefer configured. */ 909183693Smav src_if_index = 0; 910183693Smav if (hook == iface->out || iface->info.ifinfo_index == 0) { 911183693Smav if (m->m_pkthdr.rcvif != NULL) 912183693Smav src_if_index = m->m_pkthdr.rcvif->if_index; 913183693Smav } else 914183693Smav src_if_index = iface->info.ifinfo_index; 915219182Sglebius 916219182Sglebius /* Check packet FIB */ 917219182Sglebius fib = M_GETFIB(m); 918232921Smelifaro if (fib >= priv->maxfibs) { 919232921Smelifaro CTR2(KTR_NET, "ng_netflow_rcvdata(): packet fib %d is out of " 920232921Smelifaro "range of available fibs: 0 .. %d", 921232921Smelifaro fib, priv->maxfibs); 922219182Sglebius goto bypass; 923219182Sglebius } 924135332Sglebius 925219182Sglebius if ((fe = priv_to_fib(priv, fib)) == NULL) { 926219182Sglebius /* Setup new FIB */ 927219182Sglebius if (ng_netflow_fib_init(priv, fib) != 0) { 928219182Sglebius /* malloc() failed */ 929219182Sglebius goto bypass; 930219182Sglebius } 931183693Smav 932219182Sglebius fe = priv_to_fib(priv, fib); 933219182Sglebius } 934219182Sglebius 935219182Sglebius if (ip != NULL) 936248724Sglebius error = ng_netflow_flow_add(priv, fe, ip, upper_ptr, 937248724Sglebius upper_proto, flags, src_if_index); 938219182Sglebius#ifdef INET6 939219182Sglebius else if (ip6 != NULL) 940248724Sglebius error = ng_netflow_flow6_add(priv, fe, ip6, upper_ptr, 941248724Sglebius upper_proto, flags, src_if_index); 942219182Sglebius#endif 943219182Sglebius else 944219182Sglebius goto bypass; 945219182Sglebius 946219182Sglebius acct = 1; 947143988Sglebiusbypass: 948183693Smav if (out != NULL) { 949219182Sglebius if (acct == 0) { 950219182Sglebius /* Accounting failure */ 951219182Sglebius if (ip != NULL) { 952248724Sglebius atomic_fetchadd_32(&priv->info.nfinfo_spackets, 953248724Sglebius 1); 954219182Sglebius priv->info.nfinfo_sbytes += m_length(m, NULL); 955219182Sglebius } else if (ip6 != NULL) { 956248724Sglebius atomic_fetchadd_32(&priv->info.nfinfo_spackets6, 957248724Sglebius 1); 958219182Sglebius priv->info.nfinfo_sbytes6 += m_length(m, NULL); 959219182Sglebius } 960219182Sglebius } 961219182Sglebius 962143988Sglebius /* XXX: error gets overwritten here */ 963183693Smav NG_FWD_NEW_DATA(error, item, out, m); 964143988Sglebius return (error); 965143988Sglebius } 966135332Sglebiusdone: 967143912Sglebius if (item) 968143912Sglebius NG_FREE_ITEM(item); 969143912Sglebius if (m) 970135332Sglebius NG_FREE_M(m); 971135332Sglebius 972135332Sglebius return (error); 973135332Sglebius} 974135332Sglebius 975135332Sglebius/* We will be shut down in a moment */ 976135332Sglebiusstatic int 977135332Sglebiusng_netflow_close(node_p node) 978135332Sglebius{ 979135332Sglebius const priv_p priv = NG_NODE_PRIVATE(node); 980135332Sglebius 981135332Sglebius callout_drain(&priv->exp_callout); 982135332Sglebius ng_netflow_cache_flush(priv); 983135332Sglebius 984135332Sglebius return (0); 985135332Sglebius} 986135332Sglebius 987135332Sglebius/* Do local shutdown processing. */ 988135332Sglebiusstatic int 989135332Sglebiusng_netflow_rmnode(node_p node) 990135332Sglebius{ 991135332Sglebius const priv_p priv = NG_NODE_PRIVATE(node); 992135332Sglebius 993135332Sglebius NG_NODE_SET_PRIVATE(node, NULL); 994135332Sglebius NG_NODE_UNREF(priv->node); 995135332Sglebius 996232921Smelifaro free(priv->fib_data, M_NETGRAPH); 997184205Sdes free(priv, M_NETGRAPH); 998135332Sglebius 999135332Sglebius return (0); 1000135332Sglebius} 1001135332Sglebius 1002135332Sglebius/* Hook disconnection. */ 1003135332Sglebiusstatic int 1004135332Sglebiusng_netflow_disconnect(hook_p hook) 1005135332Sglebius{ 1006135332Sglebius node_p node = NG_HOOK_NODE(hook); 1007135332Sglebius priv_p priv = NG_NODE_PRIVATE(node); 1008135332Sglebius iface_p iface = NG_HOOK_PRIVATE(hook); 1009135332Sglebius 1010153791Sglebius if (iface != NULL) { 1011153791Sglebius if (iface->hook == hook) 1012153791Sglebius iface->hook = NULL; 1013153791Sglebius if (iface->out == hook) 1014153791Sglebius iface->out = NULL; 1015153791Sglebius } 1016135332Sglebius 1017175934Smav /* if export hook disconnected stop running expire(). */ 1018175934Smav if (hook == priv->export) { 1019219182Sglebius if (priv->export9 == NULL) 1020219182Sglebius callout_drain(&priv->exp_callout); 1021135332Sglebius priv->export = NULL; 1022175934Smav } 1023135332Sglebius 1024219182Sglebius if (hook == priv->export9) { 1025219182Sglebius if (priv->export == NULL) 1026219182Sglebius callout_drain(&priv->exp_callout); 1027219182Sglebius priv->export9 = NULL; 1028219182Sglebius } 1029219182Sglebius 1030135332Sglebius /* Removal of the last link destroys the node. */ 1031135332Sglebius if (NG_NODE_NUMHOOKS(node) == 0) 1032135332Sglebius ng_rmnode_self(node); 1033135332Sglebius 1034135332Sglebius return (0); 1035135332Sglebius} 1036