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>
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
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.
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
28 * $SourceForge: ng_netflow.c,v 1.30 2004/09/05 11:37:43 glebius Exp $
31 #include <sys/cdefs.h>
32 __FBSDID("$FreeBSD$");
34 #include "opt_inet6.h"
35 #include "opt_route.h"
37 #include <sys/param.h>
38 #include <sys/systm.h>
39 #include <sys/counter.h>
40 #include <sys/kernel.h>
42 #include <sys/limits.h>
43 #include <sys/malloc.h>
45 #include <sys/socket.h>
46 #include <sys/syslog.h>
47 #include <sys/ctype.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>
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>
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>
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;
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
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
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
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
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
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
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
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
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
153 /* List of commands and how to convert arguments to/from ASCII */
154 static const struct ng_cmdlist ng_netflow_cmds[] = {
160 &ng_netflow_info_type
166 &ng_parse_uint16_type,
167 &ng_netflow_ifinfo_type
173 &ng_netflow_setdlt_type,
178 NGM_NETFLOW_SETIFINDEX,
180 &ng_netflow_setifindex_type,
185 NGM_NETFLOW_SETTIMEOUTS,
187 &ng_netflow_settimeouts_type,
192 NGM_NETFLOW_SETCONFIG,
194 &ng_netflow_setconfig_type,
199 NGM_NETFLOW_SETTEMPLATE,
201 &ng_netflow_settemplate_type,
208 &ng_netflow_setmtu_type,
216 &ng_netflow_v9info_type
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,
235 NETGRAPH_INIT(netflow, &ng_netflow_typestruct);
237 /* Called at node creation */
239 ng_netflow_constructor(node_p node)
244 /* Initialize private data */
245 priv = malloc(sizeof(*priv), M_NETGRAPH, M_WAITOK | M_ZERO);
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);
252 /* Make node and its data point at each other */
253 NG_NODE_SET_PRIVATE(node, priv);
256 /* Initialize timeouts to default values */
257 priv->nfinfo_inact_t = INACTIVE_TIMEOUT;
258 priv->nfinfo_act_t = ACTIVE_TIMEOUT;
260 /* Set default config */
261 for (i = 0; i < NG_NETFLOW_MAXIFACES; i++)
262 priv->ifaces[i].info.conf = NG_NETFLOW_CONF_INGRESS;
264 /* Initialize callout handle */
265 callout_init(&priv->exp_callout, 1);
267 /* Allocate memory and set up flow cache */
268 ng_netflow_cache_init(priv);
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.
279 ng_netflow_newhook(node_p node, hook_p hook, const char *name)
281 const priv_p priv = NG_NODE_PRIVATE(node);
283 if (strncmp(name, NG_NETFLOW_HOOK_DATA, /* an iface hook? */
284 strlen(NG_NETFLOW_HOOK_DATA)) == 0) {
290 cp = name + strlen(NG_NETFLOW_HOOK_DATA);
291 if (!isdigit(*cp) || (cp[0] == '0' && cp[1] != '\0'))
294 ifnum = (int)strtoul(cp, &eptr, 10);
295 if (*eptr != '\0' || ifnum < 0 || ifnum >= NG_NETFLOW_MAXIFACES)
298 /* See if hook is already connected */
299 if (priv->ifaces[ifnum].hook != NULL)
302 iface = &priv->ifaces[ifnum];
304 /* Link private info and hook together */
305 NG_HOOK_SET_PRIVATE(hook, iface);
309 * In most cases traffic accounting is done on an
310 * Ethernet interface, so default data link type
311 * will be DLT_EN10MB.
313 iface->info.ifinfo_dlt = DLT_EN10MB;
315 } else if (strncmp(name, NG_NETFLOW_HOOK_OUT,
316 strlen(NG_NETFLOW_HOOK_OUT)) == 0) {
322 cp = name + strlen(NG_NETFLOW_HOOK_OUT);
323 if (!isdigit(*cp) || (cp[0] == '0' && cp[1] != '\0'))
326 ifnum = (int)strtoul(cp, &eptr, 10);
327 if (*eptr != '\0' || ifnum < 0 || ifnum >= NG_NETFLOW_MAXIFACES)
330 /* See if hook is already connected */
331 if (priv->ifaces[ifnum].out != NULL)
334 iface = &priv->ifaces[ifnum];
336 /* Link private info and hook together */
337 NG_HOOK_SET_PRIVATE(hook, iface);
340 } else if (strcmp(name, NG_NETFLOW_HOOK_EXPORT) == 0) {
342 if (priv->export != NULL)
345 /* Netflow version 5 supports 32-bit counters only */
346 if (CNTR_MAX == UINT64_MAX)
351 /* Exporter is ready. Let's schedule expiry. */
352 callout_reset(&priv->exp_callout, (1*hz), &ng_netflow_expire,
354 } else if (strcmp(name, NG_NETFLOW_HOOK_EXPORT9) == 0) {
356 if (priv->export9 != NULL)
359 priv->export9 = hook;
361 /* Exporter is ready. Let's schedule expiry. */
362 callout_reset(&priv->exp_callout, (1*hz), &ng_netflow_expire,
370 /* Get a netgraph control message. */
372 ng_netflow_rcvmsg (node_p node, item_p item, hook_p lasthook)
374 const priv_p priv = NG_NODE_PRIVATE(node);
375 struct ng_mesg *resp = NULL;
379 NGI_GET_MSG(item, msg);
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:
387 struct ng_netflow_info *i;
389 NG_MKRESPONSE(resp, msg, sizeof(struct ng_netflow_info),
391 i = (struct ng_netflow_info *)resp->data;
392 ng_netflow_copyinfo(priv, i);
396 case NGM_NETFLOW_IFINFO:
398 struct ng_netflow_ifinfo *i;
399 const uint16_t *index;
401 if (msg->header.arglen != sizeof(uint16_t))
404 index = (uint16_t *)msg->data;
405 if (*index >= NG_NETFLOW_MAXIFACES)
408 /* connected iface? */
409 if (priv->ifaces[*index].hook == NULL)
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));
420 case NGM_NETFLOW_SETDLT:
422 struct ng_netflow_setdlt *set;
423 struct ng_netflow_iface *iface;
425 if (msg->header.arglen !=
426 sizeof(struct ng_netflow_setdlt))
429 set = (struct ng_netflow_setdlt *)msg->data;
430 if (set->iface >= NG_NETFLOW_MAXIFACES)
432 iface = &priv->ifaces[set->iface];
434 /* connected iface? */
435 if (iface->hook == NULL)
440 iface->info.ifinfo_dlt = DLT_EN10MB;
443 iface->info.ifinfo_dlt = DLT_RAW;
450 case NGM_NETFLOW_SETIFINDEX:
452 struct ng_netflow_setifindex *set;
453 struct ng_netflow_iface *iface;
455 if (msg->header.arglen !=
456 sizeof(struct ng_netflow_setifindex))
459 set = (struct ng_netflow_setifindex *)msg->data;
460 if (set->iface >= NG_NETFLOW_MAXIFACES)
462 iface = &priv->ifaces[set->iface];
464 /* connected iface? */
465 if (iface->hook == NULL)
468 iface->info.ifinfo_index = set->index;
472 case NGM_NETFLOW_SETTIMEOUTS:
474 struct ng_netflow_settimeouts *set;
476 if (msg->header.arglen !=
477 sizeof(struct ng_netflow_settimeouts))
480 set = (struct ng_netflow_settimeouts *)msg->data;
482 priv->nfinfo_inact_t = set->inactive_timeout;
483 priv->nfinfo_act_t = set->active_timeout;
487 case NGM_NETFLOW_SETCONFIG:
489 struct ng_netflow_setconfig *set;
491 if (msg->header.arglen !=
492 sizeof(struct ng_netflow_setconfig))
495 set = (struct ng_netflow_setconfig *)msg->data;
497 if (set->iface >= NG_NETFLOW_MAXIFACES)
500 priv->ifaces[set->iface].info.conf = set->conf;
504 case NGM_NETFLOW_SETTEMPLATE:
506 struct ng_netflow_settemplate *set;
508 if (msg->header.arglen !=
509 sizeof(struct ng_netflow_settemplate))
512 set = (struct ng_netflow_settemplate *)msg->data;
514 priv->templ_packets = set->packets;
515 priv->templ_time = set->time;
519 case NGM_NETFLOW_SETMTU:
521 struct ng_netflow_setmtu *set;
523 if (msg->header.arglen !=
524 sizeof(struct ng_netflow_setmtu))
527 set = (struct ng_netflow_setmtu *)msg->data;
528 if ((set->mtu < MIN_MTU) || (set->mtu > MAX_MTU))
531 priv->mtu = set->mtu;
535 case NGM_NETFLOW_SHOW:
536 if (msg->header.arglen !=
537 sizeof(struct ngnf_show_header))
540 NG_MKRESPONSE(resp, msg, NGRESP_SIZE, M_NOWAIT);
545 error = ng_netflow_flow_show(priv,
546 (struct ngnf_show_header *)msg->data,
547 (struct ngnf_show_header *)resp->data);
553 case NGM_NETFLOW_V9INFO:
555 struct ng_netflow_v9info *i;
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);
565 ERROUT(EINVAL); /* unknown command */
570 ERROUT(EINVAL); /* incorrect cookie */
575 * Take care of synchronous response, if any.
576 * Free memory and return.
579 NG_RESPOND_MSG(error, node, item, resp);
585 /* Receive data on hook. */
587 ng_netflow_rcvdata (hook_p hook, item_p item)
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);
593 struct mbuf *m = NULL, *m_old = NULL;
594 struct ip *ip = NULL;
595 struct ip6_hdr *ip6 = NULL;
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;
605 if ((hook == priv->export) || (hook == priv->export9)) {
607 * Data arrived on export hook.
608 * This must not happen.
610 log(LOG_ERR, "ng_netflow: incoming data on export hook!\n");
614 if (hook == iface->hook) {
615 if ((iface->info.conf & NG_NETFLOW_CONF_INGRESS) == 0)
618 } else if (hook == iface->out) {
619 if ((iface->info.conf & NG_NETFLOW_CONF_EGRESS) == 0)
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)) {
635 mtag = m_tag_locate(NGI_M(item), MTAG_NETFLOW,
636 MTAG_NETFLOW_CALLED, mtag);
644 NG_FWD_ITEM_HOOK(error, item, out);
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);
653 ((ng_ID_t *)(mtag + 1))[0] = NG_NODE_ID(node);
654 m_tag_prepend(NGI_M(item), mtag);
658 /* Import configuration flags related to flow creation */
659 flags = iface->info.conf & NG_NETFLOW_FLOW_FLAGS;
664 /* Increase counters. */
665 iface->info.ifinfo_packets++;
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.
674 * In case of unrecognized data we don't return error, but just
675 * pass data to downstream hook, if it is available.
678 #define M_CHECK(length) do { \
679 pullup_len += length; \
680 if (((m)->m_pkthdr.len < (pullup_len)) || \
681 ((pullup_len) > MHLEN)) { \
685 if ((m)->m_len < (pullup_len) && \
686 (((m) = m_pullup((m),(pullup_len))) == NULL)) { \
692 switch (iface->info.ifinfo_dlt) {
693 case DLT_EN10MB: /* Ethernet */
695 struct ether_header *eh;
698 M_CHECK(sizeof(struct ether_header));
699 eh = mtod(m, struct ether_header *);
701 /* Make sure this is IP frame. */
702 etype = ntohs(eh->ether_type);
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);
713 * m_pullup() called by M_CHECK() pullups
714 * kern.ipc.max_protohdr (default 60 bytes)
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);
725 struct ether_vlan_header *evh;
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);
733 if (etype == ETHERTYPE_IP) {
734 M_CHECK(sizeof(struct ip));
735 ip = (struct ip *)(evh + 1);
738 } else if (etype == ETHERTYPE_IPV6) {
739 M_CHECK(sizeof(struct ip6_hdr));
740 ip6 = (struct ip6_hdr *)(evh + 1);
746 goto bypass; /* pass this frame */
750 case DLT_RAW: /* IP packets */
751 M_CHECK(sizeof(struct ip));
752 ip = mtod(m, struct ip *);
753 /* l3_off is already zero */
756 * If INET6 is not defined IPv6 packets
757 * will be discarded in ng_netflow_flow_add().
759 if (ip->ip_v == IP6VERSION) {
761 M_CHECK(sizeof(struct ip6_hdr) - sizeof(struct ip));
762 ip6 = mtod(m, struct ip6_hdr *);
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)))
778 * In case of IPv4 header with options, we haven't pulled
781 M_CHECK((ip->ip_hl << 2) - sizeof(struct ip));
783 /* Save upper layer offset and proto */
785 upper_proto = ip->ip_p;
788 * XXX: in case of wrong upper layer header we will
789 * forward this packet but skip this record in netflow.
793 M_CHECK(sizeof(struct tcphdr));
796 M_CHECK(sizeof(struct udphdr));
799 M_CHECK(sizeof(struct sctphdr));
802 } else if (ip != NULL) {
804 * Nothing to save except upper layer proto,
805 * since this is a packet fragment.
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)))
813 } else if (ip6 != NULL) {
814 int cur = ip6->ip6_nxt, hdr_off = 0;
815 struct ip6_ext *ip6e;
816 struct ip6_frag *ip6f;
818 if (priv->export9 == NULL)
821 /* Save upper layer info. */
825 if ((ip6->ip6_vfc & IPV6_VERSION_MASK) != IPV6_VERSION)
829 * Loop through IPv6 extended headers to get upper
830 * layer header / frag.
835 * Same as in IPv4, we can forward a 'bad'
836 * packet without accounting.
839 M_CHECK(sizeof(struct tcphdr));
842 M_CHECK(sizeof(struct udphdr));
845 M_CHECK(sizeof(struct sctphdr));
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) +
855 upper_proto = ip6e->ip6e_nxt;
856 hdr_off = (ip6e->ip6e_len + 1) << 3;
859 /* RFC4302, can be before DSTOPTS */
861 M_CHECK(sizeof(struct ip6_ext));
862 ip6e = (struct ip6_ext *)(mtod(m, caddr_t) +
864 upper_proto = ip6e->ip6e_nxt;
865 hdr_off = (ip6e->ip6e_len + 2) << 2;
868 case IPPROTO_FRAGMENT:
869 M_CHECK(sizeof(struct ip6_frag));
870 ip6f = (struct ip6_frag *)(mtod(m, caddr_t) +
872 upper_proto = ip6f->ip6f_nxt;
873 hdr_off = sizeof(struct ip6_frag);
875 flags |= NG_NETFLOW_IS_FRAG;
883 * Any unknown header (new extension or IPv6/IPv4
884 * header for tunnels) ends loop.
900 /* Just in case of real reallocation in M_CHECK() / m_pullup() */
902 priv->nfinfo_realloc_mbuf++;
903 /* Restore ip/ipv6 pointer */
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);
910 upper_ptr = (caddr_t)(mtod(m, caddr_t) + off);
912 /* Determine packet input interface. Prefer configured. */
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;
918 src_if_index = iface->info.ifinfo_index;
920 /* Check packet FIB */
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",
929 if ((fe = priv_to_fib(priv, fib)) == NULL) {
931 if (ng_netflow_fib_init(priv, fib) != 0) {
932 /* malloc() failed */
936 fe = priv_to_fib(priv, fib);
940 error = ng_netflow_flow_add(priv, fe, ip, upper_ptr,
941 upper_proto, flags, src_if_index);
943 else if (ip6 != NULL)
944 error = ng_netflow_flow6_add(priv, fe, ip6, upper_ptr,
945 upper_proto, flags, src_if_index);
954 /* Accounting failure */
956 counter_u64_add(priv->nfinfo_spackets, 1);
957 counter_u64_add(priv->nfinfo_sbytes,
959 } else if (ip6 != NULL) {
960 counter_u64_add(priv->nfinfo_spackets6, 1);
961 counter_u64_add(priv->nfinfo_sbytes6,
966 /* XXX: error gets overwritten here */
967 NG_FWD_NEW_DATA(error, item, out, m);
979 /* We will be shut down in a moment */
981 ng_netflow_close(node_p node)
983 const priv_p priv = NG_NODE_PRIVATE(node);
985 callout_drain(&priv->exp_callout);
986 ng_netflow_cache_flush(priv);
991 /* Do local shutdown processing. */
993 ng_netflow_rmnode(node_p node)
995 const priv_p priv = NG_NODE_PRIVATE(node);
997 NG_NODE_SET_PRIVATE(node, NULL);
998 NG_NODE_UNREF(priv->node);
1000 free(priv->fib_data, M_NETGRAPH);
1001 free(priv, M_NETGRAPH);
1006 /* Hook disconnection. */
1008 ng_netflow_disconnect(hook_p hook)
1010 node_p node = NG_HOOK_NODE(hook);
1011 priv_p priv = NG_NODE_PRIVATE(node);
1012 iface_p iface = NG_HOOK_PRIVATE(hook);
1014 if (iface != NULL) {
1015 if (iface->hook == hook)
1017 if (iface->out == hook)
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;
1028 if (hook == priv->export9) {
1029 if (priv->export == NULL)
1030 callout_drain(&priv->exp_callout);
1031 priv->export9 = NULL;
1034 /* Removal of the last link destroys the node. */
1035 if (NG_NODE_NUMHOOKS(node) == 0)
1036 ng_rmnode_self(node);