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