xref: /freebsd/sys/netgraph/netflow/ng_netflow.c (revision 193d9e768ba63fcfb187cfd17f461f7d41345048)
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$");
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 */
73 static ng_constructor_t	ng_netflow_constructor;
74 static ng_rcvmsg_t	ng_netflow_rcvmsg;
75 static ng_close_t	ng_netflow_close;
76 static ng_shutdown_t	ng_netflow_rmnode;
77 static ng_newhook_t	ng_netflow_newhook;
78 static ng_rcvdata_t	ng_netflow_rcvdata;
79 static ng_disconnect_t	ng_netflow_disconnect;
80 
81 /* Parse type for struct ng_netflow_info */
82 static const struct ng_parse_struct_field ng_netflow_info_type_fields[]
83 	= NG_NETFLOW_INFO_TYPE;
84 static 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 */
90 static const struct ng_parse_struct_field ng_netflow_ifinfo_type_fields[]
91 	= NG_NETFLOW_IFINFO_TYPE;
92 static 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 */
98 static const struct ng_parse_struct_field ng_netflow_setdlt_type_fields[]
99 	= NG_NETFLOW_SETDLT_TYPE;
100 static 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 */
106 static const struct ng_parse_struct_field ng_netflow_setifindex_type_fields[]
107 	= NG_NETFLOW_SETIFINDEX_TYPE;
108 static 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 */
114 static const struct ng_parse_struct_field ng_netflow_settimeouts_type_fields[]
115 	= NG_NETFLOW_SETTIMEOUTS_TYPE;
116 static 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 */
122 static const struct ng_parse_struct_field ng_netflow_setconfig_type_fields[]
123 	= NG_NETFLOW_SETCONFIG_TYPE;
124 static 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 */
130 static const struct ng_parse_struct_field ng_netflow_settemplate_type_fields[]
131 	= NG_NETFLOW_SETTEMPLATE_TYPE;
132 static 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 */
138 static const struct ng_parse_struct_field ng_netflow_setmtu_type_fields[]
139 	= NG_NETFLOW_SETMTU_TYPE;
140 static 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 */
146 static const struct ng_parse_struct_field ng_netflow_v9info_type_fields[]
147 	= NG_NETFLOW_V9INFO_TYPE;
148 static 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 */
154 static 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 */
223 static 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 };
235 NETGRAPH_INIT(netflow, &ng_netflow_typestruct);
236 
237 /* Called at node creation */
238 static int
239 ng_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  */
278 static int
279 ng_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. */
371 static int
372 ng_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 	 */
578 done:
579 	NG_RESPOND_MSG(error, node, item, resp);
580 	NG_FREE_MSG(msg);
581 
582 	return (error);
583 }
584 
585 /* Receive data on hook. */
586 static int
587 ng_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
898 loopend:
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;
951 bypass:
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 	}
970 done:
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 */
980 static int
981 ng_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. */
992 static int
993 ng_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. */
1007 static int
1008 ng_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