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: stable/10/sys/netgraph/netflow/ng_netflow.c 314667 2017-03-04 13:03:31Z avg $");
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 */
261314667Savg	callout_init(&priv->exp_callout, 1);
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