1*b06ebda0SMatthew Dillon /*- 2*b06ebda0SMatthew Dillon * Copyright (c) 2004-2005 Gleb Smirnoff <glebius@FreeBSD.org> 3*b06ebda0SMatthew Dillon * Copyright (c) 2001-2003 Roman V. Palagin <romanp@unshadow.net> 4*b06ebda0SMatthew Dillon * All rights reserved. 5*b06ebda0SMatthew Dillon * 6*b06ebda0SMatthew Dillon * Redistribution and use in source and binary forms, with or without 7*b06ebda0SMatthew Dillon * modification, are permitted provided that the following conditions 8*b06ebda0SMatthew Dillon * are met: 9*b06ebda0SMatthew Dillon * 1. Redistributions of source code must retain the above copyright 10*b06ebda0SMatthew Dillon * notice, this list of conditions and the following disclaimer. 11*b06ebda0SMatthew Dillon * 2. Redistributions in binary form must reproduce the above copyright 12*b06ebda0SMatthew Dillon * notice, this list of conditions and the following disclaimer in the 13*b06ebda0SMatthew Dillon * documentation and/or other materials provided with the distribution. 14*b06ebda0SMatthew Dillon * 15*b06ebda0SMatthew Dillon * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 16*b06ebda0SMatthew Dillon * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 17*b06ebda0SMatthew Dillon * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 18*b06ebda0SMatthew Dillon * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 19*b06ebda0SMatthew Dillon * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 20*b06ebda0SMatthew Dillon * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 21*b06ebda0SMatthew Dillon * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 22*b06ebda0SMatthew Dillon * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 23*b06ebda0SMatthew Dillon * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 24*b06ebda0SMatthew Dillon * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 25*b06ebda0SMatthew Dillon * SUCH DAMAGE. 26*b06ebda0SMatthew Dillon * 27*b06ebda0SMatthew Dillon * $SourceForge: ng_netflow.c,v 1.30 2004/09/05 11:37:43 glebius Exp $ 28*b06ebda0SMatthew Dillon */ 29*b06ebda0SMatthew Dillon 30*b06ebda0SMatthew Dillon static const char rcs_id[] = 31*b06ebda0SMatthew Dillon "@(#) $FreeBSD: src/sys/netgraph/netflow/ng_netflow.c,v 1.17 2008/04/16 16:47:14 kris Exp $"; 32*b06ebda0SMatthew Dillon 33*b06ebda0SMatthew Dillon #include <sys/param.h> 34*b06ebda0SMatthew Dillon #include <sys/systm.h> 35*b06ebda0SMatthew Dillon #include <sys/kernel.h> 36*b06ebda0SMatthew Dillon #include <sys/limits.h> 37*b06ebda0SMatthew Dillon #include <sys/mbuf.h> 38*b06ebda0SMatthew Dillon #include <sys/socket.h> 39*b06ebda0SMatthew Dillon #include <sys/syslog.h> 40*b06ebda0SMatthew Dillon #include <sys/ctype.h> 41*b06ebda0SMatthew Dillon 42*b06ebda0SMatthew Dillon #include <net/if.h> 43*b06ebda0SMatthew Dillon #include <net/ethernet.h> 44*b06ebda0SMatthew Dillon #include <net/if_arp.h> 45*b06ebda0SMatthew Dillon #include <net/if_var.h> 46*b06ebda0SMatthew Dillon #include <net/if_vlan_var.h> 47*b06ebda0SMatthew Dillon #include <net/bpf.h> 48*b06ebda0SMatthew Dillon #include <netinet/in.h> 49*b06ebda0SMatthew Dillon #include <netinet/in_systm.h> 50*b06ebda0SMatthew Dillon #include <netinet/ip.h> 51*b06ebda0SMatthew Dillon #include <netinet/tcp.h> 52*b06ebda0SMatthew Dillon #include <netinet/udp.h> 53*b06ebda0SMatthew Dillon 54*b06ebda0SMatthew Dillon #include <netgraph/ng_message.h> 55*b06ebda0SMatthew Dillon #include <netgraph/ng_parse.h> 56*b06ebda0SMatthew Dillon #include <netgraph/netgraph.h> 57*b06ebda0SMatthew Dillon #include <netgraph/netflow/netflow.h> 58*b06ebda0SMatthew Dillon #include <netgraph/netflow/ng_netflow.h> 59*b06ebda0SMatthew Dillon 60*b06ebda0SMatthew Dillon /* Netgraph methods */ 61*b06ebda0SMatthew Dillon static ng_constructor_t ng_netflow_constructor; 62*b06ebda0SMatthew Dillon static ng_rcvmsg_t ng_netflow_rcvmsg; 63*b06ebda0SMatthew Dillon static ng_close_t ng_netflow_close; 64*b06ebda0SMatthew Dillon static ng_shutdown_t ng_netflow_rmnode; 65*b06ebda0SMatthew Dillon static ng_newhook_t ng_netflow_newhook; 66*b06ebda0SMatthew Dillon static ng_rcvdata_t ng_netflow_rcvdata; 67*b06ebda0SMatthew Dillon static ng_disconnect_t ng_netflow_disconnect; 68*b06ebda0SMatthew Dillon 69*b06ebda0SMatthew Dillon /* Parse type for struct ng_netflow_info */ 70*b06ebda0SMatthew Dillon static const struct ng_parse_struct_field ng_netflow_info_type_fields[] 71*b06ebda0SMatthew Dillon = NG_NETFLOW_INFO_TYPE; 72*b06ebda0SMatthew Dillon static const struct ng_parse_type ng_netflow_info_type = { 73*b06ebda0SMatthew Dillon &ng_parse_struct_type, 74*b06ebda0SMatthew Dillon &ng_netflow_info_type_fields 75*b06ebda0SMatthew Dillon }; 76*b06ebda0SMatthew Dillon 77*b06ebda0SMatthew Dillon /* Parse type for struct ng_netflow_ifinfo */ 78*b06ebda0SMatthew Dillon static const struct ng_parse_struct_field ng_netflow_ifinfo_type_fields[] 79*b06ebda0SMatthew Dillon = NG_NETFLOW_IFINFO_TYPE; 80*b06ebda0SMatthew Dillon static const struct ng_parse_type ng_netflow_ifinfo_type = { 81*b06ebda0SMatthew Dillon &ng_parse_struct_type, 82*b06ebda0SMatthew Dillon &ng_netflow_ifinfo_type_fields 83*b06ebda0SMatthew Dillon }; 84*b06ebda0SMatthew Dillon 85*b06ebda0SMatthew Dillon /* Parse type for struct ng_netflow_setdlt */ 86*b06ebda0SMatthew Dillon static const struct ng_parse_struct_field ng_netflow_setdlt_type_fields[] 87*b06ebda0SMatthew Dillon = NG_NETFLOW_SETDLT_TYPE; 88*b06ebda0SMatthew Dillon static const struct ng_parse_type ng_netflow_setdlt_type = { 89*b06ebda0SMatthew Dillon &ng_parse_struct_type, 90*b06ebda0SMatthew Dillon &ng_netflow_setdlt_type_fields 91*b06ebda0SMatthew Dillon }; 92*b06ebda0SMatthew Dillon 93*b06ebda0SMatthew Dillon /* Parse type for ng_netflow_setifindex */ 94*b06ebda0SMatthew Dillon static const struct ng_parse_struct_field ng_netflow_setifindex_type_fields[] 95*b06ebda0SMatthew Dillon = NG_NETFLOW_SETIFINDEX_TYPE; 96*b06ebda0SMatthew Dillon static const struct ng_parse_type ng_netflow_setifindex_type = { 97*b06ebda0SMatthew Dillon &ng_parse_struct_type, 98*b06ebda0SMatthew Dillon &ng_netflow_setifindex_type_fields 99*b06ebda0SMatthew Dillon }; 100*b06ebda0SMatthew Dillon 101*b06ebda0SMatthew Dillon /* Parse type for ng_netflow_settimeouts */ 102*b06ebda0SMatthew Dillon static const struct ng_parse_struct_field ng_netflow_settimeouts_type_fields[] 103*b06ebda0SMatthew Dillon = NG_NETFLOW_SETTIMEOUTS_TYPE; 104*b06ebda0SMatthew Dillon static const struct ng_parse_type ng_netflow_settimeouts_type = { 105*b06ebda0SMatthew Dillon &ng_parse_struct_type, 106*b06ebda0SMatthew Dillon &ng_netflow_settimeouts_type_fields 107*b06ebda0SMatthew Dillon }; 108*b06ebda0SMatthew Dillon 109*b06ebda0SMatthew Dillon /* List of commands and how to convert arguments to/from ASCII */ 110*b06ebda0SMatthew Dillon static const struct ng_cmdlist ng_netflow_cmds[] = { 111*b06ebda0SMatthew Dillon { 112*b06ebda0SMatthew Dillon NGM_NETFLOW_COOKIE, 113*b06ebda0SMatthew Dillon NGM_NETFLOW_INFO, 114*b06ebda0SMatthew Dillon "info", 115*b06ebda0SMatthew Dillon NULL, 116*b06ebda0SMatthew Dillon &ng_netflow_info_type 117*b06ebda0SMatthew Dillon }, 118*b06ebda0SMatthew Dillon { 119*b06ebda0SMatthew Dillon NGM_NETFLOW_COOKIE, 120*b06ebda0SMatthew Dillon NGM_NETFLOW_IFINFO, 121*b06ebda0SMatthew Dillon "ifinfo", 122*b06ebda0SMatthew Dillon &ng_parse_uint16_type, 123*b06ebda0SMatthew Dillon &ng_netflow_ifinfo_type 124*b06ebda0SMatthew Dillon }, 125*b06ebda0SMatthew Dillon { 126*b06ebda0SMatthew Dillon NGM_NETFLOW_COOKIE, 127*b06ebda0SMatthew Dillon NGM_NETFLOW_SETDLT, 128*b06ebda0SMatthew Dillon "setdlt", 129*b06ebda0SMatthew Dillon &ng_netflow_setdlt_type, 130*b06ebda0SMatthew Dillon NULL 131*b06ebda0SMatthew Dillon }, 132*b06ebda0SMatthew Dillon { 133*b06ebda0SMatthew Dillon NGM_NETFLOW_COOKIE, 134*b06ebda0SMatthew Dillon NGM_NETFLOW_SETIFINDEX, 135*b06ebda0SMatthew Dillon "setifindex", 136*b06ebda0SMatthew Dillon &ng_netflow_setifindex_type, 137*b06ebda0SMatthew Dillon NULL 138*b06ebda0SMatthew Dillon }, 139*b06ebda0SMatthew Dillon { 140*b06ebda0SMatthew Dillon NGM_NETFLOW_COOKIE, 141*b06ebda0SMatthew Dillon NGM_NETFLOW_SETTIMEOUTS, 142*b06ebda0SMatthew Dillon "settimeouts", 143*b06ebda0SMatthew Dillon &ng_netflow_settimeouts_type, 144*b06ebda0SMatthew Dillon NULL 145*b06ebda0SMatthew Dillon }, 146*b06ebda0SMatthew Dillon { 0 } 147*b06ebda0SMatthew Dillon }; 148*b06ebda0SMatthew Dillon 149*b06ebda0SMatthew Dillon 150*b06ebda0SMatthew Dillon /* Netgraph node type descriptor */ 151*b06ebda0SMatthew Dillon static struct ng_type ng_netflow_typestruct = { 152*b06ebda0SMatthew Dillon .version = NG_ABI_VERSION, 153*b06ebda0SMatthew Dillon .name = NG_NETFLOW_NODE_TYPE, 154*b06ebda0SMatthew Dillon .constructor = ng_netflow_constructor, 155*b06ebda0SMatthew Dillon .rcvmsg = ng_netflow_rcvmsg, 156*b06ebda0SMatthew Dillon .close = ng_netflow_close, 157*b06ebda0SMatthew Dillon .shutdown = ng_netflow_rmnode, 158*b06ebda0SMatthew Dillon .newhook = ng_netflow_newhook, 159*b06ebda0SMatthew Dillon .rcvdata = ng_netflow_rcvdata, 160*b06ebda0SMatthew Dillon .disconnect = ng_netflow_disconnect, 161*b06ebda0SMatthew Dillon .cmdlist = ng_netflow_cmds, 162*b06ebda0SMatthew Dillon }; 163*b06ebda0SMatthew Dillon NETGRAPH_INIT(netflow, &ng_netflow_typestruct); 164*b06ebda0SMatthew Dillon 165*b06ebda0SMatthew Dillon /* Called at node creation */ 166*b06ebda0SMatthew Dillon static int 167*b06ebda0SMatthew Dillon ng_netflow_constructor(node_p node) 168*b06ebda0SMatthew Dillon { 169*b06ebda0SMatthew Dillon priv_p priv; 170*b06ebda0SMatthew Dillon int error = 0; 171*b06ebda0SMatthew Dillon 172*b06ebda0SMatthew Dillon /* Initialize private data */ 173*b06ebda0SMatthew Dillon MALLOC(priv, priv_p, sizeof(*priv), M_NETGRAPH, M_NOWAIT); 174*b06ebda0SMatthew Dillon if (priv == NULL) 175*b06ebda0SMatthew Dillon return (ENOMEM); 176*b06ebda0SMatthew Dillon bzero(priv, sizeof(*priv)); 177*b06ebda0SMatthew Dillon 178*b06ebda0SMatthew Dillon /* Make node and its data point at each other */ 179*b06ebda0SMatthew Dillon NG_NODE_SET_PRIVATE(node, priv); 180*b06ebda0SMatthew Dillon priv->node = node; 181*b06ebda0SMatthew Dillon 182*b06ebda0SMatthew Dillon /* Initialize timeouts to default values */ 183*b06ebda0SMatthew Dillon priv->info.nfinfo_inact_t = INACTIVE_TIMEOUT; 184*b06ebda0SMatthew Dillon priv->info.nfinfo_act_t = ACTIVE_TIMEOUT; 185*b06ebda0SMatthew Dillon 186*b06ebda0SMatthew Dillon /* Initialize callout handle */ 187*b06ebda0SMatthew Dillon callout_init(&priv->exp_callout, CALLOUT_MPSAFE); 188*b06ebda0SMatthew Dillon 189*b06ebda0SMatthew Dillon /* Allocate memory and set up flow cache */ 190*b06ebda0SMatthew Dillon if ((error = ng_netflow_cache_init(priv))) 191*b06ebda0SMatthew Dillon return (error); 192*b06ebda0SMatthew Dillon 193*b06ebda0SMatthew Dillon return (0); 194*b06ebda0SMatthew Dillon } 195*b06ebda0SMatthew Dillon 196*b06ebda0SMatthew Dillon /* 197*b06ebda0SMatthew Dillon * ng_netflow supports two hooks: data and export. 198*b06ebda0SMatthew Dillon * Incoming traffic is expected on data, and expired 199*b06ebda0SMatthew Dillon * netflow datagrams are sent to export. 200*b06ebda0SMatthew Dillon */ 201*b06ebda0SMatthew Dillon static int 202*b06ebda0SMatthew Dillon ng_netflow_newhook(node_p node, hook_p hook, const char *name) 203*b06ebda0SMatthew Dillon { 204*b06ebda0SMatthew Dillon const priv_p priv = NG_NODE_PRIVATE(node); 205*b06ebda0SMatthew Dillon 206*b06ebda0SMatthew Dillon if (strncmp(name, NG_NETFLOW_HOOK_DATA, /* an iface hook? */ 207*b06ebda0SMatthew Dillon strlen(NG_NETFLOW_HOOK_DATA)) == 0) { 208*b06ebda0SMatthew Dillon iface_p iface; 209*b06ebda0SMatthew Dillon int ifnum = -1; 210*b06ebda0SMatthew Dillon const char *cp; 211*b06ebda0SMatthew Dillon char *eptr; 212*b06ebda0SMatthew Dillon 213*b06ebda0SMatthew Dillon cp = name + strlen(NG_NETFLOW_HOOK_DATA); 214*b06ebda0SMatthew Dillon if (!isdigit(*cp) || (cp[0] == '0' && cp[1] != '\0')) 215*b06ebda0SMatthew Dillon return (EINVAL); 216*b06ebda0SMatthew Dillon 217*b06ebda0SMatthew Dillon ifnum = (int)strtoul(cp, &eptr, 10); 218*b06ebda0SMatthew Dillon if (*eptr != '\0' || ifnum < 0 || ifnum >= NG_NETFLOW_MAXIFACES) 219*b06ebda0SMatthew Dillon return (EINVAL); 220*b06ebda0SMatthew Dillon 221*b06ebda0SMatthew Dillon /* See if hook is already connected */ 222*b06ebda0SMatthew Dillon if (priv->ifaces[ifnum].hook != NULL) 223*b06ebda0SMatthew Dillon return (EISCONN); 224*b06ebda0SMatthew Dillon 225*b06ebda0SMatthew Dillon iface = &priv->ifaces[ifnum]; 226*b06ebda0SMatthew Dillon 227*b06ebda0SMatthew Dillon /* Link private info and hook together */ 228*b06ebda0SMatthew Dillon NG_HOOK_SET_PRIVATE(hook, iface); 229*b06ebda0SMatthew Dillon iface->hook = hook; 230*b06ebda0SMatthew Dillon 231*b06ebda0SMatthew Dillon /* 232*b06ebda0SMatthew Dillon * In most cases traffic accounting is done on an 233*b06ebda0SMatthew Dillon * Ethernet interface, so default data link type 234*b06ebda0SMatthew Dillon * will be DLT_EN10MB. 235*b06ebda0SMatthew Dillon */ 236*b06ebda0SMatthew Dillon iface->info.ifinfo_dlt = DLT_EN10MB; 237*b06ebda0SMatthew Dillon 238*b06ebda0SMatthew Dillon } else if (strncmp(name, NG_NETFLOW_HOOK_OUT, 239*b06ebda0SMatthew Dillon strlen(NG_NETFLOW_HOOK_OUT)) == 0) { 240*b06ebda0SMatthew Dillon iface_p iface; 241*b06ebda0SMatthew Dillon int ifnum = -1; 242*b06ebda0SMatthew Dillon const char *cp; 243*b06ebda0SMatthew Dillon char *eptr; 244*b06ebda0SMatthew Dillon 245*b06ebda0SMatthew Dillon cp = name + strlen(NG_NETFLOW_HOOK_OUT); 246*b06ebda0SMatthew Dillon if (!isdigit(*cp) || (cp[0] == '0' && cp[1] != '\0')) 247*b06ebda0SMatthew Dillon return (EINVAL); 248*b06ebda0SMatthew Dillon 249*b06ebda0SMatthew Dillon ifnum = (int)strtoul(cp, &eptr, 10); 250*b06ebda0SMatthew Dillon if (*eptr != '\0' || ifnum < 0 || ifnum >= NG_NETFLOW_MAXIFACES) 251*b06ebda0SMatthew Dillon return (EINVAL); 252*b06ebda0SMatthew Dillon 253*b06ebda0SMatthew Dillon /* See if hook is already connected */ 254*b06ebda0SMatthew Dillon if (priv->ifaces[ifnum].out != NULL) 255*b06ebda0SMatthew Dillon return (EISCONN); 256*b06ebda0SMatthew Dillon 257*b06ebda0SMatthew Dillon iface = &priv->ifaces[ifnum]; 258*b06ebda0SMatthew Dillon 259*b06ebda0SMatthew Dillon /* Link private info and hook together */ 260*b06ebda0SMatthew Dillon NG_HOOK_SET_PRIVATE(hook, iface); 261*b06ebda0SMatthew Dillon iface->out = hook; 262*b06ebda0SMatthew Dillon 263*b06ebda0SMatthew Dillon } else if (strcmp(name, NG_NETFLOW_HOOK_EXPORT) == 0) { 264*b06ebda0SMatthew Dillon 265*b06ebda0SMatthew Dillon if (priv->export != NULL) 266*b06ebda0SMatthew Dillon return (EISCONN); 267*b06ebda0SMatthew Dillon 268*b06ebda0SMatthew Dillon priv->export = hook; 269*b06ebda0SMatthew Dillon 270*b06ebda0SMatthew Dillon #if 0 /* TODO: profile & test first */ 271*b06ebda0SMatthew Dillon /* 272*b06ebda0SMatthew Dillon * We send export dgrams in interrupt handlers and in 273*b06ebda0SMatthew Dillon * callout threads. We'd better queue data for later 274*b06ebda0SMatthew Dillon * netgraph ISR processing. 275*b06ebda0SMatthew Dillon */ 276*b06ebda0SMatthew Dillon NG_HOOK_FORCE_QUEUE(NG_HOOK_PEER(hook)); 277*b06ebda0SMatthew Dillon #endif 278*b06ebda0SMatthew Dillon 279*b06ebda0SMatthew Dillon /* Exporter is ready. Let's schedule expiry. */ 280*b06ebda0SMatthew Dillon callout_reset(&priv->exp_callout, (1*hz), &ng_netflow_expire, 281*b06ebda0SMatthew Dillon (void *)priv); 282*b06ebda0SMatthew Dillon } else 283*b06ebda0SMatthew Dillon return (EINVAL); 284*b06ebda0SMatthew Dillon 285*b06ebda0SMatthew Dillon return (0); 286*b06ebda0SMatthew Dillon } 287*b06ebda0SMatthew Dillon 288*b06ebda0SMatthew Dillon /* Get a netgraph control message. */ 289*b06ebda0SMatthew Dillon static int 290*b06ebda0SMatthew Dillon ng_netflow_rcvmsg (node_p node, item_p item, hook_p lasthook) 291*b06ebda0SMatthew Dillon { 292*b06ebda0SMatthew Dillon const priv_p priv = NG_NODE_PRIVATE(node); 293*b06ebda0SMatthew Dillon struct ng_mesg *resp = NULL; 294*b06ebda0SMatthew Dillon int error = 0; 295*b06ebda0SMatthew Dillon struct ng_mesg *msg; 296*b06ebda0SMatthew Dillon 297*b06ebda0SMatthew Dillon NGI_GET_MSG(item, msg); 298*b06ebda0SMatthew Dillon 299*b06ebda0SMatthew Dillon /* Deal with message according to cookie and command */ 300*b06ebda0SMatthew Dillon switch (msg->header.typecookie) { 301*b06ebda0SMatthew Dillon case NGM_NETFLOW_COOKIE: 302*b06ebda0SMatthew Dillon switch (msg->header.cmd) { 303*b06ebda0SMatthew Dillon case NGM_NETFLOW_INFO: 304*b06ebda0SMatthew Dillon { 305*b06ebda0SMatthew Dillon struct ng_netflow_info *i; 306*b06ebda0SMatthew Dillon 307*b06ebda0SMatthew Dillon NG_MKRESPONSE(resp, msg, sizeof(struct ng_netflow_info), 308*b06ebda0SMatthew Dillon M_NOWAIT); 309*b06ebda0SMatthew Dillon i = (struct ng_netflow_info *)resp->data; 310*b06ebda0SMatthew Dillon ng_netflow_copyinfo(priv, i); 311*b06ebda0SMatthew Dillon 312*b06ebda0SMatthew Dillon break; 313*b06ebda0SMatthew Dillon } 314*b06ebda0SMatthew Dillon case NGM_NETFLOW_IFINFO: 315*b06ebda0SMatthew Dillon { 316*b06ebda0SMatthew Dillon struct ng_netflow_ifinfo *i; 317*b06ebda0SMatthew Dillon const uint16_t *index; 318*b06ebda0SMatthew Dillon 319*b06ebda0SMatthew Dillon if (msg->header.arglen != sizeof(uint16_t)) 320*b06ebda0SMatthew Dillon ERROUT(EINVAL); 321*b06ebda0SMatthew Dillon 322*b06ebda0SMatthew Dillon index = (uint16_t *)msg->data; 323*b06ebda0SMatthew Dillon if (*index >= NG_NETFLOW_MAXIFACES) 324*b06ebda0SMatthew Dillon ERROUT(EINVAL); 325*b06ebda0SMatthew Dillon 326*b06ebda0SMatthew Dillon /* connected iface? */ 327*b06ebda0SMatthew Dillon if (priv->ifaces[*index].hook == NULL) 328*b06ebda0SMatthew Dillon ERROUT(EINVAL); 329*b06ebda0SMatthew Dillon 330*b06ebda0SMatthew Dillon NG_MKRESPONSE(resp, msg, 331*b06ebda0SMatthew Dillon sizeof(struct ng_netflow_ifinfo), M_NOWAIT); 332*b06ebda0SMatthew Dillon i = (struct ng_netflow_ifinfo *)resp->data; 333*b06ebda0SMatthew Dillon memcpy((void *)i, (void *)&priv->ifaces[*index].info, 334*b06ebda0SMatthew Dillon sizeof(priv->ifaces[*index].info)); 335*b06ebda0SMatthew Dillon 336*b06ebda0SMatthew Dillon break; 337*b06ebda0SMatthew Dillon } 338*b06ebda0SMatthew Dillon case NGM_NETFLOW_SETDLT: 339*b06ebda0SMatthew Dillon { 340*b06ebda0SMatthew Dillon struct ng_netflow_setdlt *set; 341*b06ebda0SMatthew Dillon struct ng_netflow_iface *iface; 342*b06ebda0SMatthew Dillon 343*b06ebda0SMatthew Dillon if (msg->header.arglen != sizeof(struct ng_netflow_setdlt)) 344*b06ebda0SMatthew Dillon ERROUT(EINVAL); 345*b06ebda0SMatthew Dillon 346*b06ebda0SMatthew Dillon set = (struct ng_netflow_setdlt *)msg->data; 347*b06ebda0SMatthew Dillon if (set->iface >= NG_NETFLOW_MAXIFACES) 348*b06ebda0SMatthew Dillon ERROUT(EINVAL); 349*b06ebda0SMatthew Dillon iface = &priv->ifaces[set->iface]; 350*b06ebda0SMatthew Dillon 351*b06ebda0SMatthew Dillon /* connected iface? */ 352*b06ebda0SMatthew Dillon if (iface->hook == NULL) 353*b06ebda0SMatthew Dillon ERROUT(EINVAL); 354*b06ebda0SMatthew Dillon 355*b06ebda0SMatthew Dillon switch (set->dlt) { 356*b06ebda0SMatthew Dillon case DLT_EN10MB: 357*b06ebda0SMatthew Dillon iface->info.ifinfo_dlt = DLT_EN10MB; 358*b06ebda0SMatthew Dillon break; 359*b06ebda0SMatthew Dillon case DLT_RAW: 360*b06ebda0SMatthew Dillon iface->info.ifinfo_dlt = DLT_RAW; 361*b06ebda0SMatthew Dillon break; 362*b06ebda0SMatthew Dillon default: 363*b06ebda0SMatthew Dillon ERROUT(EINVAL); 364*b06ebda0SMatthew Dillon } 365*b06ebda0SMatthew Dillon break; 366*b06ebda0SMatthew Dillon } 367*b06ebda0SMatthew Dillon case NGM_NETFLOW_SETIFINDEX: 368*b06ebda0SMatthew Dillon { 369*b06ebda0SMatthew Dillon struct ng_netflow_setifindex *set; 370*b06ebda0SMatthew Dillon struct ng_netflow_iface *iface; 371*b06ebda0SMatthew Dillon 372*b06ebda0SMatthew Dillon if (msg->header.arglen != sizeof(struct ng_netflow_setifindex)) 373*b06ebda0SMatthew Dillon ERROUT(EINVAL); 374*b06ebda0SMatthew Dillon 375*b06ebda0SMatthew Dillon set = (struct ng_netflow_setifindex *)msg->data; 376*b06ebda0SMatthew Dillon if (set->iface >= NG_NETFLOW_MAXIFACES) 377*b06ebda0SMatthew Dillon ERROUT(EINVAL); 378*b06ebda0SMatthew Dillon iface = &priv->ifaces[set->iface]; 379*b06ebda0SMatthew Dillon 380*b06ebda0SMatthew Dillon /* connected iface? */ 381*b06ebda0SMatthew Dillon if (iface->hook == NULL) 382*b06ebda0SMatthew Dillon ERROUT(EINVAL); 383*b06ebda0SMatthew Dillon 384*b06ebda0SMatthew Dillon iface->info.ifinfo_index = set->index; 385*b06ebda0SMatthew Dillon 386*b06ebda0SMatthew Dillon break; 387*b06ebda0SMatthew Dillon } 388*b06ebda0SMatthew Dillon case NGM_NETFLOW_SETTIMEOUTS: 389*b06ebda0SMatthew Dillon { 390*b06ebda0SMatthew Dillon struct ng_netflow_settimeouts *set; 391*b06ebda0SMatthew Dillon 392*b06ebda0SMatthew Dillon if (msg->header.arglen != sizeof(struct ng_netflow_settimeouts)) 393*b06ebda0SMatthew Dillon ERROUT(EINVAL); 394*b06ebda0SMatthew Dillon 395*b06ebda0SMatthew Dillon set = (struct ng_netflow_settimeouts *)msg->data; 396*b06ebda0SMatthew Dillon 397*b06ebda0SMatthew Dillon priv->info.nfinfo_inact_t = set->inactive_timeout; 398*b06ebda0SMatthew Dillon priv->info.nfinfo_act_t = set->active_timeout; 399*b06ebda0SMatthew Dillon 400*b06ebda0SMatthew Dillon break; 401*b06ebda0SMatthew Dillon } 402*b06ebda0SMatthew Dillon case NGM_NETFLOW_SHOW: 403*b06ebda0SMatthew Dillon { 404*b06ebda0SMatthew Dillon uint32_t *last; 405*b06ebda0SMatthew Dillon 406*b06ebda0SMatthew Dillon if (msg->header.arglen != sizeof(uint32_t)) 407*b06ebda0SMatthew Dillon ERROUT(EINVAL); 408*b06ebda0SMatthew Dillon 409*b06ebda0SMatthew Dillon last = (uint32_t *)msg->data; 410*b06ebda0SMatthew Dillon 411*b06ebda0SMatthew Dillon NG_MKRESPONSE(resp, msg, NGRESP_SIZE, M_NOWAIT); 412*b06ebda0SMatthew Dillon 413*b06ebda0SMatthew Dillon if (!resp) 414*b06ebda0SMatthew Dillon ERROUT(ENOMEM); 415*b06ebda0SMatthew Dillon 416*b06ebda0SMatthew Dillon error = ng_netflow_flow_show(priv, *last, resp); 417*b06ebda0SMatthew Dillon 418*b06ebda0SMatthew Dillon break; 419*b06ebda0SMatthew Dillon } 420*b06ebda0SMatthew Dillon default: 421*b06ebda0SMatthew Dillon ERROUT(EINVAL); /* unknown command */ 422*b06ebda0SMatthew Dillon break; 423*b06ebda0SMatthew Dillon } 424*b06ebda0SMatthew Dillon break; 425*b06ebda0SMatthew Dillon default: 426*b06ebda0SMatthew Dillon ERROUT(EINVAL); /* incorrect cookie */ 427*b06ebda0SMatthew Dillon break; 428*b06ebda0SMatthew Dillon } 429*b06ebda0SMatthew Dillon 430*b06ebda0SMatthew Dillon /* 431*b06ebda0SMatthew Dillon * Take care of synchronous response, if any. 432*b06ebda0SMatthew Dillon * Free memory and return. 433*b06ebda0SMatthew Dillon */ 434*b06ebda0SMatthew Dillon done: 435*b06ebda0SMatthew Dillon NG_RESPOND_MSG(error, node, item, resp); 436*b06ebda0SMatthew Dillon NG_FREE_MSG(msg); 437*b06ebda0SMatthew Dillon 438*b06ebda0SMatthew Dillon return (error); 439*b06ebda0SMatthew Dillon } 440*b06ebda0SMatthew Dillon 441*b06ebda0SMatthew Dillon /* Receive data on hook. */ 442*b06ebda0SMatthew Dillon static int 443*b06ebda0SMatthew Dillon ng_netflow_rcvdata (hook_p hook, item_p item) 444*b06ebda0SMatthew Dillon { 445*b06ebda0SMatthew Dillon const node_p node = NG_HOOK_NODE(hook); 446*b06ebda0SMatthew Dillon const priv_p priv = NG_NODE_PRIVATE(node); 447*b06ebda0SMatthew Dillon const iface_p iface = NG_HOOK_PRIVATE(hook); 448*b06ebda0SMatthew Dillon struct mbuf *m = NULL; 449*b06ebda0SMatthew Dillon struct ip *ip; 450*b06ebda0SMatthew Dillon int pullup_len = 0; 451*b06ebda0SMatthew Dillon int error = 0; 452*b06ebda0SMatthew Dillon 453*b06ebda0SMatthew Dillon if (hook == priv->export) { 454*b06ebda0SMatthew Dillon /* 455*b06ebda0SMatthew Dillon * Data arrived on export hook. 456*b06ebda0SMatthew Dillon * This must not happen. 457*b06ebda0SMatthew Dillon */ 458*b06ebda0SMatthew Dillon log(LOG_ERR, "ng_netflow: incoming data on export hook!\n"); 459*b06ebda0SMatthew Dillon ERROUT(EINVAL); 460*b06ebda0SMatthew Dillon }; 461*b06ebda0SMatthew Dillon 462*b06ebda0SMatthew Dillon if (hook == iface->out) { 463*b06ebda0SMatthew Dillon /* 464*b06ebda0SMatthew Dillon * Data arrived on out hook. Bypass it. 465*b06ebda0SMatthew Dillon */ 466*b06ebda0SMatthew Dillon if (iface->hook == NULL) 467*b06ebda0SMatthew Dillon ERROUT(ENOTCONN); 468*b06ebda0SMatthew Dillon 469*b06ebda0SMatthew Dillon NG_FWD_ITEM_HOOK(error, item, iface->hook); 470*b06ebda0SMatthew Dillon return (error); 471*b06ebda0SMatthew Dillon } 472*b06ebda0SMatthew Dillon 473*b06ebda0SMatthew Dillon NGI_GET_M(item, m); 474*b06ebda0SMatthew Dillon 475*b06ebda0SMatthew Dillon /* Increase counters. */ 476*b06ebda0SMatthew Dillon iface->info.ifinfo_packets++; 477*b06ebda0SMatthew Dillon 478*b06ebda0SMatthew Dillon /* 479*b06ebda0SMatthew Dillon * Depending on interface data link type and packet contents 480*b06ebda0SMatthew Dillon * we pullup enough data, so that ng_netflow_flow_add() does not 481*b06ebda0SMatthew Dillon * need to know about mbuf at all. We keep current length of data 482*b06ebda0SMatthew Dillon * needed to be contiguous in pullup_len. mtod() is done at the 483*b06ebda0SMatthew Dillon * very end one more time, since m can had changed after pulluping. 484*b06ebda0SMatthew Dillon * 485*b06ebda0SMatthew Dillon * In case of unrecognized data we don't return error, but just 486*b06ebda0SMatthew Dillon * pass data to downstream hook, if it is available. 487*b06ebda0SMatthew Dillon */ 488*b06ebda0SMatthew Dillon 489*b06ebda0SMatthew Dillon #define M_CHECK(length) do { \ 490*b06ebda0SMatthew Dillon pullup_len += length; \ 491*b06ebda0SMatthew Dillon if ((m)->m_pkthdr.len < (pullup_len)) { \ 492*b06ebda0SMatthew Dillon error = EINVAL; \ 493*b06ebda0SMatthew Dillon goto bypass; \ 494*b06ebda0SMatthew Dillon } \ 495*b06ebda0SMatthew Dillon if ((m)->m_len < (pullup_len) && \ 496*b06ebda0SMatthew Dillon (((m) = m_pullup((m),(pullup_len))) == NULL)) { \ 497*b06ebda0SMatthew Dillon error = ENOBUFS; \ 498*b06ebda0SMatthew Dillon goto done; \ 499*b06ebda0SMatthew Dillon } \ 500*b06ebda0SMatthew Dillon } while (0) 501*b06ebda0SMatthew Dillon 502*b06ebda0SMatthew Dillon switch (iface->info.ifinfo_dlt) { 503*b06ebda0SMatthew Dillon case DLT_EN10MB: /* Ethernet */ 504*b06ebda0SMatthew Dillon { 505*b06ebda0SMatthew Dillon struct ether_header *eh; 506*b06ebda0SMatthew Dillon uint16_t etype; 507*b06ebda0SMatthew Dillon 508*b06ebda0SMatthew Dillon M_CHECK(sizeof(struct ether_header)); 509*b06ebda0SMatthew Dillon eh = mtod(m, struct ether_header *); 510*b06ebda0SMatthew Dillon 511*b06ebda0SMatthew Dillon /* Make sure this is IP frame. */ 512*b06ebda0SMatthew Dillon etype = ntohs(eh->ether_type); 513*b06ebda0SMatthew Dillon switch (etype) { 514*b06ebda0SMatthew Dillon case ETHERTYPE_IP: 515*b06ebda0SMatthew Dillon M_CHECK(sizeof(struct ip)); 516*b06ebda0SMatthew Dillon eh = mtod(m, struct ether_header *); 517*b06ebda0SMatthew Dillon ip = (struct ip *)(eh + 1); 518*b06ebda0SMatthew Dillon break; 519*b06ebda0SMatthew Dillon case ETHERTYPE_VLAN: 520*b06ebda0SMatthew Dillon { 521*b06ebda0SMatthew Dillon struct ether_vlan_header *evh; 522*b06ebda0SMatthew Dillon 523*b06ebda0SMatthew Dillon M_CHECK(sizeof(struct ether_vlan_header) - 524*b06ebda0SMatthew Dillon sizeof(struct ether_header)); 525*b06ebda0SMatthew Dillon evh = mtod(m, struct ether_vlan_header *); 526*b06ebda0SMatthew Dillon if (ntohs(evh->evl_proto) == ETHERTYPE_IP) { 527*b06ebda0SMatthew Dillon M_CHECK(sizeof(struct ip)); 528*b06ebda0SMatthew Dillon ip = (struct ip *)(evh + 1); 529*b06ebda0SMatthew Dillon break; 530*b06ebda0SMatthew Dillon } 531*b06ebda0SMatthew Dillon } 532*b06ebda0SMatthew Dillon default: 533*b06ebda0SMatthew Dillon goto bypass; /* pass this frame */ 534*b06ebda0SMatthew Dillon } 535*b06ebda0SMatthew Dillon break; 536*b06ebda0SMatthew Dillon } 537*b06ebda0SMatthew Dillon case DLT_RAW: /* IP packets */ 538*b06ebda0SMatthew Dillon M_CHECK(sizeof(struct ip)); 539*b06ebda0SMatthew Dillon ip = mtod(m, struct ip *); 540*b06ebda0SMatthew Dillon break; 541*b06ebda0SMatthew Dillon default: 542*b06ebda0SMatthew Dillon goto bypass; 543*b06ebda0SMatthew Dillon break; 544*b06ebda0SMatthew Dillon } 545*b06ebda0SMatthew Dillon 546*b06ebda0SMatthew Dillon if ((ip->ip_off & htons(IP_OFFMASK)) == 0) { 547*b06ebda0SMatthew Dillon /* 548*b06ebda0SMatthew Dillon * In case of IP header with options, we haven't pulled 549*b06ebda0SMatthew Dillon * up enough, yet. 550*b06ebda0SMatthew Dillon */ 551*b06ebda0SMatthew Dillon pullup_len += (ip->ip_hl << 2) - sizeof(struct ip); 552*b06ebda0SMatthew Dillon 553*b06ebda0SMatthew Dillon switch (ip->ip_p) { 554*b06ebda0SMatthew Dillon case IPPROTO_TCP: 555*b06ebda0SMatthew Dillon M_CHECK(sizeof(struct tcphdr)); 556*b06ebda0SMatthew Dillon break; 557*b06ebda0SMatthew Dillon case IPPROTO_UDP: 558*b06ebda0SMatthew Dillon M_CHECK(sizeof(struct udphdr)); 559*b06ebda0SMatthew Dillon break; 560*b06ebda0SMatthew Dillon } 561*b06ebda0SMatthew Dillon } 562*b06ebda0SMatthew Dillon 563*b06ebda0SMatthew Dillon switch (iface->info.ifinfo_dlt) { 564*b06ebda0SMatthew Dillon case DLT_EN10MB: 565*b06ebda0SMatthew Dillon { 566*b06ebda0SMatthew Dillon struct ether_header *eh; 567*b06ebda0SMatthew Dillon 568*b06ebda0SMatthew Dillon eh = mtod(m, struct ether_header *); 569*b06ebda0SMatthew Dillon switch (ntohs(eh->ether_type)) { 570*b06ebda0SMatthew Dillon case ETHERTYPE_IP: 571*b06ebda0SMatthew Dillon ip = (struct ip *)(eh + 1); 572*b06ebda0SMatthew Dillon break; 573*b06ebda0SMatthew Dillon case ETHERTYPE_VLAN: 574*b06ebda0SMatthew Dillon { 575*b06ebda0SMatthew Dillon struct ether_vlan_header *evh; 576*b06ebda0SMatthew Dillon 577*b06ebda0SMatthew Dillon evh = mtod(m, struct ether_vlan_header *); 578*b06ebda0SMatthew Dillon ip = (struct ip *)(evh + 1); 579*b06ebda0SMatthew Dillon break; 580*b06ebda0SMatthew Dillon } 581*b06ebda0SMatthew Dillon default: 582*b06ebda0SMatthew Dillon panic("ng_netflow entered deadcode"); 583*b06ebda0SMatthew Dillon } 584*b06ebda0SMatthew Dillon break; 585*b06ebda0SMatthew Dillon } 586*b06ebda0SMatthew Dillon case DLT_RAW: 587*b06ebda0SMatthew Dillon ip = mtod(m, struct ip *); 588*b06ebda0SMatthew Dillon break; 589*b06ebda0SMatthew Dillon default: 590*b06ebda0SMatthew Dillon panic("ng_netflow entered deadcode"); 591*b06ebda0SMatthew Dillon } 592*b06ebda0SMatthew Dillon 593*b06ebda0SMatthew Dillon #undef M_CHECK 594*b06ebda0SMatthew Dillon 595*b06ebda0SMatthew Dillon error = ng_netflow_flow_add(priv, ip, iface, m->m_pkthdr.rcvif); 596*b06ebda0SMatthew Dillon 597*b06ebda0SMatthew Dillon bypass: 598*b06ebda0SMatthew Dillon if (iface->out != NULL) { 599*b06ebda0SMatthew Dillon /* XXX: error gets overwritten here */ 600*b06ebda0SMatthew Dillon NG_FWD_NEW_DATA(error, item, iface->out, m); 601*b06ebda0SMatthew Dillon return (error); 602*b06ebda0SMatthew Dillon } 603*b06ebda0SMatthew Dillon done: 604*b06ebda0SMatthew Dillon if (item) 605*b06ebda0SMatthew Dillon NG_FREE_ITEM(item); 606*b06ebda0SMatthew Dillon if (m) 607*b06ebda0SMatthew Dillon NG_FREE_M(m); 608*b06ebda0SMatthew Dillon 609*b06ebda0SMatthew Dillon return (error); 610*b06ebda0SMatthew Dillon } 611*b06ebda0SMatthew Dillon 612*b06ebda0SMatthew Dillon /* We will be shut down in a moment */ 613*b06ebda0SMatthew Dillon static int 614*b06ebda0SMatthew Dillon ng_netflow_close(node_p node) 615*b06ebda0SMatthew Dillon { 616*b06ebda0SMatthew Dillon const priv_p priv = NG_NODE_PRIVATE(node); 617*b06ebda0SMatthew Dillon 618*b06ebda0SMatthew Dillon callout_drain(&priv->exp_callout); 619*b06ebda0SMatthew Dillon ng_netflow_cache_flush(priv); 620*b06ebda0SMatthew Dillon 621*b06ebda0SMatthew Dillon return (0); 622*b06ebda0SMatthew Dillon } 623*b06ebda0SMatthew Dillon 624*b06ebda0SMatthew Dillon /* Do local shutdown processing. */ 625*b06ebda0SMatthew Dillon static int 626*b06ebda0SMatthew Dillon ng_netflow_rmnode(node_p node) 627*b06ebda0SMatthew Dillon { 628*b06ebda0SMatthew Dillon const priv_p priv = NG_NODE_PRIVATE(node); 629*b06ebda0SMatthew Dillon 630*b06ebda0SMatthew Dillon NG_NODE_SET_PRIVATE(node, NULL); 631*b06ebda0SMatthew Dillon NG_NODE_UNREF(priv->node); 632*b06ebda0SMatthew Dillon 633*b06ebda0SMatthew Dillon FREE(priv, M_NETGRAPH); 634*b06ebda0SMatthew Dillon 635*b06ebda0SMatthew Dillon return (0); 636*b06ebda0SMatthew Dillon } 637*b06ebda0SMatthew Dillon 638*b06ebda0SMatthew Dillon /* Hook disconnection. */ 639*b06ebda0SMatthew Dillon static int 640*b06ebda0SMatthew Dillon ng_netflow_disconnect(hook_p hook) 641*b06ebda0SMatthew Dillon { 642*b06ebda0SMatthew Dillon node_p node = NG_HOOK_NODE(hook); 643*b06ebda0SMatthew Dillon priv_p priv = NG_NODE_PRIVATE(node); 644*b06ebda0SMatthew Dillon iface_p iface = NG_HOOK_PRIVATE(hook); 645*b06ebda0SMatthew Dillon 646*b06ebda0SMatthew Dillon if (iface != NULL) { 647*b06ebda0SMatthew Dillon if (iface->hook == hook) 648*b06ebda0SMatthew Dillon iface->hook = NULL; 649*b06ebda0SMatthew Dillon if (iface->out == hook) 650*b06ebda0SMatthew Dillon iface->out = NULL; 651*b06ebda0SMatthew Dillon } 652*b06ebda0SMatthew Dillon 653*b06ebda0SMatthew Dillon /* if export hook disconnected stop running expire(). */ 654*b06ebda0SMatthew Dillon if (hook == priv->export) { 655*b06ebda0SMatthew Dillon callout_drain(&priv->exp_callout); 656*b06ebda0SMatthew Dillon priv->export = NULL; 657*b06ebda0SMatthew Dillon } 658*b06ebda0SMatthew Dillon 659*b06ebda0SMatthew Dillon /* Removal of the last link destroys the node. */ 660*b06ebda0SMatthew Dillon if (NG_NODE_NUMHOOKS(node) == 0) 661*b06ebda0SMatthew Dillon ng_rmnode_self(node); 662*b06ebda0SMatthew Dillon 663*b06ebda0SMatthew Dillon return (0); 664*b06ebda0SMatthew Dillon } 665