Call for testers: ng_netflow with v9 and IPv6 support

"Alexander V. Chernikov" <[email protected]>
Newsgroups gmane.os.freebsd.devel.net,gmane.os.freebsd.isp
Message-ID <[email protected]>
Hi!

There are patches for 7.2R and . to make ng_netflow export IPv4/IPv6 
flows via V9 protocol (RFC3954). Your feedback is welcome.
Alternative links:
http://static.ipfw.ru/patches/netflow_v9_20090906_72.diff
http://static.ipfw.ru/patches/netflow_v9_20090906_curr.diff

node runtime control messages:
Set export version:
setversion { version=X }. X=[59], defaults to  9
Set export interface mtu
setmtu { mtu=X }. X has to be <= MHLEN at the moment, defaults to 1500
Set v9 template announce conditions
settemplateperiodic { time=X packets=Y }. Send templates every X seconds 
or Y packets, X=600, Y=500 by default

_______________________________________________
[email protected] mailing list
http://lists.freebsd.org/mailman/listinfo/freebsd-net
To unsubscribe, send any mail to "[email protected]"
netflow_v9_20090906_curr.diff (text/plain, 56.7 KB)
diff -urN sys/netgraph/_netflow.orig/netflow.c sys/netgraph/netflow/netflow.c
--- sys/netgraph/_netflow.orig/netflow.c	2009-09-01 02:37:44.000000000 +0400
+++ sys/netgraph/netflow/netflow.c	2009-09-06 17:04:28.000000000 +0400
@@ -30,6 +30,8 @@
 static const char rcs_id[] =
     "@(#) $FreeBSD: src/sys/netgraph/netflow/netflow.c,v 1.33 2008/12/15 06:10:57 qingli Exp $";
 
+#include "opt_inet6.h"
+
 #include <sys/param.h>
 #include <sys/kernel.h>
 #include <sys/limits.h>
@@ -37,14 +39,18 @@
 #include <sys/syslog.h>
 #include <sys/systm.h>
 #include <sys/socket.h>
+#include <sys/endian.h>
 
 #include <machine/atomic.h>
+#include <machine/stdarg.h>
 
 #include <net/if.h>
 #include <net/route.h>
+#include <net/ethernet.h>
 #include <netinet/in.h>
 #include <netinet/in_systm.h>
 #include <netinet/ip.h>
+#include <netinet/ip6.h>
 #include <netinet/tcp.h>
 #include <netinet/udp.h>
 
@@ -94,12 +100,99 @@
 			 ((t) << 6) +	/* 64  */	\
 			 ((t) << 5) +	/* 32  */	\
 			 ((t) << 3))	/* 8   */
+/*
+ * Defines for different neflow version dispatchers
+ *
+ */
+static int export_add(item_p, struct flow_entry *);
+static int export_add_v9(item_p, struct flow_entry *);
+static int export_send(priv_p, item_p, int flags);
+static int export_send_v9(priv_p, item_p, int flags);
+
+typedef int (*record_add_ptr)(item_p, struct flow_entry *);
+typedef int (*record_send_ptr)(priv_p, item_p, int);
+struct _netflow_dispatchers {
+	record_add_ptr	record_add;
+	record_send_ptr	record_send;
+};
+
+static struct _netflow_dispatchers netflow_dispatcher[] =
+{
+	{ NULL, NULL },
+	{ NULL, NULL },
+	{ NULL, NULL },
+	{ NULL, NULL },
+	{ NULL, NULL },
+	{ export_add, export_send }, /* Version 5 */
+	{ NULL, NULL },
+	{ NULL, NULL },
+	{ NULL, NULL },
+	{ export_add_v9, export_send_v9 }, /* Version 9 */
+	{ NULL, NULL }
+};
+
+record_add_ptr record_add = NULL;
+record_send_ptr record_send = NULL;
 
 MALLOC_DECLARE(M_NETFLOW_HASH);
 MALLOC_DEFINE(M_NETFLOW_HASH, "netflow_hash", "NetFlow hash");
 
-static int export_add(item_p, struct flow_entry *);
-static int export_send(priv_p, item_p, int flags);
+
+static struct netflow_v9_template _netflow_v9_record_ipv4_tcp[] = 
+{
+	{ NETFLOW_V9_FIELD_IPV4_SRC_ADDR, 4},
+	{ NETFLOW_V9_FIELD_IPV4_DST_ADDR, 4},
+	{ NETFLOW_V9_FIELD_IPV4_NEXT_HOP, 4},
+	{ NETFLOW_V9_FIELD_INPUT_SNMP, 2},
+	{ NETFLOW_V9_FIELD_OUTPUT_SNMP, 2},
+	{ NETFLOW_V9_FIELD_IN_PKTS, sizeof(cntr)},
+	{ NETFLOW_V9_FIELD_IN_BYTES, sizeof(cntr)},
+	{ NETFLOW_V9_FIELD_OUT_PKTS, sizeof(cntr)},
+	{ NETFLOW_V9_FIELD_OUT_BYTES, sizeof(cntr)},
+	{ NETFLOW_V9_FIELD_FIRST_SWITCHED, 4},
+	{ NETFLOW_V9_FIELD_LAST_SWITCHED, 4},
+	{ NETFLOW_V9_FIELD_L4_SRC_PORT, 2},
+	{ NETFLOW_V9_FIELD_L4_DST_PORT, 2},
+	{ NETFLOW_V9_FIELD_TCP_FLAGS, 1},
+	{ NETFLOW_V9_FIELD_PROTOCOL, 1},
+	{ NETFLOW_V9_FIELD_TOS, 1},
+	{ NETFLOW_V9_FIELD_SRC_AS, 4},
+	{ NETFLOW_V9_FIELD_DST_AS, 4},
+	{ NETFLOW_V9_FIELD_SRC_MASK, 1},
+	{ NETFLOW_V9_FIELD_DST_MASK, 1},
+	{0, 0}
+};
+
+static struct netflow_v9_template _netflow_v9_record_ipv6_tcp[] = 
+{
+	{ NETFLOW_V9_FIELD_IPV6_SRC_ADDR, 16},
+	{ NETFLOW_V9_FIELD_IPV6_DST_ADDR, 16},
+	{ NETFLOW_V9_FIELD_IPV6_NEXT_HOP, 16},
+	{ NETFLOW_V9_FIELD_INPUT_SNMP, 2},
+	{ NETFLOW_V9_FIELD_OUTPUT_SNMP, 2},
+	{ NETFLOW_V9_FIELD_IN_PKTS, sizeof(cntr)},
+	{ NETFLOW_V9_FIELD_IN_BYTES, sizeof(cntr)},
+	{ NETFLOW_V9_FIELD_OUT_PKTS, sizeof(cntr)},
+	{ NETFLOW_V9_FIELD_OUT_BYTES, sizeof(cntr)},
+	{ NETFLOW_V9_FIELD_FIRST_SWITCHED, 4},
+	{ NETFLOW_V9_FIELD_LAST_SWITCHED, 4},
+	{ NETFLOW_V9_FIELD_L4_SRC_PORT, 2},
+	{ NETFLOW_V9_FIELD_L4_DST_PORT, 2},
+	{ NETFLOW_V9_FIELD_TCP_FLAGS, 1},
+	{ NETFLOW_V9_FIELD_PROTOCOL, 1},
+	{ NETFLOW_V9_FIELD_TOS, 1},
+	{ NETFLOW_V9_FIELD_SRC_AS, 4},
+	{ NETFLOW_V9_FIELD_DST_AS, 4},
+	{ NETFLOW_V9_FIELD_SRC_MASK, 1},
+	{ NETFLOW_V9_FIELD_DST_MASK, 1},
+	{0, 0}
+};
+
+
+static int generate_v9_templates(priv_p priv);
+
+MALLOC_DECLARE(M_NETFLOW_GENERAL);
+MALLOC_DEFINE(M_NETFLOW_GENERAL, "netflog_general", "plog, templates data");
 
 /* Generate hash for a given flow record. */
 static __inline uint32_t
@@ -108,10 +201,24 @@
 	switch (r->r_ip_p) {
 	case IPPROTO_TCP:
 	case IPPROTO_UDP:
-		return FULL_HASH(r->r_src.s_addr, r->r_dst.s_addr,
+		return FULL_HASH(r->src.r_src.s_addr, r->dst.r_dst.s_addr,
 		    r->r_sport, r->r_dport);
 	default:
-		return ADDR_HASH(r->r_src.s_addr, r->r_dst.s_addr);
+		return ADDR_HASH(r->src.r_src.s_addr, r->dst.r_dst.s_addr);
+	}
+}
+
+/* Generate hash for a given flow record. Use lower 4 octets from v6 addresses */
+static __inline uint32_t
+ip6_hash(struct flow_rec *r)
+{
+	switch (r->r_ip_p) {
+	case IPPROTO_TCP:
+	case IPPROTO_UDP:
+		return FULL_HASH(r->src.r_src6.__u6_addr.__u6_addr32[3], r->dst.r_dst6.__u6_addr.__u6_addr32[3],
+		    r->r_sport, r->r_dport);
+	default:
+		return ADDR_HASH(r->src.r_src6.__u6_addr.__u6_addr32[3], r->dst.r_dst6.__u6_addr.__u6_addr32[3]);
 	}
 }
 
@@ -126,6 +233,7 @@
 
 	atomic_add_32(&priv->info.nfinfo_used, 1);
 
+
 	return (0);
 }
 
@@ -141,6 +249,7 @@
 /*
  * Detach export datagram from priv, if there is any.
  * If there is no, allocate a new one.
+ * -- V9/IPv6 ready
  */
 static item_p
 get_export_dgram(priv_p priv)
@@ -166,14 +275,163 @@
 			return (NULL);
 		dgram = mtod(m, struct netflow_v5_export_dgram *);
 		dgram->header.count = 0;
-		dgram->header.version = htons(NETFLOW_V5);
+		dgram->header.version = htons(priv->version);
+
+		if (priv->version == NETFLOW_V9) {
+			atomic_fetchadd_32(&priv->sent_packets, 1);
+
+			/*
+			 * Let's insert mbuf tag to store some info
+			 */
+			struct netflow_v9_mbuf_tag *t;
+			struct m_tag *mt = m_tag_alloc(MTAG_NETFLOW, MTAG_NETFLOW_V9, sizeof(struct netflow_v9_mbuf_tag), M_NOWAIT);
+			if (mt == NULL) {
+				m_freem(m);
+				return (NULL);
+			}
+
+			m_tag_init(m);
+			m_tag_prepend(m, mt);
 
+			t = (struct netflow_v9_mbuf_tag *)(mt + 1);
+			t->length = sizeof(struct netflow_v9_header);
+			t->count = 0;
+			t->mtu = priv->mtu;
+			t->flow_header = t->length;
+
+			/*
+			 * Check if we need to insert templates into packet
+			 */
+			
+			struct timespec ts;
+			struct netflow_v9_flowset_header	*fl;
+
+			getnanotime(&ts);
+			if ((ts.tv_sec >= priv->templ_time + priv->templ_last_ts) || (priv->sent_packets >= priv->templ_packets + priv->templ_last_pkt)) {
+				//vlog("INSERTING TEMPLATE: ts: %lu last: %u packets: %u last: %u delay: %u", ts.tv_sec, priv->templ_last_ts, priv->sent_packets, priv->templ_last_pkt, priv->templ_time);
+				atomic_store_rel_32(&priv->templ_last_ts, ts.tv_sec);
+				atomic_store_rel_32(&priv->templ_last_pkt, priv->sent_packets);
+				//vlog("ts: %lu last_t: %u last_p: %u delay: %u", ts.tv_sec, priv->templ_last_ts, priv->templ_last_pkt, priv->templ_time);
+
+				fl = priv->v9_flowsets[0];
+				bcopy(fl, (char *)dgram + t->length, ntohs(fl->length));
+
+				t->length += ntohs(fl->length);
+				t->flow_header = t->length;
+				t->count += priv->flowset_records[0];
+			}
+		
+		}
 	}
 
 	return (item);
 }
 
 /*
+ * Pre-compiles flow exporter for all possible FlowSets
+ * so we can add flowset to packet via simple memcpy()
+ */
+#define __push(x)	*p++ = htons((x))
+static int
+generate_v9_templates(priv_p priv)
+{
+	uint16_t *p, *template_fields_cnt;
+	int cnt;
+
+	int flowset_size = sizeof(struct netflow_v9_flowset_header) +
+		_NETFLOW_V9_TEMPLATE_SIZE(_netflow_v9_record_ipv4_tcp) + /* netflow_v9_record_ipv4_tcp */
+		_NETFLOW_V9_TEMPLATE_SIZE(_netflow_v9_record_ipv6_tcp); /* netflow_v9_record_ipv6_tcp */
+
+	priv->v9_flowsets[0] = malloc(flowset_size, M_NETFLOW_GENERAL, M_WAITOK | M_ZERO);
+	if (priv->v9_flowsets[0] == NULL)
+		return (ENOMEM);
+
+
+	if (flowset_size % 4)
+		flowset_size += 4 - (flowset_size % 4); /* Padding to 4-byte boundary */
+
+	priv->flowsets_count = 1;
+	p = (uint16_t *)priv->v9_flowsets[0];
+	*p++ = 0; /* Flowset ID, 0 is reserved for Template FlowSets  */
+	*p++ = htons(flowset_size); /* Total FlowSet length */
+
+	/*
+	 * Most common TCP/UDP IPv4 template, ID = 256
+	 */
+	*p++ = htons(0x100 + NETFLOW_V9_FLOW_V4_L4);
+	template_fields_cnt = p++;
+	for (cnt = 0; _netflow_v9_record_ipv4_tcp[cnt].field_id != 0; cnt++) {
+		*p++ = htons(_netflow_v9_record_ipv4_tcp[cnt].field_id);
+		*p++ = htons(_netflow_v9_record_ipv4_tcp[cnt].field_length);
+	}
+	*template_fields_cnt = htons(cnt);
+
+	/*
+	 * TCP/UDP IPv6 template, ID = 257
+	 */
+	*p++ = htons(0x100 + NETFLOW_V9_FLOW_V6_L4);
+	template_fields_cnt = p++;
+	for (cnt = 0; _netflow_v9_record_ipv6_tcp[cnt].field_id != 0; cnt++) {
+		*p++ = htons(_netflow_v9_record_ipv6_tcp[cnt].field_id);
+		*p++ = htons(_netflow_v9_record_ipv6_tcp[cnt].field_length);
+	}
+	*template_fields_cnt = htons(cnt);
+
+
+	priv->flowset_records[0] = 2;
+	return (0);
+}
+
+/*
+ * Switches version used for netflow export
+ *
+ */
+void 
+ng_netflow_switch_version(priv_p priv, int ver, int boot)
+{
+	item_p	item = NULL;
+
+	if ((ver != NETFLOW_V9) && (ver != NETFLOW_V5))
+		return;
+
+	if ((ver == priv->version) && (boot == 0))
+		return;
+
+	/*
+	 * All new threads acquiring export datagram will wait for lock
+	 * so we can change pointers.
+	 * Existing threads with export datagram already held will call
+	 * wrong export function which will do version check at the beginning
+	 */
+
+	mtx_lock(&priv->export_mtx);
+	/* XXX: Need to be machine-independent here */
+	record_add = netflow_dispatcher[ver].record_add;
+	record_send = netflow_dispatcher[ver].record_send;
+	//atomic_store_rel_ptr((unsigned long *)record_add, (unsigned long)netflow_dispatcher[ver].record_add);
+	//atomic_store_rel_ptr((unsigned long *)record_send, (unsigned long)netflow_dispatcher[ver].record_send);
+	item = priv->export_item;
+	priv->export_item = NULL;
+	mtx_unlock(&priv->export_mtx);
+
+	if (ver == NETFLOW_V9) {
+		priv->templ_last_pkt = 0;
+		priv->templ_last_ts = 0;
+	}
+
+	//vlog("NEW: add=%p send=%p", record_add, record_send);
+
+	if (item != NULL)
+		(*netflow_dispatcher[priv->version].record_send)(priv, item, NG_NOFLAGS);
+	
+	if (boot)
+		log(LOG_DEBUG, "ng_netflow: v%d export started\n", ver);
+	else
+		log(LOG_DEBUG, "ng_netflow: export switched: v%d -> v%d\n", priv->version, ver);
+	priv->version = ver;
+}
+
+/*
  * Re-attach incomplete datagram back to priv.
  * If there is already another one, then send incomplete. */
 static void
@@ -190,13 +448,14 @@
 		mtx_unlock(&priv->export_mtx);
 	} else {
 		mtx_unlock(&priv->export_mtx);
-		export_send(priv, item, flags);
+		(*record_send)(priv, item, flags);
 	}
 }
 
 /*
  * The flow is over. Call export_add() and free it. If datagram is
  * full, then call export_send().
+ * -- v9/IPv6 nearly ready
  */
 static __inline void
 expire_flow(priv_p priv, item_p *item, struct flow_entry *fle, int flags)
@@ -208,8 +467,8 @@
 		uma_zfree_arg(priv->zone, fle, priv);
 		return;
 	}
-	if (export_add(*item, fle) > 0) {
-		export_send(priv, *item, flags);
+	if ((*record_add)(*item, fle) > 0) {
+		(*record_send)(priv, *item, flags);
 		*item = NULL;
 	}
 	uma_zfree_arg(priv->zone, fle, priv);
@@ -234,11 +493,14 @@
  * to be sure.
  */
 static __inline int
-hash_insert(priv_p priv, struct flow_hash_entry  *hsh, struct flow_rec *r,
+hash_insert(priv_p priv, struct flow_hash_entry  *hsh, struct flow_rec *r, uint16_t eproto,
 	int plen, uint8_t tcp_flags)
 {
 	struct flow_entry *fle;
 	struct sockaddr_in sin;
+#ifdef INET6
+	struct sockaddr_in6 sin6;
+#endif
 	struct rtentry *rt;
 
 	mtx_assert(&hsh->mtx, MA_OWNED);
@@ -257,6 +519,7 @@
 	bcopy(r, &fle->f.r, sizeof(struct flow_rec));
 	fle->f.bytes = plen;
 	fle->f.packets = 1;
+	fle->f.proto = eproto;
 	fle->f.tcp_flags = tcp_flags;
 
 	fle->f.first = fle->f.last = time_uptime;
@@ -265,46 +528,94 @@
 	 * First we do route table lookup on destination address. So we can
 	 * fill in out_ifx, dst_mask, nexthop, and dst_as in future releases.
 	 */
-	bzero(&sin, sizeof(sin));
-	sin.sin_len = sizeof(struct sockaddr_in);
-	sin.sin_family = AF_INET;
-	sin.sin_addr = fle->f.r.r_dst;
-	/* XXX MRT 0 as a default.. need the m here to get fib */
-	rt = rtalloc1_fib((struct sockaddr *)&sin, 0, 0, 0);
-	if (rt != NULL) {
-		fle->f.fle_o_ifx = rt->rt_ifp->if_index;
-
-		if (rt->rt_flags & RTF_GATEWAY &&
-		    rt->rt_gateway->sa_family == AF_INET)
-			fle->f.next_hop =
-			    ((struct sockaddr_in *)(rt->rt_gateway))->sin_addr;
-
-		if (rt_mask(rt))
-			fle->f.dst_mask = bitcount32(((struct sockaddr_in *)
-			    rt_mask(rt))->sin_addr.s_addr);
-		else if (rt->rt_flags & RTF_HOST)
-			/* Give up. We can't determine mask :( */
-			fle->f.dst_mask = 32;
-
-		RTFREE_LOCKED(rt);
-	}
-
-	/* Do route lookup on source address, to fill in src_mask. */
-	bzero(&sin, sizeof(sin));
-	sin.sin_len = sizeof(struct sockaddr_in);
-	sin.sin_family = AF_INET;
-	sin.sin_addr = fle->f.r.r_src;
-	/* XXX MRT 0 as a default  revisit.  need the mbuf for fib*/
-	rt = rtalloc1_fib((struct sockaddr *)&sin, 0, 0, 0);
-	if (rt != NULL) {
-		if (rt_mask(rt))
-			fle->f.src_mask = bitcount32(((struct sockaddr_in *)
-			    rt_mask(rt))->sin_addr.s_addr);
-		else if (rt->rt_flags & RTF_HOST)
-			/* Give up. We can't determine mask :( */
-			fle->f.src_mask = 32;
+	if (eproto == ETHERTYPE_IP) {
+		bzero(&sin, sizeof(sin));
+		sin.sin_len = sizeof(struct sockaddr_in);
+		sin.sin_family = AF_INET;
+		sin.sin_addr = fle->f.r.dst.r_dst;
+		/* XXX MRT 0 as a default.. need the m here to get fib */
+		rt = rtalloc1_fib((struct sockaddr *)&sin, 0, 0, 0);
+		if (rt != NULL) {
+			fle->f.fle_o_ifx = rt->rt_ifp->if_index;
+	
+			if (rt->rt_flags & RTF_GATEWAY &&
+			    rt->rt_gateway->sa_family == AF_INET)
+				fle->f.n.next_hop =
+				    ((struct sockaddr_in *)(rt->rt_gateway))->sin_addr;
+	
+			if (rt_mask(rt))
+				fle->f.dst_mask = bitcount32(((struct sockaddr_in *)
+				    rt_mask(rt))->sin_addr.s_addr);
+			else if (rt->rt_flags & RTF_HOST)
+				/* Give up. We can't determine mask :( */
+				fle->f.dst_mask = 32;
+	
+			RTFREE_LOCKED(rt);
+		}
+	
+		/* Do route lookup on source address, to fill in src_mask. */
+		bzero(&sin, sizeof(sin));
+		sin.sin_len = sizeof(struct sockaddr_in);
+		sin.sin_family = AF_INET;
+		sin.sin_addr = fle->f.r.src.r_src;
+		/* XXX MRT 0 as a default  revisit.  need the mbuf for fib*/
+		rt = rtalloc1_fib((struct sockaddr *)&sin, 0, 0, 0);
+		if (rt != NULL) {
+			if (rt_mask(rt))
+				fle->f.src_mask = bitcount32(((struct sockaddr_in *)
+				    rt_mask(rt))->sin_addr.s_addr);
+			else if (rt->rt_flags & RTF_HOST)
+				/* Give up. We can't determine mask :( */
+				fle->f.src_mask = 32;
+	
+			RTFREE_LOCKED(rt);
+		}
+	} else {
+#ifdef INET6		
+		bzero(&sin6, sizeof(sin6));
+		sin6.sin6_len = sizeof(struct sockaddr_in6);
+		sin6.sin6_family = AF_INET6;
+		sin6.sin6_addr = fle->f.r.dst.r_dst6;
+		/* XXX fib works for AF_INET only */
+		rt = rtalloc1_fib((struct sockaddr *)&sin6, 0, 0, 0);
+		if (rt != NULL) {
+			fle->f.fle_o_ifx = rt->rt_ifp->if_index;
+	
+			if (rt->rt_flags & RTF_GATEWAY &&
+			    rt->rt_gateway->sa_family == AF_INET6)
+				fle->f.n.next_hop6 =
+				    ((struct sockaddr_in6 *)(rt->rt_gateway))->sin6_addr;
+			if (rt_mask(rt))
+/*	
+				fle->f.dst_mask = bitcount32(((struct sockaddr_in6 *)
+				    rt_mask(rt))->sin_addr.s_addr);
+			else if (rt->rt_flags & RTF_HOST) */
+				/* Give up. We can't determine mask :( */
+				fle->f.dst_mask = 128;
+	
+			RTFREE_LOCKED(rt);
+		}
+	
+		/* Do route lookup on source address, to fill in src_mask. */
+		bzero(&sin6, sizeof(sin6));
+		sin6.sin6_len = sizeof(struct sockaddr_in6);
+		sin6.sin6_family = AF_INET6;
+		sin6.sin6_addr = fle->f.r.src.r_src6;
+		/* XXX MRT 0 as a default  revisit.  need the mbuf for fib*/
+		rt = rtalloc1_fib((struct sockaddr *)&sin6, 0, 0, 0);
+		if (rt != NULL) {
+/*			
+			if (rt_mask(rt))
+				fle->f.src_mask = bitcount32(((struct sockaddr_in6 *)
+				    rt_mask(rt))->sin_addr.s_addr);
+			else if (rt->rt_flags & RTF_HOST) */
+				/* Give up. We can't determine mask :( */
+				fle->f.src_mask = 128;
+	
+			RTFREE_LOCKED(rt);
+		}
 
-		RTFREE_LOCKED(rt);
+#endif
 	}
 
 	/* Push new flow at the and of hash. */
@@ -347,6 +658,10 @@
 
 	mtx_init(&priv->export_mtx, "export dgram lock", NULL, MTX_DEF);
 
+	generate_v9_templates(priv);
+
+	ng_netflow_switch_version(priv, priv->version, 1);
+
 	return (0);
 }
 
@@ -359,6 +674,7 @@
 	item_p			item = NULL;
 	int i;
 
+
 	/*
 	 * We are going to free probably billable data.
 	 * Expire everything before freeing it.
@@ -371,7 +687,7 @@
 		}
 
 	if (item != NULL)
-		export_send(priv, item, NG_QUEUE);
+		(*record_send)(priv, item, NG_QUEUE);
 
 	uma_zdestroy(priv->zone);
 
@@ -383,46 +699,34 @@
 	if (priv->hash)
 		free(priv->hash, M_NETFLOW_HASH);
 
+	/* FreeFlow Tables */
+	for (i = 0; i < priv->flowsets_count; i++)
+		free(priv->v9_flowsets[i], M_NETFLOW_GENERAL);
+
 	mtx_destroy(&priv->export_mtx);
 }
 
-/* Insert packet from into flow cache. */
+/* 
+ * Insert packet from into flow cache. Assume size/version check passed
+ */
 int
-ng_netflow_flow_add(priv_p priv, struct ip *ip, unsigned int src_if_index)
+ng_netflow_flow_add(priv_p priv, caddr_t ip_ptr, caddr_t upper_ptr, uint8_t upper_proto, uint8_t is_frag, unsigned int src_if_index)
 {
 	register struct flow_entry	*fle, *fle1;
 	struct flow_hash_entry		*hsh;
 	struct flow_rec		r;
+	struct ip		*ip = NULL;
+#ifdef INET6
+	struct ip6_hdr		*ip6 = NULL;
+#endif
 	item_p			item = NULL;
-	int			hlen, plen;
+	int			plen;
 	int			error = 0;
 	uint8_t			tcp_flags = 0;
-
-	/* Try to fill flow_rec r */
-	bzero(&r, sizeof(r));
-	/* check version */
-	if (ip->ip_v != IPVERSION)
-		return (EINVAL);
-
-	/* verify min header length */
-	hlen = ip->ip_hl << 2;
-
-	if (hlen < sizeof(struct ip))
-		return (EINVAL);
-
-	r.r_src = ip->ip_src;
-	r.r_dst = ip->ip_dst;
-
-	/* save packet length */
-	plen = ntohs(ip->ip_len);
-
-	r.r_ip_p = ip->ip_p;
-	r.r_tos = ip->ip_tos;
-
-	r.r_i_ifx = src_if_index;
+	uint16_t		eproto;
 
 	/*
-	 * XXX NOTE: only first fragment of fragmented TCP, UDP and
+	 * XXX Fragmentation NOTE: only first fragment of fragmented TCP, UDP and
 	 * ICMP packet will be recorded with proper s_port and d_port.
 	 * Following fragments will be recorded simply as IP packet with
 	 * ip_proto = ip->ip_p and s_port, d_port set to zero.
@@ -430,22 +734,91 @@
 	 * ip packet assebmling here. Anyway, (in)famous trafd works this way -
 	 * and nobody complains yet :)
 	 */
-	if ((ip->ip_off & htons(IP_OFFMASK)) == 0)
-		switch(r.r_ip_p) {
-		case IPPROTO_TCP:
-		{
-			register struct tcphdr *tcp;
-
-			tcp = (struct tcphdr *)((caddr_t )ip + hlen);
-			r.r_sport = tcp->th_sport;
-			r.r_dport = tcp->th_dport;
-			tcp_flags = tcp->th_flags;
-			break;
+
+	/* Try to fill flow_rec r */
+	bzero(&r, sizeof(r));
+	ip = (struct ip *)ip_ptr;
+	if (ip->ip_v == IPVERSION) {
+		eproto = ETHERTYPE_IP;
+		r.src.r_src = ip->ip_src;
+		r.dst.r_dst = ip->ip_dst;
+
+		/* Assume L$ template by default */
+		r.flow_type = NETFLOW_V9_FLOW_V4_L4;
+
+		/* save packet length */
+		plen = ntohs(ip->ip_len);
+
+		r.r_tos = ip->ip_tos;
+
+		if (is_frag == 0) {
+			switch(upper_proto) {
+			case IPPROTO_TCP:
+			{
+				register struct tcphdr *tcp;
+
+				tcp = (struct tcphdr *)upper_ptr;
+				r.r_ports = *(uint32_t *)upper_ptr;
+				tcp_flags = tcp->th_flags;
+				/* r.flow_type = NETFLOW_V9_FLOW_V4_L4; */
+				break;
+			}
+			case IPPROTO_UDP:
+			case IPPROTO_SCTP:
+			{
+				r.r_ports = *(uint32_t *)upper_ptr;
+				/* r.flow_type = NETFLOW_V9_FLOW_V4_L4; */
+				break;
+			}
+
+			}
 		}
+
+	} else {
+#ifdef INET6		
+		/* IPv6 traffic */
+		ip = NULL;
+		ip6 = (struct ip6_hdr *)ip_ptr;
+		eproto = ETHERTYPE_IPV6;
+
+		r.src.r_src6 = ip6->ip6_src;
+		r.dst.r_dst6 = ip6->ip6_dst;
+
+		/* Assume L4 template by default */
+		r.flow_type = NETFLOW_V9_FLOW_V6_L4;
+
+		/* save packet length */
+		plen = ntohs(ip6->ip6_plen) + sizeof(struct ip6_hdr);
+
+		//r.r_tos = ip->ip_tos;
+
+		if (is_frag == 0) {
+			switch(upper_proto) {
+			case IPPROTO_TCP:
+			{
+				register struct tcphdr *tcp;
+
+				tcp = (struct tcphdr *)upper_ptr;
+				r.r_ports = *(uint32_t *)upper_ptr;
+				tcp_flags = tcp->th_flags;
+				/* r.flow_type = NETFLOW_V9_FLOW_V6_L4; */
+				break;
+			}
 			case IPPROTO_UDP:
-			r.r_ports = *(uint32_t *)((caddr_t )ip + hlen);
-			break;
+			case IPPROTO_SCTP:
+			{
+				r.r_ports = *(uint32_t *)upper_ptr;
+				/* r.flow_type = NETFLOW_V9_FLOW_V6_L4; */
+				break;
+			}
+
+			}
 		}
+#endif		
+	}
+
+	r.r_ip_p = upper_proto;
+	r.r_i_ifx = src_if_index;
 
 	/* Update node statistics. XXX: race... */
 	priv->info.nfinfo_packets ++;
@@ -486,7 +859,7 @@
 		 * - it is going to overflow counter
 		 */
 		if (tcp_flags & TH_FIN || tcp_flags & TH_RST || AGED(fle) ||
-		    (fle->f.bytes >= (UINT_MAX - IF_MAXMTU)) ) {
+		    (fle->f.bytes >= (cntr_max - IF_MAXMTU)) ) {
 			TAILQ_REMOVE(&hsh->head, fle, fle_hash);
 			expire_flow(priv, &item, fle, NG_QUEUE);
 			atomic_add_32(&priv->info.nfinfo_act_exp, 1);
@@ -502,7 +875,7 @@
 			}
 		}
 	} else				/* A new flow entry. */
-		error = hash_insert(priv, hsh, &r, plen, tcp_flags);
+		error = hash_insert(priv, hsh, &r, eproto, plen, tcp_flags);
 
 	mtx_unlock(&hsh->mtx);
 
@@ -611,6 +984,65 @@
 	return (error);
 }
 
+/* We have full datagram in privdata. Send it to export hook. */
+static int
+export_send_v9(priv_p priv, item_p item, int flags)
+{
+	struct mbuf *m = NGI_M(item);
+	struct netflow_v9_export_dgram *dgram = mtod(m,
+					struct netflow_v9_export_dgram *);
+	struct netflow_v9_header *header = &dgram->header;
+	struct timespec ts;
+	int error = 0;
+	uint16_t len = 0;
+
+	struct netflow_v9_mbuf_tag *t;
+	struct netflow_v9_flowset_header *fs = NULL;
+	struct m_tag *mt = m_tag_locate(m, MTAG_NETFLOW, MTAG_NETFLOW_V9, NULL);
+
+	if (mt == NULL) {
+		/* */
+		log(LOG_DEBUG, "ng_netflow: V9 export packet without tag!\n");
+		return (0);
+	}
+
+	t = (struct netflow_v9_mbuf_tag *)(mt + 1);
+
+	/* Close FlowSet if not closed */
+	if (t->length != t->flow_header) {
+		fs = (struct netflow_v9_flowset_header *)(mtod(m, char *) + t->flow_header);
+		len = (uint16_t)(t->length - t->flow_header);
+		if (len % 4) {
+			t->length += 4 - (len % 4);
+			len += 4 - (len % 4);
+		}
+		fs->length = htons(len);
+	}
+
+
+
+	/* Fill mbuf header. */
+	m->m_len = m->m_pkthdr.len = t->length;
+
+	/* Fill export header. */
+	header->count = t->count;
+	header->sys_uptime = htonl(MILLIUPTIME(time_uptime));
+	getnanotime(&ts);
+	header->unix_secs  = htonl(ts.tv_sec);
+	header->seq_num = htonl(atomic_fetchadd_32(&priv->flow_seq, 1));
+	header->count = htons(t->count);
+	header->source_id = htonl(NG_NODE_ID(priv->node));
+
+	/* remove tag */
+	m_tag_delete_chain(m, NULL);
+
+	if (priv->export != NULL)
+		NG_FWD_ITEM_HOOK_FLAGS(error, item, priv->export, flags);
+	else
+		NG_FREE_ITEM(item);
+
+	return (error);
+}
 
 /* Add export record to dgram. */
 static int
@@ -628,9 +1060,9 @@
 	    ("ng_netflow: export too big"));
 
 	/* Fill in export record. */
-	rec->src_addr = fle->f.r.r_src.s_addr;
-	rec->dst_addr = fle->f.r.r_dst.s_addr;
-	rec->next_hop = fle->f.next_hop.s_addr;
+	rec->src_addr = fle->f.r.src.r_src.s_addr;
+	rec->dst_addr = fle->f.r.dst.r_dst.s_addr;
+	rec->next_hop = fle->f.n.next_hop.s_addr;
 	rec->i_ifx    = htons(fle->f.fle_i_ifx);
 	rec->o_ifx    = htons(fle->f.fle_o_ifx);
 	rec->packets  = htonl(fle->f.packets);
@@ -654,6 +1086,135 @@
 		return (0);	
 }
 
+/* Add V9 record to dgram. */
+static int
+export_add_v9(item_p item, struct flow_entry *fle)
+{
+	size_t len = 0;
+	uint16_t new_flow = 0;
+	void *offset_ptr;
+	struct netflow_v9_mbuf_tag *t;
+	struct netflow_v9_flowset_header *fs = NULL;
+	struct mbuf *m =  NGI_M(item);
+	struct m_tag *mt = m_tag_locate(m, MTAG_NETFLOW, MTAG_NETFLOW_V9, NULL);
+
+	if (mt == NULL) {
+		/* */
+		log(LOG_DEBUG, "ng_netflow: V9 export packet without tag!\n");
+		return (0);
+	}
+
+	t = (struct netflow_v9_mbuf_tag *)(mt + 1);
+	offset_ptr = (mtod(m, char *) + t->length);
+
+	/* Check if new records has the same template */
+	if (fle->f.r.flow_type != t->flow_type) {
+		new_flow = 1;
+		
+		/* 'Close' old FlowSet */
+		fs = (struct netflow_v9_flowset_header *)(mtod(m, char *) + t->flow_header);
+		len = (uint16_t)(t->length - t->flow_header);
+		if (len % 4) {
+			t->length += 4 - (len % 4);
+			len += 4 - (len % 4);
+		}
+		fs->length = htons(len);
+
+		/* Prepare 'new', but do not modify any counters here because switch can fail */
+		t->flow_type = NETFLOW_V9_FLOW_FAKE;
+		t->flow_header = t->length;
+		fs = (struct netflow_v9_flowset_header *)(mtod(m, char *) + t->length);
+		offset_ptr = (fs + 1);
+	}
+
+
+	switch (fle->f.r.flow_type) {
+	case NETFLOW_V9_FLOW_V4_L4:
+	{
+		/* IPv4 TCP/UDP/[SCTP] */
+		struct netflow_v9_record_ipv4_tcp *rec = (struct netflow_v9_record_ipv4_tcp *)offset_ptr;
+
+		
+		rec->src_addr = fle->f.r.src.r_src.s_addr;
+		rec->dst_addr = fle->f.r.dst.r_dst.s_addr;
+		rec->next_hop = fle->f.n.next_hop.s_addr;
+		rec->i_ifx    = htons(fle->f.fle_i_ifx);
+		rec->o_ifx    = htons(fle->f.fle_o_ifx);
+		rec->i_packets  = htonl(fle->f.packets);
+		rec->i_octets   = htonl(fle->f.bytes);
+		rec->o_packets  = htonl(0);
+		rec->o_octets   = htonl(0);
+		rec->first    = htonl(MILLIUPTIME(fle->f.first));
+		rec->last     = htonl(MILLIUPTIME(fle->f.last));
+		rec->s_port   = fle->f.r.r_sport;
+		rec->d_port   = fle->f.r.r_dport;
+		rec->flags    = fle->f.tcp_flags;
+		rec->prot     = fle->f.r.r_ip_p;
+		rec->tos      = fle->f.r.r_tos;
+		rec->dst_mask = fle->f.dst_mask;
+		rec->src_mask = fle->f.src_mask;
+
+		/* Not supported fields. */
+		rec->src_as = rec->dst_as = 0;
+
+		len = sizeof(struct netflow_v9_record_ipv4_tcp);
+		break;
+	}
+#ifdef INET6	
+	case NETFLOW_V9_FLOW_V6_L4:
+	{
+		/* IPv6 TCP/UDP/[SCTP] */
+		struct netflow_v9_record_ipv6_tcp *rec = (struct netflow_v9_record_ipv6_tcp *)offset_ptr;
+
+		/* ACHTUNG! unchecked code! */
+		rec->src_addr = fle->f.r.src.r_src6;
+		rec->dst_addr = fle->f.r.dst.r_dst6;
+		rec->next_hop = fle->f.n.next_hop6;
+		rec->i_ifx    = htons(fle->f.fle_i_ifx);
+		rec->o_ifx    = htons(fle->f.fle_o_ifx);
+		rec->i_packets  = htonl(fle->f.packets);
+		rec->i_octets   = htonl(fle->f.bytes);
+		rec->first    = htonl(MILLIUPTIME(fle->f.first));
+		rec->last     = htonl(MILLIUPTIME(fle->f.last));
+		rec->s_port   = fle->f.r.r_sport;
+		rec->d_port   = fle->f.r.r_dport;
+		rec->flags    = fle->f.tcp_flags;
+		rec->prot     = fle->f.r.r_ip_p;
+		rec->tos      = fle->f.r.r_tos;
+		rec->dst_mask = fle->f.dst_mask;
+		rec->src_mask = fle->f.src_mask;
+
+		/* Not supported fields. */
+		rec->src_as = rec->dst_as = 0;
+
+		len = sizeof(struct netflow_v9_record_ipv6_tcp);
+		break;
+	}
+#endif	
+	default:
+	{
+		log(LOG_DEBUG, "ng_netflow: Don't know what to do with %d flow type!\n", fle->f.r.flow_type);
+		return (0);
+	}
+	}
+
+	if (new_flow) {
+		/* Generate data segment ID */
+		fs->id = htons(0x100 + fle->f.r.flow_type);
+		t->flow_type = fle->f.r.flow_type;
+		t->length += sizeof(struct netflow_v9_flowset_header);
+	}
+
+	t->length += len;
+	t->count++;
+
+	if (t->length + NETFLOW_V9_MAX_RECORD_SIZE + 4 >= _NETFLOW_V9_MAX_SIZE(t->mtu))
+		return (1); /* end of datagram */
+	else
+		return (0);
+}
+
+
 /* Periodic flow expiry run. */
 void
 ng_netflow_expire(void *arg)
diff -urN sys/netgraph/_netflow.orig/netflow.h sys/netgraph/netflow/netflow.h
--- sys/netgraph/_netflow.orig/netflow.h	2009-09-01 02:37:44.000000000 +0400
+++ sys/netgraph/netflow/netflow.h	2009-09-06 12:41:29.000000000 +0400
@@ -46,6 +46,7 @@
 
 #define NETFLOW_V1 1
 #define NETFLOW_V5 5
+#define NETFLOW_V9 9
 
 struct netflow_v1_header
 {
@@ -69,6 +70,16 @@
   uint16_t pad;		/* Pad to word boundary */
 } __attribute__((__packed__));
 
+struct netflow_v9_header
+{
+  uint16_t version;	/* NetFlow version */
+  uint16_t count;	/* Total number of records in packet */
+  uint32_t sys_uptime;	/* System uptime */
+  uint32_t unix_secs;	/* Current seconds since 0000 UTC 1970 */
+  uint32_t seq_num;	/* Sequence number */
+  uint32_t source_id;	/* Observation Domain id */
+} __attribute__((__packed__));
+
 struct netflow_v1_record
 {
   uint32_t src_addr;	/* Source IP address */
@@ -115,6 +126,142 @@
   uint16_t pad2;	/* Pad to word boundary */
 } __attribute__((__packed__));
 
+
+
+/* RFC3954 field definitions */
+#define NETFLOW_V9_FIELD_IN_BYTES	1	/* Input bytes count for a flow. Default 4, can be 8 */
+#define NETFLOW_V9_FIELD_IN_PKTS	2	/* Incoming counter with number of packets associated with an IP Flow. Default 4 */
+#define NETFLOW_V9_FIELD_FLOWS		3	/* Number of Flows that were aggregated. Default 4 */
+#define NETFLOW_V9_FIELD_PROTOCOL	4	/* IP protocol byte. 1 */
+#define NETFLOW_V9_FIELD_TOS		5	/* Type of service byte setting when entering the incoming interface. 1 */
+#define NETFLOW_V9_FIELD_TCP_FLAGS	6	/* TCP flags; cumulative of all the TCP flags seen in this Flow. 1 */
+#define NETFLOW_V9_FIELD_L4_SRC_PORT	7	/* TCP/UDP source port number. 2 */
+#define NETFLOW_V9_FIELD_IPV4_SRC_ADDR	8	/* IPv4 source address. 4 */
+#define NETFLOW_V9_FIELD_SRC_MASK	9	/* The number of contiguous bits in the source subnet mask (i.e., the mask in slash notation). 1 */
+#define NETFLOW_V9_FIELD_INPUT_SNMP	10	/* Input interface index. Default 2 */
+#define NETFLOW_V9_FIELD_L4_DST_PORT	11	/* TCP/UDP destination port number. 2 */
+#define NETFLOW_V9_FIELD_IPV4_DST_ADDR	12	/* IPv4 destination address. 4 */
+#define NETFLOW_V9_FIELD_DST_MASK	13	/* The number of contiguous bits in the destination subnet mask (i.e., the mask in slash notation). 1 */
+#define NETFLOW_V9_FIELD_OUTPUT_SNMP	14	/* Output interface index. Default 2 */
+#define NETFLOW_V9_FIELD_IPV4_NEXT_HOP	15	/* IPv4 address of the next-hop router. 4 */
+#define NETFLOW_V9_FIELD_SRC_AS		16	/* Source BGP autonomous system number. Default 2, can be 4 */
+#define NETFLOW_V9_FIELD_DST_AS		17	/* Destination BGP autonomous system number. Default 2, can be 4 */
+#define NETFLOW_V9_FIELD_BGP_IPV4_NEXT_HOP	18	/* Next-hop router's IP address in the BGP domain. 4 */
+#define NETFLOW_V9_FIELD_MUL_DST_PKTS	19	/* IP multicast outgoing packet counter for packets associated with IP flow. Default 4 */
+#define NETFLOW_V9_FIELD_MUL_DST_BYTES	20	/* IP multicast outgoing Octet (byte) counter for the number of bytes associated with IP flow. Default 4 */
+#define NETFLOW_V9_FIELD_LAST_SWITCHED	21	/* sysUptime in msec at which the last packet of this Flow was switched. 4 */
+#define NETFLOW_V9_FIELD_FIRST_SWITCHED	22	/* sysUptime in msec at which the first packet of this Flow was switched. 4 */
+#define NETFLOW_V9_FIELD_OUT_BYTES	23	/* Outgoing counter for the number of bytes associated with an IP Flow. Default 4 */
+#define NETFLOW_V9_FIELD_OUT_PKTS	24	/* Outgoing counter for the number of packets associated with an IP Flow. Default 4 */
+#define NETFLOW_V9_FIELD_IPV6_SRC_ADDR	27	/* IPv6 source address. 16 */
+#define NETFLOW_V9_FIELD_IPV6_DST_ADDR	28	/* IPv6 destination address. 16 */
+#define NETFLOW_V9_FIELD_IPV6_SRC_MASK	29	/* Length of the IPv6 source mask in contiguous bits. 1 */
+#define NETFLOW_V9_FIELD_IPV6_DST_MASK	30	/* Length of the IPv6 destination mask in contiguous bits. 1 */
+#define NETFLOW_V9_FIELD_IPV6_FLOW_LABEL	31	/* IPv6 flow label as per RFC 2460 definition. 3 */
+#define NETFLOW_V9_FIELD_ICMP_TYPE	32	/* Internet Control Message Protocol (ICMP) packet type; reported as ICMP Type * 256 + ICMP code. 2 */
+#define NETFLOW_V9_FIELD_MUL_IGMP_TYPE	33	/* Internet Group Management Protocol (IGMP) packet type. 1 */
+#define NETFLOW_V9_FIELD_SAMPLING_INTERVAL	34	/* When using sampled NetFlow, the rate at which packets are sampled; for example, a value of 100 indicates that one of every hundred packets is sampled. 4 */
+#define NETFLOW_V9_FIELD_SAMPLING_ALGORITHM	35	/* For sampled NetFlow platform-wide: 0x01 deterministic sampling 0x02 random sampling. 1 */
+#define NETFLOW_V9_FIELD_FLOW_ACTIVE_TIMEOUT	36	/* Timeout value (in seconds) for active flow entries in the NetFlow cache. 2 */
+#define NETFLOW_V9_FIELD_FLOW_INACTIVE_TIMEOUT	37	/* Timeout value (in seconds) for inactive Flow entries in the NetFlow cache. 2 */
+#define NETFLOW_V9_FIELD_ENGINE_TYPE	38	/* Type of Flow switching engine (route processor, linecard, etc...). 1 */
+#define NETFLOW_V9_FIELD_ENGINE_ID	39	/* ID number of the Flow switching engine. 1 */
+#define NETFLOW_V9_FIELD_TOTAL_BYTES_EXP	40	/* Counter with for the number of bytes exported by the Observation Domain. Default 4 */
+#define NETFLOW_V9_FIELD_TOTAL_PKTS_EXP	41	/* Counter with for the number of packets exported by the Observation Domain. Default 4 */
+#define NETFLOW_V9_FIELD_TOTAL_FLOWS_EXP	42	/* Counter with for the number of flows exported by the Observation Domain. Default 4 */
+#define NETFLOW_V9_FIELD_MPLS_TOP_LABEL_TYPE	46	/* MPLS Top Label Type. 1 */
+#define NETFLOW_V9_FIELD_MPLS_TOP_LABEL_IP_ADDR	47	/* Forwarding Equivalent Class corresponding to the MPLS Top Label. 4 */
+#define NETFLOW_V9_FIELD_FLOW_SAMPLER_ID	48	/* Identifier shown in "show flow-sampler". 1 */
+#define NETFLOW_V9_FIELD_FLOW_SAMPLER_MODE	49	/* The type of algorithm used for sampling data. 2 */
+#define NETFLOW_V9_FIELD_FLOW_SAMPLER_RANDOM_INTERVAL	50	/* Packet interval at which to sample. 4. */
+#define NETFLOW_V9_FIELD_DST_TOS	55	/* Type of Service byte setting when exiting outgoing interface. 1. */
+#define NETFLOW_V9_FIELD_SRC_MAC	56	/* Source MAC Address. 6 */
+#define NETFLOW_V9_FIELD_DST_MAC	57	/* Destination MAC Address. 6 */
+#define NETFLOW_V9_FIELD_SRC_VLAN	58	/* Virtual LAN identifier associated with ingress interface. 2 */
+#define NETFLOW_V9_FIELD_DST_VLAN	59	/* irtual LAN identifier associated with egress interface. 2 */
+#define NETFLOW_V9_FIELD_IP_PROTOCOL_VERSION	60	/* Internet Protocol Version. Set to 4 for IPv4, set to 6 for IPv6. If not present in the template, then version 4 is assumed. 1. */
+#define NETFLOW_V9_FIELD_DIRECTION	61	/* Flow direction: 0 - ingress flow 1 - egress flow. 1 */
+#define NETFLOW_V9_FIELD_IPV6_NEXT_HOP	62	/* IPv6 address of the next-hop router. 16 */
+#define NETFLOW_V9_FIELD_BGP_IPV6_NEXT_HOP	63	/* Next-hop router in the BGP domain. 16 */
+#define NETFLOW_V9_FIELD_IPV6_OPTION_HEADERS	64	/* Bit-encoded field identifying IPv6 option headers found in the flow */
+#define NETFLOW_V9_FIELD_MPLS_LABEL_1	70	/* MPLS label at position 1 in the stack. 3 */
+#define NETFLOW_V9_FIELD_MPLS_LABEL_2	71	/* MPLS label at position 2 in the stack. 3 */
+#define NETFLOW_V9_FIELD_MPLS_LABEL_3	72	/* MPLS label at position 3 in the stack. 3 */
+#define NETFLOW_V9_FIELD_MPLS_LABEL_4	73	/* MPLS label at position 4 in the stack. 3 */
+#define NETFLOW_V9_FIELD_MPLS_LABEL_5	74	/* MPLS label at position 5 in the stack. 3 */
+#define NETFLOW_V9_FIELD_MPLS_LABEL_6	75	/* MPLS label at position 6 in the stack. 3 */
+#define NETFLOW_V9_FIELD_MPLS_LABEL_7	76	/* MPLS label at position 7 in the stack. 3 */
+#define NETFLOW_V9_FIELD_MPLS_LABEL_8	77	/* MPLS label at position 8 in the stack. 3 */
+#define NETFLOW_V9_FIELD_MPLS_LABEL_9	78	/* MPLS label at position 9 in the stack. 3 */
+#define NETFLOW_V9_FIELD_MPLS_LABEL_10	79	/* MPLS label at position 10 in the stack. 3 */
+
+#ifdef COUNTERS_64
+#define cntr		uint64_t
+#define cntr_max	UINT64_MAX
+#else
+#define cntr		uint32_t
+#define cntr_max	UINT_MAX
+#endif
+
+struct netflow_v9_template
+{
+	int	field_id;
+	int	field_length;
+};
+
+/* Template ID for tcp/udp v4 streams ID:257 (0x100 + NETFLOW_V9_FLOW_V4_L4) */
+struct netflow_v9_record_ipv4_tcp
+{
+  uint32_t src_addr;	/* Source IPv4 address (IPV4_SRC_ADDR) */
+  uint32_t dst_addr;	/* Destination IPv4 address (IPV4_DST_ADDR) */
+  uint32_t next_hop;	/* Next hop IPv4 address (IPV4_NEXT_HOP) */  
+  uint16_t i_ifx;	/* Source interface index (INPUT_SNMP) */
+  uint16_t o_ifx;	/* Destination interface index (OUTPUT_SNMP) */
+  cntr i_packets;	/* Number of incoming packets in a flow (IN_PKTS) */
+  cntr i_octets;	/* Number of incoming octets in a flow (IN_BYTES) */
+  cntr o_packets;	/* Number of outgoing packets in a flow (OUT_PKTS) */
+  cntr o_octets;	/* Number of outgoing octets in a flow (OUT_BYTES) */
+  uint32_t first;	/* System uptime at start of a flow (FIRST_SWITCHED) */
+  uint32_t last;	/* System uptime at end of a flow (LAST_SWITCHED) */
+  uint16_t s_port;	/* Source port (L4_SRC_PORT) */
+  uint16_t d_port;	/* Destination port (L4_DST_PORT) */
+  uint8_t flags;	/* Cumulative OR of tcp flags (TCP_FLAGS) */
+  uint8_t prot;		/* IP protocol */
+  uint8_t tos;		/* IP type of service IN (or OUT) (TOS) */
+  uint32_t src_as;	/* Src peer/origin Autonomous System (SRC_AS) */  
+  uint32_t dst_as;	/* Dst peer/origin Autonomous System (DST_AS) */
+  uint8_t src_mask;	/* Source route's mask bits (SRC_MASK) */
+  uint8_t dst_mask; 	/* Destination route's mask bits (DST_MASK) */
+} __attribute__((__packed__));
+
+
+/* Template ID for tcp/udp v6 streams ID: 260 (0x100 + NETFLOW_V9_FLOW_V6_L4) */
+struct netflow_v9_record_ipv6_tcp
+{
+  struct in6_addr src_addr;	/* Source IPv6 address (IPV6_SRC_ADDR) */
+  struct in6_addr dst_addr;	/* Destination IPv6 address (IPV6_DST_ADDR) */
+  struct in6_addr next_hop;	/* Next hop IPv6 address (IPV6_NEXT_HOP) */  
+  uint16_t i_ifx;	/* Source interface index (INPUT_SNMP) */
+  uint16_t o_ifx;	/* Destination interface index (OUTPUT_SNMP) */
+  cntr i_packets;	/* Number of incoming packets in a flow (IN_PKTS) */
+  cntr i_octets;	/* Number of incoming octets in a flow (IN_BYTES) */
+  cntr o_packets;	/* Number of outgoing packets in a flow (OUT_PKTS) */
+  cntr o_octets;	/* Number of outgoing octets in a flow (OUT_BYTES) */
+  uint32_t first;	/* System uptime at start of a flow (FIRST_SWITCHED) */
+  uint32_t last;	/* System uptime at end of a flow (LAST_SWITCHED) */
+  uint16_t s_port;	/* Source port (L4_SRC_PORT) */
+  uint16_t d_port;	/* Destination port (L4_DST_PORT) */
+  uint8_t flags;	/* Cumulative OR of tcp flags (TCP_FLAGS) */
+  uint8_t prot;		/* IP protocol */
+  uint8_t tos;		/* IP type of service IN (or OUT) (TOS) */
+  uint32_t src_as;	/* Src peer/origin Autonomous System (SRC_AS) */  
+  uint32_t dst_as;	/* Dst peer/origin Autonomous System (DST_AS) */
+  uint8_t src_mask;	/* Source route's mask bits (SRC_MASK) */
+  uint8_t dst_mask; 	/* Destination route's mask bits (DST_MASK) */
+} __attribute__((__packed__));
+
+#define vlog(x, ...)	log(LOG_DEBUG, "%s:%d " x "\n", __FUNCTION__, __LINE__, ##__VA_ARGS__)
+
 #define NETFLOW_V1_MAX_RECORDS 24
 #define NETFLOW_V5_MAX_RECORDS 30
 
@@ -123,7 +270,50 @@
 #define NETFLOW_V5_MAX_SIZE (sizeof(netflow_v5_header)+ \
 			     sizeof(netflow_v5_record)*NETFLOW_V5_MAX_RECORDS)
 
+#define BASE_MTU	1500
+#define MIN_MTU		sizeof(struct netflow_v5_header)
+#define MAX_MTU		16384
+#define NETFLOW_V9_MAX_SIZE	_NETFLOW_V9_MAX_SIZE(BASE_MTU)
+#define _NETFLOW_V9_MAX_SIZE(x)	(x) - sizeof(struct ip6_hdr) - sizeof(struct udphdr)
+#define NETFLOW_V9_MAX_FLOWSETS	2
+
+#define NETFLOW_V9_MAX_RECORD_SIZE	sizeof(struct netflow_v9_record_ipv6_tcp)
+#define NETFLOW_V9_MAX_PACKETS_TEMPL	500	/* Send data templates every ... packets */
+#define NETFLOW_V9_MAX_TIME_TEMPL	600	/* Send data templates every ... seconds */
+#define NETFLOW_V9_MAX_TEMPLATES	16	/* Not a real value */
+#define _NETFLOW_V9_TEMPLATE_SIZE(x)	(sizeof(x) / sizeof(struct netflow_v9_template)) * 4
+//#define _NETFLOW_V9_TEMPLATE_SIZE(x)	((x) + 1) * 4
+
+/* Flow Templates */
+#define NETFLOW_V9_FLOW_V4_L4	1 /* IPv4 TCP/UDP packet */
+#define NETFLOW_V9_FLOW_V4_ICMP	2 /* IPv4 ICMP packet, currently unused */
+#define NETFLOW_V9_FLOW_V4_L3	3 /* IPv4 IP packet */
+#define NETFLOW_V9_FLOW_V6_L4	4 /* IPv6 TCP/UDP packet */
+#define NETFLOW_V9_FLOW_V6_ICMP	5 /* IPv6 ICMP packet, currently unused */
+#define NETFLOW_V9_FLOW_V6_L3	6 /* IPv6 IP packet */
+
+#define NETFLOW_V9_FLOW_FAKE	65535 /* Not uset used in real flowsets! */
+
 struct netflow_v5_export_dgram {
 	struct netflow_v5_header	header;
 	struct netflow_v5_record	r[NETFLOW_V5_MAX_RECORDS];
 } __attribute__((__packed__));
+
+struct netflow_v9_export_dgram {
+	struct netflow_v9_header	header;
+	char				data; /* MTU can change, record length is dynamic */
+};
+
+struct netflow_v9_flowset_header {
+	uint16_t	id; /* FlowSet id */
+	uint16_t	length; /* FlowSet length */
+} __attribute__((__packed__));
+
+struct netflow_v9_mbuf_tag {
+	struct m_tag	tag; /* pointer to mbuf tag */
+	uint16_t	length; /* Current packet length */
+	uint16_t	count; /* current records count */
+	uint16_t	mtu; /* max MTU shapshot */
+	uint16_t	flow_type; /* current flowset */
+	uint16_t	flow_header; /* offset pointing to current flow header */
+};
diff -urN sys/netgraph/_netflow.orig/ng_netflow.c sys/netgraph/netflow/ng_netflow.c
--- sys/netgraph/_netflow.orig/ng_netflow.c	2009-09-01 02:37:44.000000000 +0400
+++ sys/netgraph/netflow/ng_netflow.c	2009-09-06 16:36:08.000000000 +0400
@@ -30,6 +30,8 @@
 static const char rcs_id[] =
     "@(#) $FreeBSD: src/sys/netgraph/netflow/ng_netflow.c,v 1.20 2009/05/13 02:26:34 mav Exp $";
 
+#include "opt_inet6.h"
+
 #include <sys/param.h>
 #include <sys/systm.h>
 #include <sys/kernel.h>
@@ -48,8 +50,11 @@
 #include <netinet/in.h>
 #include <netinet/in_systm.h>
 #include <netinet/ip.h>
+#include <netinet/ip6.h>
+#include <netinet/ip6.h>
 #include <netinet/tcp.h>
 #include <netinet/udp.h>
+#include <netinet/sctp.h>
 
 #include <netgraph/ng_message.h>
 #include <netgraph/ng_parse.h>
@@ -114,6 +119,30 @@
 	&ng_netflow_setconfig_type_fields
 };
 
+/* Parse type for ng_netflow_setversion */
+static const struct ng_parse_struct_field ng_netflow_setversion_type_fields[]
+	= NG_NETFLOW_SETVERSION_TYPE;
+static const struct ng_parse_type ng_netflow_setversion_type = {
+	&ng_parse_struct_type,
+	&ng_netflow_setversion_type_fields
+};
+
+/* Parse type for ng_netflow_settemplateperiodic */
+static const struct ng_parse_struct_field ng_netflow_settemplateperiodic_type_fields[]
+	= NG_NETFLOW_SETTEMPLATEPERIODIC_TYPE;
+static const struct ng_parse_type ng_netflow_settemplateperiodic_type = {
+	&ng_parse_struct_type,
+	&ng_netflow_settemplateperiodic_type_fields
+};
+
+/* Parse type for ng_netflow_setmtu */
+static const struct ng_parse_struct_field ng_netflow_setmtu_type_fields[]
+	= NG_NETFLOW_SETMTU_TYPE;
+static const struct ng_parse_type ng_netflow_setmtu_type = {
+	&ng_parse_struct_type,
+	&ng_netflow_setmtu_type_fields
+};
+
 /* List of commands and how to convert arguments to/from ASCII */
 static const struct ng_cmdlist ng_netflow_cmds[] = {
        {
@@ -158,6 +187,27 @@
 	&ng_netflow_setconfig_type,
 	NULL
        },
+       {
+	NGM_NETFLOW_COOKIE,
+	NGM_NETFLOW_SETVERSION,
+	"setversion",
+	&ng_netflow_setversion_type,
+	NULL
+       },
+       {
+	NGM_NETFLOW_COOKIE,
+	NGM_NETFLOW_SETTEMPLATEPERIODIC,
+	"settemplateperiodic",
+	&ng_netflow_settemplateperiodic_type,
+	NULL
+       },
+       {
+	NGM_NETFLOW_COOKIE,
+	NGM_NETFLOW_SETMTU,
+	"setmtu",
+	&ng_netflow_setmtu_type,
+	NULL
+       },
        { 0 }
 };
 
@@ -202,6 +252,13 @@
 	for (i = 0; i < NG_NETFLOW_MAXIFACES; i++)
 		priv->ifaces[i].info.conf = NG_NETFLOW_CONF_INGRESS;
 
+	/* Set v9 defaults */
+	priv->version = NETFLOW_V9;
+	priv->templ_time = NETFLOW_V9_MAX_TIME_TEMPL;
+	priv->templ_packets = NETFLOW_V9_MAX_PACKETS_TEMPL;
+	priv->mtu = BASE_MTU;
+	priv->flow_seq = 0;
+
 	/* Initialize callout handle */
 	callout_init(&priv->exp_callout, CALLOUT_MPSAFE);
 
@@ -434,6 +491,52 @@
 	
 			break;
 		}
+		case NGM_NETFLOW_SETVERSION:
+		{
+			struct ng_netflow_setversion *set;
+
+			if (msg->header.arglen != sizeof(struct ng_netflow_setversion))
+				ERROUT(EINVAL);
+
+			set = (struct ng_netflow_setversion *)msg->data;
+			if ((set->version != NETFLOW_V9) && (set->version != NETFLOW_V5))
+				ERROUT(EINVAL);
+
+			ng_netflow_switch_version(priv, set->version, 0);
+
+			break;
+		}
+		case NGM_NETFLOW_SETTEMPLATEPERIODIC:
+		{
+			struct ng_netflow_settemplateperiodic *set;
+
+			if (msg->header.arglen != sizeof(struct ng_netflow_settemplateperiodic))
+				ERROUT(EINVAL);
+
+			set = (struct ng_netflow_settemplateperiodic *)msg->data;
+
+			/* XXX: Set atomic here */
+			priv->templ_packets = set->packets;
+			priv->templ_time = set->time;
+
+			break;
+		}
+		case NGM_NETFLOW_SETMTU:
+		{
+			struct ng_netflow_setmtu *set;
+
+			if (msg->header.arglen != sizeof(struct ng_netflow_setmtu))
+				ERROUT(EINVAL);
+
+			set = (struct ng_netflow_setmtu *)msg->data;
+			if ((set->mtu < MIN_MTU) || (set->mtu > MAX_MTU))
+				ERROUT(EINVAL);
+
+			/* XXX: Set atomic here */
+			priv->mtu = set->mtu;
+
+			break;
+		}
 		case NGM_NETFLOW_SHOW:
 		{
 			uint32_t *last;
@@ -481,12 +584,15 @@
 	const priv_p priv = NG_NODE_PRIVATE(node);
 	const iface_p iface = NG_HOOK_PRIVATE(hook);
 	hook_p out;
-	struct mbuf *m = NULL;
-	struct ip *ip;
+	struct mbuf *m, *m_old = NULL;
+	struct ip *ip = NULL;
+	struct ip6_hdr *ip6 = NULL;
 	struct m_tag *mtag;
-	int pullup_len = 0;
+	int pullup_len = 0, off;
+	uint8_t upper_proto = 0, is_frag = 0;
 	int error = 0, bypass = 0;
 	unsigned int src_if_index;
+	caddr_t upper_ptr = NULL;
 
 	if (hook == priv->export) {
 		/*
@@ -541,6 +647,7 @@
 	}
 
 	NGI_GET_M(item, m);
+	m_old = m;
 
 	/* Increase counters. */
 	iface->info.ifinfo_packets++;
@@ -569,6 +676,22 @@
 	}							\
 } while (0)
 
+/*	
+#define	M_HCHECK(length)	do {				\
+	pullup_len += length + sizeof(ip6_ext);			\
+	if ((m)->m_pkthdr.len < (pullup_len)) {			\
+		pullup_len -= length + sizeof(ip6_ext);
+		M_CHECK(length);
+		`
+	} 							\
+	if ((m)->m_len < (pullup_len) &&			\
+	   (((m) = m_pullup((m),(pullup_len))) == NULL)) {	\
+		error = ENOBUFS;				\
+		goto done;					\
+	}							\
+} while (0)
+*/
+
 	switch (iface->info.ifinfo_dlt) {
 	case DLT_EN10MB:	/* Ethernet */
 	    {
@@ -586,6 +709,33 @@
 			eh = mtod(m, struct ether_header *);
 			ip = (struct ip *)(eh + 1);
 			break;
+#ifdef INET6			
+		case ETHERTYPE_IPV6:
+			/* 
+			 * This is done not to pullup mbuf twice on every ext
+			 * header 
+			 */
+			pullup_len += sizeof(struct ip6_hdr) + sizeof(struct ip6_ext);
+			if (m->m_pkthdr.len >= pullup_len) {
+				if ((m = m_pullup(m, pullup_len)) == NULL) {
+					error = ENOBUFS;
+					goto done;
+				}
+				pullup_len -= sizeof(struct ip6_ext);
+			} else {
+				pullup_len -= sizeof(struct ip6_hdr) + sizeof(struct ip6_ext);
+				M_CHECK(sizeof(struct ip6_hdr));
+				eh = mtod(m, struct ether_header *);
+				ip6 = (struct ip6_hdr *)(eh + 1);
+				if (ip6->ip6_nxt != IPPROTO_NONE) {
+					error = EINVAL;
+					goto bypass;
+				}
+			}
+			eh = mtod(m, struct ether_header *);
+			ip6 = (struct ip6_hdr *)(eh + 1);
+			break;
+#endif			
 		case ETHERTYPE_VLAN:
 		    {
 			struct ether_vlan_header *evh;
@@ -597,6 +747,29 @@
 				M_CHECK(sizeof(struct ip));
 				ip = (struct ip *)(evh + 1);
 				break;
+			} else if (ntohs(evh->evl_proto) == ETHERTYPE_IPV6) {
+#ifdef INET6				
+				pullup_len += sizeof(struct ip6_hdr) + sizeof(struct ip6_ext);
+				if (m->m_pkthdr.len >= pullup_len) {
+					if ((m = m_pullup(m, pullup_len)) == NULL) {
+						error = ENOBUFS;
+						goto done;
+					}
+					pullup_len -= sizeof(struct ip6_ext);
+				} else {
+					pullup_len -= sizeof(struct ip6_hdr) + sizeof(struct ip6_ext);
+					M_CHECK(sizeof(struct ip6_hdr));
+					eh = mtod(m, struct ether_header *);
+					ip6 = (struct ip6_hdr *)(eh + 1);
+					if (ip6->ip6_nxt != IPPROTO_NONE) {
+						error = EINVAL;
+						goto bypass;
+					}
+				}
+				eh = mtod(m, struct ether_header *);
+				ip6 = (struct ip6_hdr *)(eh + 1);
+				break;
+#endif				
 			}
 		    }
 		default:
@@ -607,18 +780,32 @@
 	case DLT_RAW:		/* IP packets */
 		M_CHECK(sizeof(struct ip));
 		ip = mtod(m, struct ip *);
+#ifdef INET6		
+		if (ip->ip_v == 6) {
+			/* IPv6 packet */
+			ip = NULL;
+			M_CHECK(sizeof(struct ip6_hdr));
+			ip6 = mtod(m, struct ip6_hdr *);
+		}
+#endif		
 		break;
 	default:
 		goto bypass;
 		break;
 	}
 
-	if ((ip->ip_off & htons(IP_OFFMASK)) == 0) {
+	if ((ip != NULL) && ((ip->ip_off & htons(IP_OFFMASK)) == 0)) {
+		if ((ip->ip_v != IPVERSION) || 
+		    ((ip->ip_hl << 2) < sizeof(struct ip)))
+			goto bypass;
 		/*
-		 * In case of IP header with options, we haven't pulled
+		 * In case of IPv4 header with options, we haven't pulled
 		 * up enough, yet.
 		 */
 		pullup_len += (ip->ip_hl << 2) - sizeof(struct ip);
+		off = pullup_len;
+		//vlog("upper_offset = %d", off);
+		upper_proto = ip->ip_p;
 
 		switch (ip->ip_p) {
 		case IPPROTO_TCP:
@@ -627,38 +814,161 @@
 		case IPPROTO_UDP:
 			M_CHECK(sizeof(struct udphdr));
 			break;
+		case IPPROTO_SCTP:
+			M_CHECK(sizeof(struct sctphdr));
+			break;
 		}
-	}
+	} else if (ip != NULL) {
+		is_frag = 1;
+		upper_proto = ip->ip_p;
+		if ((ip->ip_v != IPVERSION) || 
+		    ((ip->ip_hl << 2) < sizeof(struct ip)))
+			goto bypass;
+	} else if (ip6 != NULL) {
+#ifdef INET6		
+		/* Check if we can export */
+		if (priv->version < NETFLOW_V9)
+			goto bypass;
+
+		/* Loop thru IPv6 extended headers to get upper layer header / frag */
+		int cur = ip6->ip6_nxt, hdr_off = 0;
+		struct ip6_ext *ip6e;
+		struct ip6_frag *ip6f;
+
+		off = pullup_len;
+		upper_proto = cur;
+
+		if ((ip6->ip6_vfc & IPV6_VERSION_MASK) != IPV6_VERSION)
+			goto bypass;
+
+		while (42) {
+
+			/*
+			 * At the moment we're 'standing' with pullup_len and
+			 * off 
+			 */
+			switch (cur) {
+
+			/* Ensure pointer is contiguous */
+			case IPPROTO_TCP:
+				M_CHECK(sizeof(struct tcphdr));
+				goto loopend;
+			case IPPROTO_UDP:
+				M_CHECK(sizeof(struct udphdr));
+				goto loopend;
+			case IPPROTO_SCTP:
+				M_CHECK(sizeof(struct sctphdr));
+				goto loopend;
+
+			/* Loop until 'real' upper layer headers */
+			case IPPROTO_HOPOPTS:
+			case IPPROTO_ROUTING:
+			case IPPROTO_DSTOPTS:
+				ip6e = (struct ip6_ext *)(mtod(m, caddr_t) + off);
+				upper_proto = ip6e->ip6e_nxt;
+				hdr_off = (ip6e->ip6e_len + 1) << 3;
+				break;
 
-	switch (iface->info.ifinfo_dlt) {
-	case DLT_EN10MB:
-	    {
-		struct ether_header *eh;
+			/* RFC4302, can be before DSTOPTS */
+			case IPPROTO_AH:
+				ip6e = (struct ip6_ext *)(mtod(m, caddr_t) + off);
+				upper_proto = ip6e->ip6e_nxt;
+				hdr_off = (ip6e->ip6e_len + 2) << 2;
+				break;
 
-		eh = mtod(m, struct ether_header *);
-		switch (ntohs(eh->ether_type)) {
-		case ETHERTYPE_IP:
-			ip = (struct ip *)(eh + 1);
-			break;
-		case ETHERTYPE_VLAN:
-		    {
-			struct ether_vlan_header *evh;
+			
+			case IPPROTO_FRAGMENT:
+				ip6f = (struct ip6_frag *)(mtod(m, caddr_t) + off);
+				upper_proto = ip6f->ip6f_nxt;
+				hdr_off = sizeof(struct ip6_frag);
+				off += hdr_off;
+				is_frag = 1;
+				goto loopend;
+
+/*				
+			case IPPROTO_NONE:
+				goto loopend;
+*/
+			default:
+				goto loopend;
+			}
 
-			evh = mtod(m, struct ether_vlan_header *);
-			ip = (struct ip *)(evh + 1);
+			off += hdr_off + sizeof(struct ip6_ext);
+			cur = upper_proto;
+			if (m->m_pkthdr.len >= off) {
+				if ((m = m_pullup(m, off)) == NULL) {
+					error = ENOBUFS;
+					goto done;
+				}
+				off -= sizeof(struct ip6_ext);
+				pullup_len += hdr_off;
+			} else {
+				/*
+				 * Packet ends somewhere here.
+				 * if its next header is not IPPROTO_NONE it is
+				 * possibly mailformed packet. Let's count as RAW IP
+				 */
+				upper_proto = IPPROTO_NONE;
+				off -= sizeof(struct ip6_ext);
+				goto loopend;
+			}
+
+		}
+#endif
+	}
+
+#ifdef INET6
+loopend:
+#endif
+	if (m != m_old) {
+		switch (iface->info.ifinfo_dlt) {
+		case DLT_EN10MB:	/* Ethernet */
+		    {
+			struct ether_header *eh;
+	
+			eh = mtod(m, struct ether_header *);
+			switch (ntohs(eh->ether_type)) {
+			case ETHERTYPE_IP:
+				ip = (struct ip *)(eh + 1);
+				break;
+			case ETHERTYPE_IPV6:
+				ip6 = (struct ip6_hdr *)(eh + 1);
+				break;
+			case ETHERTYPE_VLAN:
+			    {
+				struct ether_vlan_header *evh;
+	
+				evh = mtod(m, struct ether_vlan_header *);
+				if (ntohs(evh->evl_proto) == ETHERTYPE_IP) {
+					ip = (struct ip *)(evh + 1);
+					break;
+				} else if (ntohs(evh->evl_proto) == ETHERTYPE_IPV6) {
+#ifdef INET6					
+					ip6 = (struct ip6_hdr *)(evh + 1);
+					break;
+#endif					
+				}
+			    }
+			default:
+				panic("ng_netflow entered deadcode");
+			}
+			break;
+		    }
+		case DLT_RAW:		/* IP packets */
+			ip = mtod(m, struct ip *);
+#ifdef INET6			
+			if (ip->ip_v == 6) {
+				/* IPv6 packet */
+				ip = NULL;
+				ip6 = mtod(m, struct ip6_hdr *);
+			}
+#endif			
 			break;
-		     }
 		default:
 			panic("ng_netflow entered deadcode");
 		}
-		break;
-	     }
-	case DLT_RAW:
-		ip = mtod(m, struct ip *);
-		break;
-	default:
-		panic("ng_netflow entered deadcode");
 	}
+	upper_ptr = (caddr_t)(mtod(m, caddr_t) + off);
 
 #undef	M_CHECK
 
@@ -670,7 +980,7 @@
 	} else
 		src_if_index = iface->info.ifinfo_index;
 
-	error = ng_netflow_flow_add(priv, ip, src_if_index);
+	error = ng_netflow_flow_add(priv, (ip != NULL) ? (caddr_t)ip : (caddr_t)ip6, upper_ptr, upper_proto, is_frag, src_if_index);
 
 bypass:
 	if (out != NULL) {
diff -urN sys/netgraph/_netflow.orig/ng_netflow.h sys/netgraph/netflow/ng_netflow.h
--- sys/netgraph/_netflow.orig/ng_netflow.h	2009-09-01 02:37:44.000000000 +0400
+++ sys/netgraph/netflow/ng_netflow.h	2009-09-06 16:36:17.000000000 +0400
@@ -51,6 +51,9 @@
     NGM_NETFLOW_SETIFINDEX	= 5, 	/* set interface index */
     NGM_NETFLOW_SETTIMEOUTS	= 6, 	/* set active/inactive flow timeouts */
     NGM_NETFLOW_SETCONFIG	= 7, 	/* set flow generation options */
+    NGM_NETFLOW_SETVERSION	= 8, 	/* set flow export version  */
+    NGM_NETFLOW_SETTEMPLATEPERIODIC	= 9, 	/* set v9 flow template periodic */
+    NGM_NETFLOW_SETMTU		= 10, 	/* set outgoing interface MTU */
 };
 
 /* This structure is returned by the NGM_NETFLOW_INFO message */
@@ -105,10 +108,34 @@
 	u_int32_t conf;			/* new config */
 };
 
+/* This structure is passed to NGM_NETFLOW_SETVERSION */
+struct ng_netflow_setversion {
+	u_int8_t version;		/* netflow version */
+};
+
+/* This structure is passed to NGM_NETFLOW_SETTEMPLATEPERIODIC */
+struct ng_netflow_settemplateperiodic {
+	u_int16_t time;		/* max time between announce */
+	u_int16_t packets;	/* max packets between announce */
+};
+
+/* This structure is passed to NGM_NETFLOW_SETMTU */
+struct ng_netflow_setmtu {
+	u_int16_t mtu;		/* MTU for packet */
+};
+
 /* This is unique data, which identifies flow */
 struct flow_rec {
-	struct in_addr	r_src;
-	struct in_addr	r_dst;
+	uint16_t	flow_type; /* IPv4 L4/L3 Ipv6 L4/L3 flow, see NETFLOW_V9_FLOW* */
+	uint16_t	ether_proto; /* unused */
+	union {
+		struct in_addr	r_src;
+		struct in6_addr	r_src6;
+	} src;
+	union {
+		struct in_addr	r_dst;
+		struct in6_addr	r_dst6;
+	} dst;
 	union {
 		struct {
 			uint16_t	s_port;	/* source TCP/UDP port */
@@ -137,7 +164,10 @@
 /* A flow entry which accumulates statistics */
 struct flow_entry_data {
 	struct flow_rec		r;
-	struct in_addr		next_hop;
+	union {
+		struct in_addr		next_hop;
+		struct in6_addr		next_hop6;
+	} n;
 	uint16_t		fle_o_ifx;	/* output interface index */
 #define				fle_i_ifx	r.misc.i.i_ifx
 	uint8_t		dst_mask;	/* destination route mask bits */
@@ -146,6 +176,7 @@
 	u_long			bytes;
 	long			first;	/* uptime on first packet */
 	long			last;	/* uptime on last packet */
+	uint16_t		proto; /* IP/IPv6 */
 	u_char			tcp_flags;	/* cumulative OR */
 };
 
@@ -227,6 +258,26 @@
 	{ NULL }					\
 }
 
+/* Parse the setversion structure */
+#define	NG_NETFLOW_SETVERSION_TYPE {			\
+	{ "version",	&ng_parse_uint8_type },		\
+	{ NULL }					\
+}
+
+/* Parse the settemplateperiodic structure */
+#define	NG_NETFLOW_SETTEMPLATEPERIODIC_TYPE {			\
+	{ "time",	&ng_parse_uint16_type },		\
+	{ "packets",	&ng_parse_uint16_type },		\
+	{ NULL }					\
+}
+
+/* Parse the setmtu structure */
+#define	NG_NETFLOW_SETMTU_TYPE {			\
+	{ "mtu",	&ng_parse_uint16_type },		\
+	{ NULL }					\
+}
+
+
 /* Private hook data */
 struct ng_netflow_iface {
 	hook_p		hook;		/* NULL when disconnected */
@@ -270,6 +321,20 @@
 	item_p			export_item;
 	struct mtx		export_mtx;
 	uint32_t		flow_seq;	/* current flow sequence */
+	/* 
+	 * RFC 3954 clause 7.3
+	 * "Both options MUST be configurable by the user on the Exporter."
+	 */
+	uint16_t		templ_time;	/* time between sending templates */
+	uint16_t		templ_packets;	/* packets between sending templates */
+	uint32_t		templ_last_ts;	/* unixtime of last template announce */
+	uint32_t		templ_last_pkt;	/* packets count on last template announce */
+	uint32_t		sent_packets;	/* packets sent by exporeter; can be reached another way ? */
+	u_char			version;	/* Netflow export version */
+	u_char			flowsets_count; /* current flowsets used */
+	u_char			flowset_records[NETFLOW_V9_MAX_FLOWSETS - 1]; /* Count of records in each flowset */
+	uint16_t		mtu;		/* export interface MTU */
+	struct netflow_v9_flowset_header	*v9_flowsets[NETFLOW_V9_MAX_FLOWSETS - 1]; /* Pointers to pre-compiled flowsets */
 
 	struct ng_netflow_iface	ifaces[NG_NETFLOW_MAXIFACES];
 };
@@ -286,14 +351,15 @@
 
 #define MTAG_NETFLOW		1221656444
 #define MTAG_NETFLOW_CALLED	0
+#define MTAG_NETFLOW_V9		1
 
 /* Prototypes for netflow.c */
 int	ng_netflow_cache_init(priv_p);
 void	ng_netflow_cache_flush(priv_p);
 void	ng_netflow_copyinfo(priv_p, struct ng_netflow_info *);
 timeout_t ng_netflow_expire;
-int	ng_netflow_flow_add(priv_p, struct ip *, unsigned int src_if_index);
+int	ng_netflow_flow_add(priv_p, caddr_t ip_ptr, caddr_t upper_ptr, uint8_t upper_proto, uint8_t is_frag, unsigned int src_if_index);
 int	ng_netflow_flow_show(priv_p, uint32_t last, struct ng_mesg *);
-
+void	ng_netflow_switch_version(priv_p priv, int ver, int boot);
 #endif	/* _KERNEL */
 #endif	/* _NG_NETFLOW_H_ */
diff -urN sys/modules/netgraph/netflow/Makefile.orig sys/modules/netgraph/netflow/Makefile
--- sys/modules/netgraph/netflow/Makefile.orig	2009-09-06 16:31:18.000000000 +0400
+++ sys/modules/netgraph/netflow/Makefile	2009-09-06 16:33:32.000000000 +0400
@@ -3,9 +3,19 @@
 # Author: Gleb Smirnoff <[email protected]>
 #
 
+.include <bsd.own.mk>
+
 .PATH: ${.CURDIR}/../../../netgraph/netflow
 
 KMOD=   ng_netflow
-SRCS=   ng_netflow.c netflow.c
+SRCS=   ng_netflow.c netflow.c opt_inet6.h
+
+.if !defined(KERNBUILDDIR)
+
+.if ${MK_INET6_SUPPORT} != "no"
+opt_inet6.h:
+	echo "#define INET6 1" > ${.TARGET}
+.endif
+.endif
 
 .include <bsd.kmod.mk>
netflow_v9_20090906_72.diff (text/plain, 56.9 KB)
diff -urN sys/netgraph/netflow/netflow.c.orig sys/netgraph/netflow/netflow.c
--- sys/netgraph/netflow/netflow.c.orig	2009-01-09 23:55:26.000000000 +0300
+++ sys/netgraph/netflow/netflow.c	2009-09-06 18:51:53.000000000 +0400
@@ -30,6 +30,8 @@
 static const char rcs_id[] =
     "@(#) $FreeBSD: src/sys/netgraph/netflow/netflow.c,v 1.25.2.3 2009/01/09 20:55:26 mav Exp $";
 
+#include "opt_inet6.h"
+
 #include <sys/param.h>
 #include <sys/kernel.h>
 #include <sys/limits.h>
@@ -37,14 +39,18 @@
 #include <sys/syslog.h>
 #include <sys/systm.h>
 #include <sys/socket.h>
+#include <sys/endian.h>
 
 #include <machine/atomic.h>
+#include <machine/stdarg.h>
 
 #include <net/if.h>
 #include <net/route.h>
+#include <net/ethernet.h>
 #include <netinet/in.h>
 #include <netinet/in_systm.h>
 #include <netinet/ip.h>
+#include <netinet/ip6.h>
 #include <netinet/tcp.h>
 #include <netinet/udp.h>
 
@@ -94,12 +100,99 @@
 			 ((t) << 6) +	/* 64  */	\
 			 ((t) << 5) +	/* 32  */	\
 			 ((t) << 3))	/* 8   */
+/*
+ * Defines for different neflow version dispatchers
+ *
+ */
+static int export_add(item_p, struct flow_entry *);
+static int export_add_v9(item_p, struct flow_entry *);
+static int export_send(priv_p, item_p, int flags);
+static int export_send_v9(priv_p, item_p, int flags);
+
+typedef int (*record_add_ptr)(item_p, struct flow_entry *);
+typedef int (*record_send_ptr)(priv_p, item_p, int);
+struct _netflow_dispatchers {
+	record_add_ptr	record_add;
+	record_send_ptr	record_send;
+};
+
+static struct _netflow_dispatchers netflow_dispatcher[] =
+{
+	{ NULL, NULL },
+	{ NULL, NULL },
+	{ NULL, NULL },
+	{ NULL, NULL },
+	{ NULL, NULL },
+	{ export_add, export_send }, /* Version 5 */
+	{ NULL, NULL },
+	{ NULL, NULL },
+	{ NULL, NULL },
+	{ export_add_v9, export_send_v9 }, /* Version 9 */
+	{ NULL, NULL }
+};
+
+record_add_ptr record_add = NULL;
+record_send_ptr record_send = NULL;
 
 MALLOC_DECLARE(M_NETFLOW_HASH);
 MALLOC_DEFINE(M_NETFLOW_HASH, "netflow_hash", "NetFlow hash");
 
-static int export_add(item_p, struct flow_entry *);
-static int export_send(priv_p, item_p, int flags);
+
+static struct netflow_v9_template _netflow_v9_record_ipv4_tcp[] = 
+{
+	{ NETFLOW_V9_FIELD_IPV4_SRC_ADDR, 4},
+	{ NETFLOW_V9_FIELD_IPV4_DST_ADDR, 4},
+	{ NETFLOW_V9_FIELD_IPV4_NEXT_HOP, 4},
+	{ NETFLOW_V9_FIELD_INPUT_SNMP, 2},
+	{ NETFLOW_V9_FIELD_OUTPUT_SNMP, 2},
+	{ NETFLOW_V9_FIELD_IN_PKTS, sizeof(cntr)},
+	{ NETFLOW_V9_FIELD_IN_BYTES, sizeof(cntr)},
+	{ NETFLOW_V9_FIELD_OUT_PKTS, sizeof(cntr)},
+	{ NETFLOW_V9_FIELD_OUT_BYTES, sizeof(cntr)},
+	{ NETFLOW_V9_FIELD_FIRST_SWITCHED, 4},
+	{ NETFLOW_V9_FIELD_LAST_SWITCHED, 4},
+	{ NETFLOW_V9_FIELD_L4_SRC_PORT, 2},
+	{ NETFLOW_V9_FIELD_L4_DST_PORT, 2},
+	{ NETFLOW_V9_FIELD_TCP_FLAGS, 1},
+	{ NETFLOW_V9_FIELD_PROTOCOL, 1},
+	{ NETFLOW_V9_FIELD_TOS, 1},
+	{ NETFLOW_V9_FIELD_SRC_AS, 4},
+	{ NETFLOW_V9_FIELD_DST_AS, 4},
+	{ NETFLOW_V9_FIELD_SRC_MASK, 1},
+	{ NETFLOW_V9_FIELD_DST_MASK, 1},
+	{0, 0}
+};
+
+static struct netflow_v9_template _netflow_v9_record_ipv6_tcp[] = 
+{
+	{ NETFLOW_V9_FIELD_IPV6_SRC_ADDR, 16},
+	{ NETFLOW_V9_FIELD_IPV6_DST_ADDR, 16},
+	{ NETFLOW_V9_FIELD_IPV6_NEXT_HOP, 16},
+	{ NETFLOW_V9_FIELD_INPUT_SNMP, 2},
+	{ NETFLOW_V9_FIELD_OUTPUT_SNMP, 2},
+	{ NETFLOW_V9_FIELD_IN_PKTS, sizeof(cntr)},
+	{ NETFLOW_V9_FIELD_IN_BYTES, sizeof(cntr)},
+	{ NETFLOW_V9_FIELD_OUT_PKTS, sizeof(cntr)},
+	{ NETFLOW_V9_FIELD_OUT_BYTES, sizeof(cntr)},
+	{ NETFLOW_V9_FIELD_FIRST_SWITCHED, 4},
+	{ NETFLOW_V9_FIELD_LAST_SWITCHED, 4},
+	{ NETFLOW_V9_FIELD_L4_SRC_PORT, 2},
+	{ NETFLOW_V9_FIELD_L4_DST_PORT, 2},
+	{ NETFLOW_V9_FIELD_TCP_FLAGS, 1},
+	{ NETFLOW_V9_FIELD_PROTOCOL, 1},
+	{ NETFLOW_V9_FIELD_TOS, 1},
+	{ NETFLOW_V9_FIELD_SRC_AS, 4},
+	{ NETFLOW_V9_FIELD_DST_AS, 4},
+	{ NETFLOW_V9_FIELD_SRC_MASK, 1},
+	{ NETFLOW_V9_FIELD_DST_MASK, 1},
+	{0, 0}
+};
+
+
+static int generate_v9_templates(priv_p priv);
+
+MALLOC_DECLARE(M_NETFLOW_GENERAL);
+MALLOC_DEFINE(M_NETFLOW_GENERAL, "netflog_general", "plog, templates data");
 
 /* Generate hash for a given flow record. */
 static __inline uint32_t
@@ -108,10 +201,24 @@
 	switch (r->r_ip_p) {
 	case IPPROTO_TCP:
 	case IPPROTO_UDP:
-		return FULL_HASH(r->r_src.s_addr, r->r_dst.s_addr,
+		return FULL_HASH(r->src.r_src.s_addr, r->dst.r_dst.s_addr,
+		    r->r_sport, r->r_dport);
+	default:
+		return ADDR_HASH(r->src.r_src.s_addr, r->dst.r_dst.s_addr);
+	}
+}
+
+/* Generate hash for a given flow record. Use lower 4 octets from v6 addresses */
+static __inline uint32_t
+ip6_hash(struct flow_rec *r)
+{
+	switch (r->r_ip_p) {
+	case IPPROTO_TCP:
+	case IPPROTO_UDP:
+		return FULL_HASH(r->src.r_src6.__u6_addr.__u6_addr32[3], r->dst.r_dst6.__u6_addr.__u6_addr32[3],
 		    r->r_sport, r->r_dport);
 	default:
-		return ADDR_HASH(r->r_src.s_addr, r->r_dst.s_addr);
+		return ADDR_HASH(r->src.r_src6.__u6_addr.__u6_addr32[3], r->dst.r_dst6.__u6_addr.__u6_addr32[3]);
 	}
 }
 
@@ -126,6 +233,7 @@
 
 	atomic_add_32(&priv->info.nfinfo_used, 1);
 
+
 	return (0);
 }
 
@@ -141,6 +249,7 @@
 /*
  * Detach export datagram from priv, if there is any.
  * If there is no, allocate a new one.
+ * -- V9/IPv6 ready
  */
 static item_p
 get_export_dgram(priv_p priv)
@@ -166,14 +275,163 @@
 			return (NULL);
 		dgram = mtod(m, struct netflow_v5_export_dgram *);
 		dgram->header.count = 0;
-		dgram->header.version = htons(NETFLOW_V5);
+		dgram->header.version = htons(priv->version);
+
+		if (priv->version == NETFLOW_V9) {
+			atomic_fetchadd_32(&priv->sent_packets, 1);
+
+			/*
+			 * Let's insert mbuf tag to store some info
+			 */
+			struct netflow_v9_mbuf_tag *t;
+			struct m_tag *mt = m_tag_alloc(MTAG_NETFLOW, MTAG_NETFLOW_V9, sizeof(struct netflow_v9_mbuf_tag), M_NOWAIT);
+			if (mt == NULL) {
+				m_freem(m);
+				return (NULL);
+			}
+
+			m_tag_init(m);
+			m_tag_prepend(m, mt);
 
+			t = (struct netflow_v9_mbuf_tag *)(mt + 1);
+			t->length = sizeof(struct netflow_v9_header);
+			t->count = 0;
+			t->mtu = priv->mtu;
+			t->flow_header = t->length;
+
+			/*
+			 * Check if we need to insert templates into packet
+			 */
+			
+			struct timespec ts;
+			struct netflow_v9_flowset_header	*fl;
+
+			getnanotime(&ts);
+			if ((ts.tv_sec >= priv->templ_time + priv->templ_last_ts) || (priv->sent_packets >= priv->templ_packets + priv->templ_last_pkt)) {
+				//vlog("INSERTING TEMPLATE: ts: %lu last: %u packets: %u last: %u delay: %u", ts.tv_sec, priv->templ_last_ts, priv->sent_packets, priv->templ_last_pkt, priv->templ_time);
+				atomic_store_rel_32(&priv->templ_last_ts, ts.tv_sec);
+				atomic_store_rel_32(&priv->templ_last_pkt, priv->sent_packets);
+				//vlog("ts: %lu last_t: %u last_p: %u delay: %u", ts.tv_sec, priv->templ_last_ts, priv->templ_last_pkt, priv->templ_time);
+
+				fl = priv->v9_flowsets[0];
+				bcopy(fl, (char *)dgram + t->length, ntohs(fl->length));
+
+				t->length += ntohs(fl->length);
+				t->flow_header = t->length;
+				t->count += priv->flowset_records[0];
+			}
+		
+		}
 	}
 
 	return (item);
 }
 
 /*
+ * Pre-compiles flow exporter for all possible FlowSets
+ * so we can add flowset to packet via simple memcpy()
+ */
+#define __push(x)	*p++ = htons((x))
+static int
+generate_v9_templates(priv_p priv)
+{
+	uint16_t *p, *template_fields_cnt;
+	int cnt;
+
+	int flowset_size = sizeof(struct netflow_v9_flowset_header) +
+		_NETFLOW_V9_TEMPLATE_SIZE(_netflow_v9_record_ipv4_tcp) + /* netflow_v9_record_ipv4_tcp */
+		_NETFLOW_V9_TEMPLATE_SIZE(_netflow_v9_record_ipv6_tcp); /* netflow_v9_record_ipv6_tcp */
+
+	priv->v9_flowsets[0] = malloc(flowset_size, M_NETFLOW_GENERAL, M_WAITOK | M_ZERO);
+	if (priv->v9_flowsets[0] == NULL)
+		return (ENOMEM);
+
+
+	if (flowset_size % 4)
+		flowset_size += 4 - (flowset_size % 4); /* Padding to 4-byte boundary */
+
+	priv->flowsets_count = 1;
+	p = (uint16_t *)priv->v9_flowsets[0];
+	*p++ = 0; /* Flowset ID, 0 is reserved for Template FlowSets  */
+	*p++ = htons(flowset_size); /* Total FlowSet length */
+
+	/*
+	 * Most common TCP/UDP IPv4 template, ID = 256
+	 */
+	*p++ = htons(0x100 + NETFLOW_V9_FLOW_V4_L4);
+	template_fields_cnt = p++;
+	for (cnt = 0; _netflow_v9_record_ipv4_tcp[cnt].field_id != 0; cnt++) {
+		*p++ = htons(_netflow_v9_record_ipv4_tcp[cnt].field_id);
+		*p++ = htons(_netflow_v9_record_ipv4_tcp[cnt].field_length);
+	}
+	*template_fields_cnt = htons(cnt);
+
+	/*
+	 * TCP/UDP IPv6 template, ID = 257
+	 */
+	*p++ = htons(0x100 + NETFLOW_V9_FLOW_V6_L4);
+	template_fields_cnt = p++;
+	for (cnt = 0; _netflow_v9_record_ipv6_tcp[cnt].field_id != 0; cnt++) {
+		*p++ = htons(_netflow_v9_record_ipv6_tcp[cnt].field_id);
+		*p++ = htons(_netflow_v9_record_ipv6_tcp[cnt].field_length);
+	}
+	*template_fields_cnt = htons(cnt);
+
+
+	priv->flowset_records[0] = 2;
+	return (0);
+}
+
+/*
+ * Switches version used for netflow export
+ *
+ */
+void 
+ng_netflow_switch_version(priv_p priv, int ver, int boot)
+{
+	item_p	item = NULL;
+
+	if ((ver != NETFLOW_V9) && (ver != NETFLOW_V5))
+		return;
+
+	if ((ver == priv->version) && (boot == 0))
+		return;
+
+	/*
+	 * All new threads acquiring export datagram will wait for lock
+	 * so we can change pointers.
+	 * Existing threads with export datagram already held will call
+	 * wrong export function which will do version check at the beginning
+	 */
+
+	mtx_lock(&priv->export_mtx);
+	/* XXX: Need to be machine-independent here */
+	record_add = netflow_dispatcher[ver].record_add;
+	record_send = netflow_dispatcher[ver].record_send;
+	//atomic_store_rel_ptr((unsigned long *)record_add, (unsigned long)netflow_dispatcher[ver].record_add);
+	//atomic_store_rel_ptr((unsigned long *)record_send, (unsigned long)netflow_dispatcher[ver].record_send);
+	item = priv->export_item;
+	priv->export_item = NULL;
+	mtx_unlock(&priv->export_mtx);
+
+	if (ver == NETFLOW_V9) {
+		priv->templ_last_pkt = 0;
+		priv->templ_last_ts = 0;
+	}
+
+	//vlog("NEW: add=%p send=%p", record_add, record_send);
+
+	if (item != NULL)
+		(*netflow_dispatcher[priv->version].record_send)(priv, item, NG_NOFLAGS);
+	
+	if (boot)
+		log(LOG_DEBUG, "ng_netflow: v%d export started\n", ver);
+	else
+		log(LOG_DEBUG, "ng_netflow: export switched: v%d -> v%d\n", priv->version, ver);
+	priv->version = ver;
+}
+
+/*
  * Re-attach incomplete datagram back to priv.
  * If there is already another one, then send incomplete. */
 static void
@@ -190,13 +448,14 @@
 		mtx_unlock(&priv->export_mtx);
 	} else {
 		mtx_unlock(&priv->export_mtx);
-		export_send(priv, item, flags);
+		(*record_send)(priv, item, flags);
 	}
 }
 
 /*
  * The flow is over. Call export_add() and free it. If datagram is
  * full, then call export_send().
+ * -- v9/IPv6 nearly ready
  */
 static __inline void
 expire_flow(priv_p priv, item_p *item, struct flow_entry *fle, int flags)
@@ -208,8 +467,8 @@
 		uma_zfree_arg(priv->zone, fle, priv);
 		return;
 	}
-	if (export_add(*item, fle) > 0) {
-		export_send(priv, *item, flags);
+	if ((*record_add)(*item, fle) > 0) {
+		(*record_send)(priv, *item, flags);
 		*item = NULL;
 	}
 	uma_zfree_arg(priv->zone, fle, priv);
@@ -234,12 +493,15 @@
  * to be sure.
  */
 static __inline int
-hash_insert(priv_p priv, struct flow_hash_entry  *hsh, struct flow_rec *r,
+hash_insert(priv_p priv, struct flow_hash_entry  *hsh, struct flow_rec *r, uint16_t eproto,
 	int plen, uint8_t tcp_flags)
 {
 	struct flow_entry	*fle;
-	struct route ro;
-	struct sockaddr_in *sin;
+	struct sockaddr_in sin;
+#ifdef INET6
+	struct sockaddr_in6 sin6;
+#endif
+	struct rtentry *rt;
 
 	mtx_assert(&hsh->mtx, MA_OWNED);
 
@@ -257,6 +519,7 @@
 	bcopy(r, &fle->f.r, sizeof(struct flow_rec));
 	fle->f.bytes = plen;
 	fle->f.packets = 1;
+	fle->f.proto = eproto;
 	fle->f.tcp_flags = tcp_flags;
 
 	fle->f.first = fle->f.last = time_uptime;
@@ -265,52 +528,93 @@
 	 * First we do route table lookup on destination address. So we can
 	 * fill in out_ifx, dst_mask, nexthop, and dst_as in future releases.
 	 */
-	bzero((caddr_t)&ro, sizeof(ro));
-	sin = (struct sockaddr_in *)&ro.ro_dst;
-	sin->sin_len = sizeof(*sin);
-	sin->sin_family = AF_INET;
-	sin->sin_addr = fle->f.r.r_dst;
-	/* XXX MRT 0 as a default.. need the m here to get fib */
-	rtalloc_ign_fib(&ro, RTF_CLONING, 0);
-	if (ro.ro_rt != NULL) {
-		struct rtentry *rt = ro.ro_rt;
-
-		fle->f.fle_o_ifx = rt->rt_ifp->if_index;
-
-		if (rt->rt_flags & RTF_GATEWAY &&
-		    rt->rt_gateway->sa_family == AF_INET)
-			fle->f.next_hop =
-			    ((struct sockaddr_in *)(rt->rt_gateway))->sin_addr;
-
-		if (rt_mask(rt))
-			fle->f.dst_mask = bitcount32(((struct sockaddr_in *)
-			    rt_mask(rt))->sin_addr.s_addr);
-		else if (rt->rt_flags & RTF_HOST)
-			/* Give up. We can't determine mask :( */
-			fle->f.dst_mask = 32;
-
-		RTFREE(ro.ro_rt);
-	}
-
-	/* Do route lookup on source address, to fill in src_mask. */
-
-	bzero((caddr_t)&ro, sizeof(ro));
-	sin = (struct sockaddr_in *)&ro.ro_dst;
-	sin->sin_len = sizeof(*sin);
-	sin->sin_family = AF_INET;
-	sin->sin_addr = fle->f.r.r_src;
-	rtalloc_ign_fib(&ro, RTF_CLONING, 0); /* XXX MRT */
-	if (ro.ro_rt != NULL) {
-		struct rtentry *rt = ro.ro_rt;
-
-		if (rt_mask(rt))
-			fle->f.src_mask = bitcount32(((struct sockaddr_in *)
-			    rt_mask(rt))->sin_addr.s_addr);
-		else if (rt->rt_flags & RTF_HOST)
-			/* Give up. We can't determine mask :( */
-			fle->f.src_mask = 32;
-
-		RTFREE(ro.ro_rt);
+	if (eproto == ETHERTYPE_IP) {
+		bzero(&sin, sizeof(sin));
+		sin.sin_len = sizeof(struct sockaddr_in);
+		sin.sin_family = AF_INET;
+		sin.sin_addr = fle->f.r.dst.r_dst;
+		/* XXX MRT 0 as a default.. need the m here to get fib */
+		rt = rtalloc1_fib((struct sockaddr *)&sin, 0, 0, 0);
+		if (rt != NULL) {
+			fle->f.fle_o_ifx = rt->rt_ifp->if_index;
+	
+			if (rt->rt_flags & RTF_GATEWAY &&
+			    rt->rt_gateway->sa_family == AF_INET)
+				fle->f.n.next_hop =
+				    ((struct sockaddr_in *)(rt->rt_gateway))->sin_addr;
+	
+			if (rt_mask(rt))
+				fle->f.dst_mask = bitcount32(((struct sockaddr_in *)
+				    rt_mask(rt))->sin_addr.s_addr);
+			else if (rt->rt_flags & RTF_HOST)
+				/* Give up. We can't determine mask :( */
+				fle->f.dst_mask = 32;
+	
+			RTFREE_LOCKED(rt);
+		}
+	
+		/* Do route lookup on source address, to fill in src_mask. */
+		bzero(&sin, sizeof(sin));
+		sin.sin_len = sizeof(struct sockaddr_in);
+		sin.sin_family = AF_INET;
+		sin.sin_addr = fle->f.r.src.r_src;
+		/* XXX MRT 0 as a default  revisit.  need the mbuf for fib*/
+		rt = rtalloc1_fib((struct sockaddr *)&sin, 0, 0, 0);
+		if (rt != NULL) {
+			if (rt_mask(rt))
+				fle->f.src_mask = bitcount32(((struct sockaddr_in *)
+				    rt_mask(rt))->sin_addr.s_addr);
+			else if (rt->rt_flags & RTF_HOST)
+				/* Give up. We can't determine mask :( */
+				fle->f.src_mask = 32;
+	
+			RTFREE_LOCKED(rt);
+		}
+	} else {
+#ifdef INET6		
+		bzero(&sin6, sizeof(sin6));
+		sin6.sin6_len = sizeof(struct sockaddr_in6);
+		sin6.sin6_family = AF_INET6;
+		sin6.sin6_addr = fle->f.r.dst.r_dst6;
+		/* XXX fib works for AF_INET only */
+		rt = rtalloc1_fib((struct sockaddr *)&sin6, 0, 0, 0);
+		if (rt != NULL) {
+			fle->f.fle_o_ifx = rt->rt_ifp->if_index;
+	
+			if (rt->rt_flags & RTF_GATEWAY &&
+			    rt->rt_gateway->sa_family == AF_INET6)
+				fle->f.n.next_hop6 =
+				    ((struct sockaddr_in6 *)(rt->rt_gateway))->sin6_addr;
+			if (rt_mask(rt))
+/*	
+				fle->f.dst_mask = bitcount32(((struct sockaddr_in6 *)
+				    rt_mask(rt))->sin_addr.s_addr);
+			else if (rt->rt_flags & RTF_HOST) */
+				/* Give up. We can't determine mask :( */
+				fle->f.dst_mask = 128;
+	
+			RTFREE_LOCKED(rt);
+		}
+	
+		/* Do route lookup on source address, to fill in src_mask. */
+		bzero(&sin6, sizeof(sin6));
+		sin6.sin6_len = sizeof(struct sockaddr_in6);
+		sin6.sin6_family = AF_INET6;
+		sin6.sin6_addr = fle->f.r.src.r_src6;
+		/* XXX MRT 0 as a default  revisit.  need the mbuf for fib*/
+		rt = rtalloc1_fib((struct sockaddr *)&sin6, 0, 0, 0);
+		if (rt != NULL) {
+/*			
+			if (rt_mask(rt))
+				fle->f.src_mask = bitcount32(((struct sockaddr_in6 *)
+				    rt_mask(rt))->sin_addr.s_addr);
+			else if (rt->rt_flags & RTF_HOST) */
+				/* Give up. We can't determine mask :( */
+				fle->f.src_mask = 128;
+	
+			RTFREE_LOCKED(rt);
+		}
+#endif
 	}
 
 	/* Push new flow at the and of hash. */
@@ -354,6 +658,10 @@
 
 	mtx_init(&priv->export_mtx, "export dgram lock", NULL, MTX_DEF);
 
+	generate_v9_templates(priv);
+
+	ng_netflow_switch_version(priv, priv->version, 1);
+
 	return (0);
 }
 
@@ -366,6 +674,7 @@
 	item_p			item = NULL;
 	int i;
 
+
 	/*
 	 * We are going to free probably billable data.
 	 * Expire everything before freeing it.
@@ -378,7 +687,7 @@
 		}
 
 	if (item != NULL)
-		export_send(priv, item, NG_QUEUE);
+		(*record_send)(priv, item, NG_QUEUE);
 
 	uma_zdestroy(priv->zone);
 
@@ -390,46 +699,34 @@
 	if (priv->hash)
 		FREE(priv->hash, M_NETFLOW_HASH);
 
+	/* FreeFlow Tables */
+	for (i = 0; i < priv->flowsets_count; i++)
+		free(priv->v9_flowsets[i], M_NETFLOW_GENERAL);
+
 	mtx_destroy(&priv->export_mtx);
 }
 
-/* Insert packet from into flow cache. */
+/* 
+ * Insert packet from into flow cache. Assume size/version check passed
+ */
 int
-ng_netflow_flow_add(priv_p priv, struct ip *ip, unsigned int src_if_index)
+ng_netflow_flow_add(priv_p priv, caddr_t ip_ptr, caddr_t upper_ptr, uint8_t upper_proto, uint8_t is_frag, unsigned int src_if_index)
 {
 	register struct flow_entry	*fle, *fle1;
 	struct flow_hash_entry		*hsh;
 	struct flow_rec		r;
+	struct ip		*ip = NULL;
+#ifdef INET6
+	struct ip6_hdr		*ip6 = NULL;
+#endif
 	item_p			item = NULL;
-	int			hlen, plen;
+	int			plen;
 	int			error = 0;
 	uint8_t			tcp_flags = 0;
-
-	/* Try to fill flow_rec r */
-	bzero(&r, sizeof(r));
-	/* check version */
-	if (ip->ip_v != IPVERSION)
-		return (EINVAL);
-
-	/* verify min header length */
-	hlen = ip->ip_hl << 2;
-
-	if (hlen < sizeof(struct ip))
-		return (EINVAL);
-
-	r.r_src = ip->ip_src;
-	r.r_dst = ip->ip_dst;
-
-	/* save packet length */
-	plen = ntohs(ip->ip_len);
-
-	r.r_ip_p = ip->ip_p;
-	r.r_tos = ip->ip_tos;
-
-	r.r_i_ifx = src_if_index;
+	uint16_t		eproto;
 
 	/*
-	 * XXX NOTE: only first fragment of fragmented TCP, UDP and
+	 * XXX Fragmentation NOTE: only first fragment of fragmented TCP, UDP and
 	 * ICMP packet will be recorded with proper s_port and d_port.
 	 * Following fragments will be recorded simply as IP packet with
 	 * ip_proto = ip->ip_p and s_port, d_port set to zero.
@@ -437,22 +734,91 @@
 	 * ip packet assebmling here. Anyway, (in)famous trafd works this way -
 	 * and nobody complains yet :)
 	 */
-	if ((ip->ip_off & htons(IP_OFFMASK)) == 0)
-		switch(r.r_ip_p) {
-		case IPPROTO_TCP:
-		{
-			register struct tcphdr *tcp;
-
-			tcp = (struct tcphdr *)((caddr_t )ip + hlen);
-			r.r_sport = tcp->th_sport;
-			r.r_dport = tcp->th_dport;
-			tcp_flags = tcp->th_flags;
-			break;
+
+	/* Try to fill flow_rec r */
+	bzero(&r, sizeof(r));
+	ip = (struct ip *)ip_ptr;
+	if (ip->ip_v == IPVERSION) {
+		eproto = ETHERTYPE_IP;
+		r.src.r_src = ip->ip_src;
+		r.dst.r_dst = ip->ip_dst;
+
+		/* Assume L$ template by default */
+		r.flow_type = NETFLOW_V9_FLOW_V4_L4;
+
+		/* save packet length */
+		plen = ntohs(ip->ip_len);
+
+		r.r_tos = ip->ip_tos;
+
+		if (is_frag == 0) {
+			switch(upper_proto) {
+			case IPPROTO_TCP:
+			{
+				register struct tcphdr *tcp;
+
+				tcp = (struct tcphdr *)upper_ptr;
+				r.r_ports = *(uint32_t *)upper_ptr;
+				tcp_flags = tcp->th_flags;
+				/* r.flow_type = NETFLOW_V9_FLOW_V4_L4; */
+				break;
+			}
+			case IPPROTO_UDP:
+			case IPPROTO_SCTP:
+			{
+				r.r_ports = *(uint32_t *)upper_ptr;
+				/* r.flow_type = NETFLOW_V9_FLOW_V4_L4; */
+				break;
+			}
+
+			}
 		}
+
+	} else {
+#ifdef INET6		
+		/* IPv6 traffic */
+		ip = NULL;
+		ip6 = (struct ip6_hdr *)ip_ptr;
+		eproto = ETHERTYPE_IPV6;
+
+		r.src.r_src6 = ip6->ip6_src;
+		r.dst.r_dst6 = ip6->ip6_dst;
+
+		/* Assume L4 template by default */
+		r.flow_type = NETFLOW_V9_FLOW_V6_L4;
+
+		/* save packet length */
+		plen = ntohs(ip6->ip6_plen) + sizeof(struct ip6_hdr);
+
+		//r.r_tos = ip->ip_tos;
+
+		if (is_frag == 0) {
+			switch(upper_proto) {
+			case IPPROTO_TCP:
+			{
+				register struct tcphdr *tcp;
+
+				tcp = (struct tcphdr *)upper_ptr;
+				r.r_ports = *(uint32_t *)upper_ptr;
+				tcp_flags = tcp->th_flags;
+				/* r.flow_type = NETFLOW_V9_FLOW_V6_L4; */
+				break;
+			}
 			case IPPROTO_UDP:
-			r.r_ports = *(uint32_t *)((caddr_t )ip + hlen);
-			break;
+			case IPPROTO_SCTP:
+			{
+				r.r_ports = *(uint32_t *)upper_ptr;
+				/* r.flow_type = NETFLOW_V9_FLOW_V6_L4; */
+				break;
+			}
+
+			}
 		}
+#endif		
+	}
+
+	r.r_ip_p = upper_proto;
+	r.r_i_ifx = src_if_index;
 
 	/* Update node statistics. XXX: race... */
 	priv->info.nfinfo_packets ++;
@@ -493,7 +859,7 @@
 		 * - it is going to overflow counter
 		 */
 		if (tcp_flags & TH_FIN || tcp_flags & TH_RST || AGED(fle) ||
-		    (fle->f.bytes >= (UINT_MAX - IF_MAXMTU)) ) {
+		    (fle->f.bytes >= (cntr_max - IF_MAXMTU)) ) {
 			TAILQ_REMOVE(&hsh->head, fle, fle_hash);
 			expire_flow(priv, &item, fle, NG_QUEUE);
 			atomic_add_32(&priv->info.nfinfo_act_exp, 1);
@@ -509,7 +875,7 @@
 			}
 		}
 	} else				/* A new flow entry. */
-		error = hash_insert(priv, hsh, &r, plen, tcp_flags);
+		error = hash_insert(priv, hsh, &r, eproto, plen, tcp_flags);
 
 	mtx_unlock(&hsh->mtx);
 
@@ -618,6 +984,65 @@
 	return (error);
 }
 
+/* We have full datagram in privdata. Send it to export hook. */
+static int
+export_send_v9(priv_p priv, item_p item, int flags)
+{
+	struct mbuf *m = NGI_M(item);
+	struct netflow_v9_export_dgram *dgram = mtod(m,
+					struct netflow_v9_export_dgram *);
+	struct netflow_v9_header *header = &dgram->header;
+	struct timespec ts;
+	int error = 0;
+	uint16_t len = 0;
+
+	struct netflow_v9_mbuf_tag *t;
+	struct netflow_v9_flowset_header *fs = NULL;
+	struct m_tag *mt = m_tag_locate(m, MTAG_NETFLOW, MTAG_NETFLOW_V9, NULL);
+
+	if (mt == NULL) {
+		/* */
+		log(LOG_DEBUG, "ng_netflow: V9 export packet without tag!\n");
+		return (0);
+	}
+
+	t = (struct netflow_v9_mbuf_tag *)(mt + 1);
+
+	/* Close FlowSet if not closed */
+	if (t->length != t->flow_header) {
+		fs = (struct netflow_v9_flowset_header *)(mtod(m, char *) + t->flow_header);
+		len = (uint16_t)(t->length - t->flow_header);
+		if (len % 4) {
+			t->length += 4 - (len % 4);
+			len += 4 - (len % 4);
+		}
+		fs->length = htons(len);
+	}
+
+
+
+	/* Fill mbuf header. */
+	m->m_len = m->m_pkthdr.len = t->length;
+
+	/* Fill export header. */
+	header->count = t->count;
+	header->sys_uptime = htonl(MILLIUPTIME(time_uptime));
+	getnanotime(&ts);
+	header->unix_secs  = htonl(ts.tv_sec);
+	header->seq_num = htonl(atomic_fetchadd_32(&priv->flow_seq, 1));
+	header->count = htons(t->count);
+	header->source_id = htonl(NG_NODE_ID(priv->node));
+
+	/* remove tag */
+	m_tag_delete_chain(m, NULL);
+
+	if (priv->export != NULL)
+		NG_FWD_ITEM_HOOK_FLAGS(error, item, priv->export, flags);
+	else
+		NG_FREE_ITEM(item);
+
+	return (error);
+}
 
 /* Add export record to dgram. */
 static int
@@ -635,9 +1060,9 @@
 	    ("ng_netflow: export too big"));
 
 	/* Fill in export record. */
-	rec->src_addr = fle->f.r.r_src.s_addr;
-	rec->dst_addr = fle->f.r.r_dst.s_addr;
-	rec->next_hop = fle->f.next_hop.s_addr;
+	rec->src_addr = fle->f.r.src.r_src.s_addr;
+	rec->dst_addr = fle->f.r.dst.r_dst.s_addr;
+	rec->next_hop = fle->f.n.next_hop.s_addr;
 	rec->i_ifx    = htons(fle->f.fle_i_ifx);
 	rec->o_ifx    = htons(fle->f.fle_o_ifx);
 	rec->packets  = htonl(fle->f.packets);
@@ -661,6 +1086,135 @@
 		return (0);	
 }
 
+/* Add V9 record to dgram. */
+static int
+export_add_v9(item_p item, struct flow_entry *fle)
+{
+	size_t len = 0;
+	uint16_t new_flow = 0;
+	void *offset_ptr;
+	struct netflow_v9_mbuf_tag *t;
+	struct netflow_v9_flowset_header *fs = NULL;
+	struct mbuf *m =  NGI_M(item);
+	struct m_tag *mt = m_tag_locate(m, MTAG_NETFLOW, MTAG_NETFLOW_V9, NULL);
+
+	if (mt == NULL) {
+		/* */
+		log(LOG_DEBUG, "ng_netflow: V9 export packet without tag!\n");
+		return (0);
+	}
+
+	t = (struct netflow_v9_mbuf_tag *)(mt + 1);
+	offset_ptr = (mtod(m, char *) + t->length);
+
+	/* Check if new records has the same template */
+	if (fle->f.r.flow_type != t->flow_type) {
+		new_flow = 1;
+		
+		/* 'Close' old FlowSet */
+		fs = (struct netflow_v9_flowset_header *)(mtod(m, char *) + t->flow_header);
+		len = (uint16_t)(t->length - t->flow_header);
+		if (len % 4) {
+			t->length += 4 - (len % 4);
+			len += 4 - (len % 4);
+		}
+		fs->length = htons(len);
+
+		/* Prepare 'new', but do not modify any counters here because switch can fail */
+		t->flow_type = NETFLOW_V9_FLOW_FAKE;
+		t->flow_header = t->length;
+		fs = (struct netflow_v9_flowset_header *)(mtod(m, char *) + t->length);
+		offset_ptr = (fs + 1);
+	}
+
+
+	switch (fle->f.r.flow_type) {
+	case NETFLOW_V9_FLOW_V4_L4:
+	{
+		/* IPv4 TCP/UDP/[SCTP] */
+		struct netflow_v9_record_ipv4_tcp *rec = (struct netflow_v9_record_ipv4_tcp *)offset_ptr;
+
+		
+		rec->src_addr = fle->f.r.src.r_src.s_addr;
+		rec->dst_addr = fle->f.r.dst.r_dst.s_addr;
+		rec->next_hop = fle->f.n.next_hop.s_addr;
+		rec->i_ifx    = htons(fle->f.fle_i_ifx);
+		rec->o_ifx    = htons(fle->f.fle_o_ifx);
+		rec->i_packets  = htonl(fle->f.packets);
+		rec->i_octets   = htonl(fle->f.bytes);
+		rec->o_packets  = htonl(0);
+		rec->o_octets   = htonl(0);
+		rec->first    = htonl(MILLIUPTIME(fle->f.first));
+		rec->last     = htonl(MILLIUPTIME(fle->f.last));
+		rec->s_port   = fle->f.r.r_sport;
+		rec->d_port   = fle->f.r.r_dport;
+		rec->flags    = fle->f.tcp_flags;
+		rec->prot     = fle->f.r.r_ip_p;
+		rec->tos      = fle->f.r.r_tos;
+		rec->dst_mask = fle->f.dst_mask;
+		rec->src_mask = fle->f.src_mask;
+
+		/* Not supported fields. */
+		rec->src_as = rec->dst_as = 0;
+
+		len = sizeof(struct netflow_v9_record_ipv4_tcp);
+		break;
+	}
+#ifdef INET6	
+	case NETFLOW_V9_FLOW_V6_L4:
+	{
+		/* IPv6 TCP/UDP/[SCTP] */
+		struct netflow_v9_record_ipv6_tcp *rec = (struct netflow_v9_record_ipv6_tcp *)offset_ptr;
+
+		/* ACHTUNG! unchecked code! */
+		rec->src_addr = fle->f.r.src.r_src6;
+		rec->dst_addr = fle->f.r.dst.r_dst6;
+		rec->next_hop = fle->f.n.next_hop6;
+		rec->i_ifx    = htons(fle->f.fle_i_ifx);
+		rec->o_ifx    = htons(fle->f.fle_o_ifx);
+		rec->i_packets  = htonl(fle->f.packets);
+		rec->i_octets   = htonl(fle->f.bytes);
+		rec->first    = htonl(MILLIUPTIME(fle->f.first));
+		rec->last     = htonl(MILLIUPTIME(fle->f.last));
+		rec->s_port   = fle->f.r.r_sport;
+		rec->d_port   = fle->f.r.r_dport;
+		rec->flags    = fle->f.tcp_flags;
+		rec->prot     = fle->f.r.r_ip_p;
+		rec->tos      = fle->f.r.r_tos;
+		rec->dst_mask = fle->f.dst_mask;
+		rec->src_mask = fle->f.src_mask;
+
+		/* Not supported fields. */
+		rec->src_as = rec->dst_as = 0;
+
+		len = sizeof(struct netflow_v9_record_ipv6_tcp);
+		break;
+	}
+#endif	
+	default:
+	{
+		log(LOG_DEBUG, "ng_netflow: Don't know what to do with %d flow type!\n", fle->f.r.flow_type);
+		return (0);
+	}
+	}
+
+	if (new_flow) {
+		/* Generate data segment ID */
+		fs->id = htons(0x100 + fle->f.r.flow_type);
+		t->flow_type = fle->f.r.flow_type;
+		t->length += sizeof(struct netflow_v9_flowset_header);
+	}
+
+	t->length += len;
+	t->count++;
+
+	if (t->length + NETFLOW_V9_MAX_RECORD_SIZE + 4 >= _NETFLOW_V9_MAX_SIZE(t->mtu))
+		return (1); /* end of datagram */
+	else
+		return (0);
+}
+
+
 /* Periodic flow expiry run. */
 void
 ng_netflow_expire(void *arg)
diff -urN sys/netgraph/netflow/ng_netflow.c.orig sys/netgraph/netflow/ng_netflow.c
--- sys/netgraph/netflow/ng_netflow.c.orig	2009-01-09 23:55:26.000000000 +0300
+++ sys/netgraph/netflow/ng_netflow.c	2009-09-06 18:38:40.000000000 +0400
@@ -30,6 +30,8 @@
 static const char rcs_id[] =
     "@(#) $FreeBSD: src/sys/netgraph/netflow/ng_netflow.c,v 1.14.2.4 2009/01/09 20:55:26 mav Exp $";
 
+#include "opt_inet6.h"
+
 #include <sys/param.h>
 #include <sys/systm.h>
 #include <sys/kernel.h>
@@ -48,8 +50,11 @@
 #include <netinet/in.h>
 #include <netinet/in_systm.h>
 #include <netinet/ip.h>
+#include <netinet/ip6.h>
+#include <netinet/ip6.h>
 #include <netinet/tcp.h>
 #include <netinet/udp.h>
+#include <netinet/sctp.h>
 
 #include <netgraph/ng_message.h>
 #include <netgraph/ng_parse.h>
@@ -114,6 +119,30 @@
 	&ng_netflow_setconfig_type_fields
 };
 
+/* Parse type for ng_netflow_setversion */
+static const struct ng_parse_struct_field ng_netflow_setversion_type_fields[]
+	= NG_NETFLOW_SETVERSION_TYPE;
+static const struct ng_parse_type ng_netflow_setversion_type = {
+	&ng_parse_struct_type,
+	&ng_netflow_setversion_type_fields
+};
+
+/* Parse type for ng_netflow_settemplateperiodic */
+static const struct ng_parse_struct_field ng_netflow_settemplateperiodic_type_fields[]
+	= NG_NETFLOW_SETTEMPLATEPERIODIC_TYPE;
+static const struct ng_parse_type ng_netflow_settemplateperiodic_type = {
+	&ng_parse_struct_type,
+	&ng_netflow_settemplateperiodic_type_fields
+};
+
+/* Parse type for ng_netflow_setmtu */
+static const struct ng_parse_struct_field ng_netflow_setmtu_type_fields[]
+	= NG_NETFLOW_SETMTU_TYPE;
+static const struct ng_parse_type ng_netflow_setmtu_type = {
+	&ng_parse_struct_type,
+	&ng_netflow_setmtu_type_fields
+};
+
 /* List of commands and how to convert arguments to/from ASCII */
 static const struct ng_cmdlist ng_netflow_cmds[] = {
        {
@@ -158,6 +187,27 @@
 	&ng_netflow_setconfig_type,
 	NULL
        },
+       {
+	NGM_NETFLOW_COOKIE,
+	NGM_NETFLOW_SETVERSION,
+	"setversion",
+	&ng_netflow_setversion_type,
+	NULL
+       },
+       {
+	NGM_NETFLOW_COOKIE,
+	NGM_NETFLOW_SETTEMPLATEPERIODIC,
+	"settemplateperiodic",
+	&ng_netflow_settemplateperiodic_type,
+	NULL
+       },
+       {
+	NGM_NETFLOW_COOKIE,
+	NGM_NETFLOW_SETMTU,
+	"setmtu",
+	&ng_netflow_setmtu_type,
+	NULL
+       },
        { 0 }
 };
 
@@ -202,6 +252,13 @@
 	for (i = 0; i < NG_NETFLOW_MAXIFACES; i++)
 		priv->ifaces[i].info.conf = NG_NETFLOW_CONF_INGRESS;
 
+	/* Set v9 defaults */
+	priv->version = NETFLOW_V9;
+	priv->templ_time = NETFLOW_V9_MAX_TIME_TEMPL;
+	priv->templ_packets = NETFLOW_V9_MAX_PACKETS_TEMPL;
+	priv->mtu = BASE_MTU;
+	priv->flow_seq = 0;
+
 	/* Initialize callout handle */
 	callout_init(&priv->exp_callout, CALLOUT_MPSAFE);
 
@@ -434,6 +491,52 @@
 	
 			break;
 		}
+		case NGM_NETFLOW_SETVERSION:
+		{
+			struct ng_netflow_setversion *set;
+
+			if (msg->header.arglen != sizeof(struct ng_netflow_setversion))
+				ERROUT(EINVAL);
+
+			set = (struct ng_netflow_setversion *)msg->data;
+			if ((set->version != NETFLOW_V9) && (set->version != NETFLOW_V5))
+				ERROUT(EINVAL);
+
+			ng_netflow_switch_version(priv, set->version, 0);
+
+			break;
+		}
+		case NGM_NETFLOW_SETTEMPLATEPERIODIC:
+		{
+			struct ng_netflow_settemplateperiodic *set;
+
+			if (msg->header.arglen != sizeof(struct ng_netflow_settemplateperiodic))
+				ERROUT(EINVAL);
+
+			set = (struct ng_netflow_settemplateperiodic *)msg->data;
+
+			/* XXX: Set atomic here */
+			priv->templ_packets = set->packets;
+			priv->templ_time = set->time;
+
+			break;
+		}
+		case NGM_NETFLOW_SETMTU:
+		{
+			struct ng_netflow_setmtu *set;
+
+			if (msg->header.arglen != sizeof(struct ng_netflow_setmtu))
+				ERROUT(EINVAL);
+
+			set = (struct ng_netflow_setmtu *)msg->data;
+			if ((set->mtu < MIN_MTU) || (set->mtu > MAX_MTU))
+				ERROUT(EINVAL);
+
+			/* XXX: Set atomic here */
+			priv->mtu = set->mtu;
+
+			break;
+		}
 		case NGM_NETFLOW_SHOW:
 		{
 			uint32_t *last;
@@ -481,12 +584,15 @@
 	const priv_p priv = NG_NODE_PRIVATE(node);
 	const iface_p iface = NG_HOOK_PRIVATE(hook);
 	hook_p out;
-	struct mbuf *m = NULL;
-	struct ip *ip;
+	struct mbuf *m, *m_old = NULL;
+	struct ip *ip = NULL;
+	struct ip6_hdr *ip6 = NULL;
 	struct m_tag *mtag;
-	int pullup_len = 0;
+	int pullup_len = 0, off = 0;
+	uint8_t upper_proto = 0, is_frag = 0;
 	int error = 0, bypass = 0;
 	unsigned int src_if_index;
+	caddr_t upper_ptr = NULL;
 
 	if (hook == priv->export) {
 		/*
@@ -541,6 +647,7 @@
 	}
 
 	NGI_GET_M(item, m);
+	m_old = m;
 
 	/* Increase counters. */
 	iface->info.ifinfo_packets++;
@@ -569,6 +676,22 @@
 	}							\
 } while (0)
 
+/*	
+#define	M_HCHECK(length)	do {				\
+	pullup_len += length + sizeof(ip6_ext);			\
+	if ((m)->m_pkthdr.len < (pullup_len)) {			\
+		pullup_len -= length + sizeof(ip6_ext);
+		M_CHECK(length);
+		`
+	} 							\
+	if ((m)->m_len < (pullup_len) &&			\
+	   (((m) = m_pullup((m),(pullup_len))) == NULL)) {	\
+		error = ENOBUFS;				\
+		goto done;					\
+	}							\
+} while (0)
+*/
+
 	switch (iface->info.ifinfo_dlt) {
 	case DLT_EN10MB:	/* Ethernet */
 	    {
@@ -586,6 +709,33 @@
 			eh = mtod(m, struct ether_header *);
 			ip = (struct ip *)(eh + 1);
 			break;
+#ifdef INET6			
+		case ETHERTYPE_IPV6:
+			/* 
+			 * This is done not to pullup mbuf twice on every ext
+			 * header 
+			 */
+			pullup_len += sizeof(struct ip6_hdr) + sizeof(struct ip6_ext);
+			if (m->m_pkthdr.len >= pullup_len) {
+				if ((m = m_pullup(m, pullup_len)) == NULL) {
+					error = ENOBUFS;
+					goto done;
+				}
+				pullup_len -= sizeof(struct ip6_ext);
+			} else {
+				pullup_len -= sizeof(struct ip6_hdr) + sizeof(struct ip6_ext);
+				M_CHECK(sizeof(struct ip6_hdr));
+				eh = mtod(m, struct ether_header *);
+				ip6 = (struct ip6_hdr *)(eh + 1);
+				if (ip6->ip6_nxt != IPPROTO_NONE) {
+					error = EINVAL;
+					goto bypass;
+				}
+			}
+			eh = mtod(m, struct ether_header *);
+			ip6 = (struct ip6_hdr *)(eh + 1);
+			break;
+#endif			
 		case ETHERTYPE_VLAN:
 		    {
 			struct ether_vlan_header *evh;
@@ -597,6 +747,29 @@
 				M_CHECK(sizeof(struct ip));
 				ip = (struct ip *)(evh + 1);
 				break;
+			} else if (ntohs(evh->evl_proto) == ETHERTYPE_IPV6) {
+#ifdef INET6				
+				pullup_len += sizeof(struct ip6_hdr) + sizeof(struct ip6_ext);
+				if (m->m_pkthdr.len >= pullup_len) {
+					if ((m = m_pullup(m, pullup_len)) == NULL) {
+						error = ENOBUFS;
+						goto done;
+					}
+					pullup_len -= sizeof(struct ip6_ext);
+				} else {
+					pullup_len -= sizeof(struct ip6_hdr) + sizeof(struct ip6_ext);
+					M_CHECK(sizeof(struct ip6_hdr));
+					eh = mtod(m, struct ether_header *);
+					ip6 = (struct ip6_hdr *)(eh + 1);
+					if (ip6->ip6_nxt != IPPROTO_NONE) {
+						error = EINVAL;
+						goto bypass;
+					}
+				}
+				eh = mtod(m, struct ether_header *);
+				ip6 = (struct ip6_hdr *)(eh + 1);
+				break;
+#endif				
 			}
 		    }
 		default:
@@ -607,18 +780,32 @@
 	case DLT_RAW:		/* IP packets */
 		M_CHECK(sizeof(struct ip));
 		ip = mtod(m, struct ip *);
+#ifdef INET6		
+		if (ip->ip_v == 6) {
+			/* IPv6 packet */
+			ip = NULL;
+			M_CHECK(sizeof(struct ip6_hdr));
+			ip6 = mtod(m, struct ip6_hdr *);
+		}
+#endif		
 		break;
 	default:
 		goto bypass;
 		break;
 	}
 
-	if ((ip->ip_off & htons(IP_OFFMASK)) == 0) {
+	if ((ip != NULL) && ((ip->ip_off & htons(IP_OFFMASK)) == 0)) {
+		if ((ip->ip_v != IPVERSION) || 
+		    ((ip->ip_hl << 2) < sizeof(struct ip)))
+			goto bypass;
 		/*
-		 * In case of IP header with options, we haven't pulled
+		 * In case of IPv4 header with options, we haven't pulled
 		 * up enough, yet.
 		 */
 		pullup_len += (ip->ip_hl << 2) - sizeof(struct ip);
+		off = pullup_len;
+		//vlog("upper_offset = %d", off);
+		upper_proto = ip->ip_p;
 
 		switch (ip->ip_p) {
 		case IPPROTO_TCP:
@@ -627,38 +814,161 @@
 		case IPPROTO_UDP:
 			M_CHECK(sizeof(struct udphdr));
 			break;
+		case IPPROTO_SCTP:
+			M_CHECK(sizeof(struct sctphdr));
+			break;
 		}
-	}
+	} else if (ip != NULL) {
+		is_frag = 1;
+		upper_proto = ip->ip_p;
+		if ((ip->ip_v != IPVERSION) || 
+		    ((ip->ip_hl << 2) < sizeof(struct ip)))
+			goto bypass;
+	} else if (ip6 != NULL) {
+#ifdef INET6		
+		/* Check if we can export */
+		if (priv->version < NETFLOW_V9)
+			goto bypass;
+
+		/* Loop thru IPv6 extended headers to get upper layer header / frag */
+		int cur = ip6->ip6_nxt, hdr_off = 0;
+		struct ip6_ext *ip6e;
+		struct ip6_frag *ip6f;
+
+		off = pullup_len;
+		upper_proto = cur;
+
+		if ((ip6->ip6_vfc & IPV6_VERSION_MASK) != IPV6_VERSION)
+			goto bypass;
+
+		while (42) {
+
+			/*
+			 * At the moment we're 'standing' with pullup_len and
+			 * off 
+			 */
+			switch (cur) {
+
+			/* Ensure pointer is contiguous */
+			case IPPROTO_TCP:
+				M_CHECK(sizeof(struct tcphdr));
+				goto loopend;
+			case IPPROTO_UDP:
+				M_CHECK(sizeof(struct udphdr));
+				goto loopend;
+			case IPPROTO_SCTP:
+				M_CHECK(sizeof(struct sctphdr));
+				goto loopend;
+
+			/* Loop until 'real' upper layer headers */
+			case IPPROTO_HOPOPTS:
+			case IPPROTO_ROUTING:
+			case IPPROTO_DSTOPTS:
+				ip6e = (struct ip6_ext *)(mtod(m, caddr_t) + off);
+				upper_proto = ip6e->ip6e_nxt;
+				hdr_off = (ip6e->ip6e_len + 1) << 3;
+				break;
 
-	switch (iface->info.ifinfo_dlt) {
-	case DLT_EN10MB:
-	    {
-		struct ether_header *eh;
+			/* RFC4302, can be before DSTOPTS */
+			case IPPROTO_AH:
+				ip6e = (struct ip6_ext *)(mtod(m, caddr_t) + off);
+				upper_proto = ip6e->ip6e_nxt;
+				hdr_off = (ip6e->ip6e_len + 2) << 2;
+				break;
 
-		eh = mtod(m, struct ether_header *);
-		switch (ntohs(eh->ether_type)) {
-		case ETHERTYPE_IP:
-			ip = (struct ip *)(eh + 1);
-			break;
-		case ETHERTYPE_VLAN:
-		    {
-			struct ether_vlan_header *evh;
+			
+			case IPPROTO_FRAGMENT:
+				ip6f = (struct ip6_frag *)(mtod(m, caddr_t) + off);
+				upper_proto = ip6f->ip6f_nxt;
+				hdr_off = sizeof(struct ip6_frag);
+				off += hdr_off;
+				is_frag = 1;
+				goto loopend;
+
+/*				
+			case IPPROTO_NONE:
+				goto loopend;
+*/
+			default:
+				goto loopend;
+			}
 
-			evh = mtod(m, struct ether_vlan_header *);
-			ip = (struct ip *)(evh + 1);
+			off += hdr_off + sizeof(struct ip6_ext);
+			cur = upper_proto;
+			if (m->m_pkthdr.len >= off) {
+				if ((m = m_pullup(m, off)) == NULL) {
+					error = ENOBUFS;
+					goto done;
+				}
+				off -= sizeof(struct ip6_ext);
+				pullup_len += hdr_off;
+			} else {
+				/*
+				 * Packet ends somewhere here.
+				 * if its next header is not IPPROTO_NONE it is
+				 * possibly mailformed packet. Let's count as RAW IP
+				 */
+				upper_proto = IPPROTO_NONE;
+				off -= sizeof(struct ip6_ext);
+				goto loopend;
+			}
+
+		}
+#endif
+	}
+
+#ifdef INET6
+loopend:
+#endif
+	if (m != m_old) {
+		switch (iface->info.ifinfo_dlt) {
+		case DLT_EN10MB:	/* Ethernet */
+		    {
+			struct ether_header *eh;
+	
+			eh = mtod(m, struct ether_header *);
+			switch (ntohs(eh->ether_type)) {
+			case ETHERTYPE_IP:
+				ip = (struct ip *)(eh + 1);
+				break;
+			case ETHERTYPE_IPV6:
+				ip6 = (struct ip6_hdr *)(eh + 1);
+				break;
+			case ETHERTYPE_VLAN:
+			    {
+				struct ether_vlan_header *evh;
+	
+				evh = mtod(m, struct ether_vlan_header *);
+				if (ntohs(evh->evl_proto) == ETHERTYPE_IP) {
+					ip = (struct ip *)(evh + 1);
+					break;
+				} else if (ntohs(evh->evl_proto) == ETHERTYPE_IPV6) {
+#ifdef INET6					
+					ip6 = (struct ip6_hdr *)(evh + 1);
+					break;
+#endif					
+				}
+			    }
+			default:
+				panic("ng_netflow entered deadcode");
+			}
+			break;
+		    }
+		case DLT_RAW:		/* IP packets */
+			ip = mtod(m, struct ip *);
+#ifdef INET6			
+			if (ip->ip_v == 6) {
+				/* IPv6 packet */
+				ip = NULL;
+				ip6 = mtod(m, struct ip6_hdr *);
+			}
+#endif			
 			break;
-		     }
 		default:
 			panic("ng_netflow entered deadcode");
 		}
-		break;
-	     }
-	case DLT_RAW:
-		ip = mtod(m, struct ip *);
-		break;
-	default:
-		panic("ng_netflow entered deadcode");
 	}
+	upper_ptr = (caddr_t)(mtod(m, caddr_t) + off);
 
 #undef	M_CHECK
 
@@ -670,7 +980,7 @@
 	} else
 		src_if_index = iface->info.ifinfo_index;
 
-	error = ng_netflow_flow_add(priv, ip, src_if_index);
+	error = ng_netflow_flow_add(priv, (ip != NULL) ? (caddr_t)ip : (caddr_t)ip6, upper_ptr, upper_proto, is_frag, src_if_index);
 
 bypass:
 	if (out != NULL) {
diff -urN sys/netgraph/_netflow.orig/netflow.h sys/netgraph/netflow/netflow.h
--- sys/netgraph/_netflow.orig/netflow.h	2009-09-01 02:37:44.000000000 +0400
+++ sys/netgraph/netflow/netflow.h	2009-09-06 12:41:29.000000000 +0400
@@ -46,6 +46,7 @@
 
 #define NETFLOW_V1 1
 #define NETFLOW_V5 5
+#define NETFLOW_V9 9
 
 struct netflow_v1_header
 {
@@ -69,6 +70,16 @@
   uint16_t pad;		/* Pad to word boundary */
 } __attribute__((__packed__));
 
+struct netflow_v9_header
+{
+  uint16_t version;	/* NetFlow version */
+  uint16_t count;	/* Total number of records in packet */
+  uint32_t sys_uptime;	/* System uptime */
+  uint32_t unix_secs;	/* Current seconds since 0000 UTC 1970 */
+  uint32_t seq_num;	/* Sequence number */
+  uint32_t source_id;	/* Observation Domain id */
+} __attribute__((__packed__));
+
 struct netflow_v1_record
 {
   uint32_t src_addr;	/* Source IP address */
@@ -115,6 +126,142 @@
   uint16_t pad2;	/* Pad to word boundary */
 } __attribute__((__packed__));
 
+
+
+/* RFC3954 field definitions */
+#define NETFLOW_V9_FIELD_IN_BYTES	1	/* Input bytes count for a flow. Default 4, can be 8 */
+#define NETFLOW_V9_FIELD_IN_PKTS	2	/* Incoming counter with number of packets associated with an IP Flow. Default 4 */
+#define NETFLOW_V9_FIELD_FLOWS		3	/* Number of Flows that were aggregated. Default 4 */
+#define NETFLOW_V9_FIELD_PROTOCOL	4	/* IP protocol byte. 1 */
+#define NETFLOW_V9_FIELD_TOS		5	/* Type of service byte setting when entering the incoming interface. 1 */
+#define NETFLOW_V9_FIELD_TCP_FLAGS	6	/* TCP flags; cumulative of all the TCP flags seen in this Flow. 1 */
+#define NETFLOW_V9_FIELD_L4_SRC_PORT	7	/* TCP/UDP source port number. 2 */
+#define NETFLOW_V9_FIELD_IPV4_SRC_ADDR	8	/* IPv4 source address. 4 */
+#define NETFLOW_V9_FIELD_SRC_MASK	9	/* The number of contiguous bits in the source subnet mask (i.e., the mask in slash notation). 1 */
+#define NETFLOW_V9_FIELD_INPUT_SNMP	10	/* Input interface index. Default 2 */
+#define NETFLOW_V9_FIELD_L4_DST_PORT	11	/* TCP/UDP destination port number. 2 */
+#define NETFLOW_V9_FIELD_IPV4_DST_ADDR	12	/* IPv4 destination address. 4 */
+#define NETFLOW_V9_FIELD_DST_MASK	13	/* The number of contiguous bits in the destination subnet mask (i.e., the mask in slash notation). 1 */
+#define NETFLOW_V9_FIELD_OUTPUT_SNMP	14	/* Output interface index. Default 2 */
+#define NETFLOW_V9_FIELD_IPV4_NEXT_HOP	15	/* IPv4 address of the next-hop router. 4 */
+#define NETFLOW_V9_FIELD_SRC_AS		16	/* Source BGP autonomous system number. Default 2, can be 4 */
+#define NETFLOW_V9_FIELD_DST_AS		17	/* Destination BGP autonomous system number. Default 2, can be 4 */
+#define NETFLOW_V9_FIELD_BGP_IPV4_NEXT_HOP	18	/* Next-hop router's IP address in the BGP domain. 4 */
+#define NETFLOW_V9_FIELD_MUL_DST_PKTS	19	/* IP multicast outgoing packet counter for packets associated with IP flow. Default 4 */
+#define NETFLOW_V9_FIELD_MUL_DST_BYTES	20	/* IP multicast outgoing Octet (byte) counter for the number of bytes associated with IP flow. Default 4 */
+#define NETFLOW_V9_FIELD_LAST_SWITCHED	21	/* sysUptime in msec at which the last packet of this Flow was switched. 4 */
+#define NETFLOW_V9_FIELD_FIRST_SWITCHED	22	/* sysUptime in msec at which the first packet of this Flow was switched. 4 */
+#define NETFLOW_V9_FIELD_OUT_BYTES	23	/* Outgoing counter for the number of bytes associated with an IP Flow. Default 4 */
+#define NETFLOW_V9_FIELD_OUT_PKTS	24	/* Outgoing counter for the number of packets associated with an IP Flow. Default 4 */
+#define NETFLOW_V9_FIELD_IPV6_SRC_ADDR	27	/* IPv6 source address. 16 */
+#define NETFLOW_V9_FIELD_IPV6_DST_ADDR	28	/* IPv6 destination address. 16 */
+#define NETFLOW_V9_FIELD_IPV6_SRC_MASK	29	/* Length of the IPv6 source mask in contiguous bits. 1 */
+#define NETFLOW_V9_FIELD_IPV6_DST_MASK	30	/* Length of the IPv6 destination mask in contiguous bits. 1 */
+#define NETFLOW_V9_FIELD_IPV6_FLOW_LABEL	31	/* IPv6 flow label as per RFC 2460 definition. 3 */
+#define NETFLOW_V9_FIELD_ICMP_TYPE	32	/* Internet Control Message Protocol (ICMP) packet type; reported as ICMP Type * 256 + ICMP code. 2 */
+#define NETFLOW_V9_FIELD_MUL_IGMP_TYPE	33	/* Internet Group Management Protocol (IGMP) packet type. 1 */
+#define NETFLOW_V9_FIELD_SAMPLING_INTERVAL	34	/* When using sampled NetFlow, the rate at which packets are sampled; for example, a value of 100 indicates that one of every hundred packets is sampled. 4 */
+#define NETFLOW_V9_FIELD_SAMPLING_ALGORITHM	35	/* For sampled NetFlow platform-wide: 0x01 deterministic sampling 0x02 random sampling. 1 */
+#define NETFLOW_V9_FIELD_FLOW_ACTIVE_TIMEOUT	36	/* Timeout value (in seconds) for active flow entries in the NetFlow cache. 2 */
+#define NETFLOW_V9_FIELD_FLOW_INACTIVE_TIMEOUT	37	/* Timeout value (in seconds) for inactive Flow entries in the NetFlow cache. 2 */
+#define NETFLOW_V9_FIELD_ENGINE_TYPE	38	/* Type of Flow switching engine (route processor, linecard, etc...). 1 */
+#define NETFLOW_V9_FIELD_ENGINE_ID	39	/* ID number of the Flow switching engine. 1 */
+#define NETFLOW_V9_FIELD_TOTAL_BYTES_EXP	40	/* Counter with for the number of bytes exported by the Observation Domain. Default 4 */
+#define NETFLOW_V9_FIELD_TOTAL_PKTS_EXP	41	/* Counter with for the number of packets exported by the Observation Domain. Default 4 */
+#define NETFLOW_V9_FIELD_TOTAL_FLOWS_EXP	42	/* Counter with for the number of flows exported by the Observation Domain. Default 4 */
+#define NETFLOW_V9_FIELD_MPLS_TOP_LABEL_TYPE	46	/* MPLS Top Label Type. 1 */
+#define NETFLOW_V9_FIELD_MPLS_TOP_LABEL_IP_ADDR	47	/* Forwarding Equivalent Class corresponding to the MPLS Top Label. 4 */
+#define NETFLOW_V9_FIELD_FLOW_SAMPLER_ID	48	/* Identifier shown in "show flow-sampler". 1 */
+#define NETFLOW_V9_FIELD_FLOW_SAMPLER_MODE	49	/* The type of algorithm used for sampling data. 2 */
+#define NETFLOW_V9_FIELD_FLOW_SAMPLER_RANDOM_INTERVAL	50	/* Packet interval at which to sample. 4. */
+#define NETFLOW_V9_FIELD_DST_TOS	55	/* Type of Service byte setting when exiting outgoing interface. 1. */
+#define NETFLOW_V9_FIELD_SRC_MAC	56	/* Source MAC Address. 6 */
+#define NETFLOW_V9_FIELD_DST_MAC	57	/* Destination MAC Address. 6 */
+#define NETFLOW_V9_FIELD_SRC_VLAN	58	/* Virtual LAN identifier associated with ingress interface. 2 */
+#define NETFLOW_V9_FIELD_DST_VLAN	59	/* irtual LAN identifier associated with egress interface. 2 */
+#define NETFLOW_V9_FIELD_IP_PROTOCOL_VERSION	60	/* Internet Protocol Version. Set to 4 for IPv4, set to 6 for IPv6. If not present in the template, then version 4 is assumed. 1. */
+#define NETFLOW_V9_FIELD_DIRECTION	61	/* Flow direction: 0 - ingress flow 1 - egress flow. 1 */
+#define NETFLOW_V9_FIELD_IPV6_NEXT_HOP	62	/* IPv6 address of the next-hop router. 16 */
+#define NETFLOW_V9_FIELD_BGP_IPV6_NEXT_HOP	63	/* Next-hop router in the BGP domain. 16 */
+#define NETFLOW_V9_FIELD_IPV6_OPTION_HEADERS	64	/* Bit-encoded field identifying IPv6 option headers found in the flow */
+#define NETFLOW_V9_FIELD_MPLS_LABEL_1	70	/* MPLS label at position 1 in the stack. 3 */
+#define NETFLOW_V9_FIELD_MPLS_LABEL_2	71	/* MPLS label at position 2 in the stack. 3 */
+#define NETFLOW_V9_FIELD_MPLS_LABEL_3	72	/* MPLS label at position 3 in the stack. 3 */
+#define NETFLOW_V9_FIELD_MPLS_LABEL_4	73	/* MPLS label at position 4 in the stack. 3 */
+#define NETFLOW_V9_FIELD_MPLS_LABEL_5	74	/* MPLS label at position 5 in the stack. 3 */
+#define NETFLOW_V9_FIELD_MPLS_LABEL_6	75	/* MPLS label at position 6 in the stack. 3 */
+#define NETFLOW_V9_FIELD_MPLS_LABEL_7	76	/* MPLS label at position 7 in the stack. 3 */
+#define NETFLOW_V9_FIELD_MPLS_LABEL_8	77	/* MPLS label at position 8 in the stack. 3 */
+#define NETFLOW_V9_FIELD_MPLS_LABEL_9	78	/* MPLS label at position 9 in the stack. 3 */
+#define NETFLOW_V9_FIELD_MPLS_LABEL_10	79	/* MPLS label at position 10 in the stack. 3 */
+
+#ifdef COUNTERS_64
+#define cntr		uint64_t
+#define cntr_max	UINT64_MAX
+#else
+#define cntr		uint32_t
+#define cntr_max	UINT_MAX
+#endif
+
+struct netflow_v9_template
+{
+	int	field_id;
+	int	field_length;
+};
+
+/* Template ID for tcp/udp v4 streams ID:257 (0x100 + NETFLOW_V9_FLOW_V4_L4) */
+struct netflow_v9_record_ipv4_tcp
+{
+  uint32_t src_addr;	/* Source IPv4 address (IPV4_SRC_ADDR) */
+  uint32_t dst_addr;	/* Destination IPv4 address (IPV4_DST_ADDR) */
+  uint32_t next_hop;	/* Next hop IPv4 address (IPV4_NEXT_HOP) */  
+  uint16_t i_ifx;	/* Source interface index (INPUT_SNMP) */
+  uint16_t o_ifx;	/* Destination interface index (OUTPUT_SNMP) */
+  cntr i_packets;	/* Number of incoming packets in a flow (IN_PKTS) */
+  cntr i_octets;	/* Number of incoming octets in a flow (IN_BYTES) */
+  cntr o_packets;	/* Number of outgoing packets in a flow (OUT_PKTS) */
+  cntr o_octets;	/* Number of outgoing octets in a flow (OUT_BYTES) */
+  uint32_t first;	/* System uptime at start of a flow (FIRST_SWITCHED) */
+  uint32_t last;	/* System uptime at end of a flow (LAST_SWITCHED) */
+  uint16_t s_port;	/* Source port (L4_SRC_PORT) */
+  uint16_t d_port;	/* Destination port (L4_DST_PORT) */
+  uint8_t flags;	/* Cumulative OR of tcp flags (TCP_FLAGS) */
+  uint8_t prot;		/* IP protocol */
+  uint8_t tos;		/* IP type of service IN (or OUT) (TOS) */
+  uint32_t src_as;	/* Src peer/origin Autonomous System (SRC_AS) */  
+  uint32_t dst_as;	/* Dst peer/origin Autonomous System (DST_AS) */
+  uint8_t src_mask;	/* Source route's mask bits (SRC_MASK) */
+  uint8_t dst_mask; 	/* Destination route's mask bits (DST_MASK) */
+} __attribute__((__packed__));
+
+
+/* Template ID for tcp/udp v6 streams ID: 260 (0x100 + NETFLOW_V9_FLOW_V6_L4) */
+struct netflow_v9_record_ipv6_tcp
+{
+  struct in6_addr src_addr;	/* Source IPv6 address (IPV6_SRC_ADDR) */
+  struct in6_addr dst_addr;	/* Destination IPv6 address (IPV6_DST_ADDR) */
+  struct in6_addr next_hop;	/* Next hop IPv6 address (IPV6_NEXT_HOP) */  
+  uint16_t i_ifx;	/* Source interface index (INPUT_SNMP) */
+  uint16_t o_ifx;	/* Destination interface index (OUTPUT_SNMP) */
+  cntr i_packets;	/* Number of incoming packets in a flow (IN_PKTS) */
+  cntr i_octets;	/* Number of incoming octets in a flow (IN_BYTES) */
+  cntr o_packets;	/* Number of outgoing packets in a flow (OUT_PKTS) */
+  cntr o_octets;	/* Number of outgoing octets in a flow (OUT_BYTES) */
+  uint32_t first;	/* System uptime at start of a flow (FIRST_SWITCHED) */
+  uint32_t last;	/* System uptime at end of a flow (LAST_SWITCHED) */
+  uint16_t s_port;	/* Source port (L4_SRC_PORT) */
+  uint16_t d_port;	/* Destination port (L4_DST_PORT) */
+  uint8_t flags;	/* Cumulative OR of tcp flags (TCP_FLAGS) */
+  uint8_t prot;		/* IP protocol */
+  uint8_t tos;		/* IP type of service IN (or OUT) (TOS) */
+  uint32_t src_as;	/* Src peer/origin Autonomous System (SRC_AS) */  
+  uint32_t dst_as;	/* Dst peer/origin Autonomous System (DST_AS) */
+  uint8_t src_mask;	/* Source route's mask bits (SRC_MASK) */
+  uint8_t dst_mask; 	/* Destination route's mask bits (DST_MASK) */
+} __attribute__((__packed__));
+
+#define vlog(x, ...)	log(LOG_DEBUG, "%s:%d " x "\n", __FUNCTION__, __LINE__, ##__VA_ARGS__)
+
 #define NETFLOW_V1_MAX_RECORDS 24
 #define NETFLOW_V5_MAX_RECORDS 30
 
@@ -123,7 +270,50 @@
 #define NETFLOW_V5_MAX_SIZE (sizeof(netflow_v5_header)+ \
 			     sizeof(netflow_v5_record)*NETFLOW_V5_MAX_RECORDS)
 
+#define BASE_MTU	1500
+#define MIN_MTU		sizeof(struct netflow_v5_header)
+#define MAX_MTU		16384
+#define NETFLOW_V9_MAX_SIZE	_NETFLOW_V9_MAX_SIZE(BASE_MTU)
+#define _NETFLOW_V9_MAX_SIZE(x)	(x) - sizeof(struct ip6_hdr) - sizeof(struct udphdr)
+#define NETFLOW_V9_MAX_FLOWSETS	2
+
+#define NETFLOW_V9_MAX_RECORD_SIZE	sizeof(struct netflow_v9_record_ipv6_tcp)
+#define NETFLOW_V9_MAX_PACKETS_TEMPL	500	/* Send data templates every ... packets */
+#define NETFLOW_V9_MAX_TIME_TEMPL	600	/* Send data templates every ... seconds */
+#define NETFLOW_V9_MAX_TEMPLATES	16	/* Not a real value */
+#define _NETFLOW_V9_TEMPLATE_SIZE(x)	(sizeof(x) / sizeof(struct netflow_v9_template)) * 4
+//#define _NETFLOW_V9_TEMPLATE_SIZE(x)	((x) + 1) * 4
+
+/* Flow Templates */
+#define NETFLOW_V9_FLOW_V4_L4	1 /* IPv4 TCP/UDP packet */
+#define NETFLOW_V9_FLOW_V4_ICMP	2 /* IPv4 ICMP packet, currently unused */
+#define NETFLOW_V9_FLOW_V4_L3	3 /* IPv4 IP packet */
+#define NETFLOW_V9_FLOW_V6_L4	4 /* IPv6 TCP/UDP packet */
+#define NETFLOW_V9_FLOW_V6_ICMP	5 /* IPv6 ICMP packet, currently unused */
+#define NETFLOW_V9_FLOW_V6_L3	6 /* IPv6 IP packet */
+
+#define NETFLOW_V9_FLOW_FAKE	65535 /* Not uset used in real flowsets! */
+
 struct netflow_v5_export_dgram {
 	struct netflow_v5_header	header;
 	struct netflow_v5_record	r[NETFLOW_V5_MAX_RECORDS];
 } __attribute__((__packed__));
+
+struct netflow_v9_export_dgram {
+	struct netflow_v9_header	header;
+	char				data; /* MTU can change, record length is dynamic */
+};
+
+struct netflow_v9_flowset_header {
+	uint16_t	id; /* FlowSet id */
+	uint16_t	length; /* FlowSet length */
+} __attribute__((__packed__));
+
+struct netflow_v9_mbuf_tag {
+	struct m_tag	tag; /* pointer to mbuf tag */
+	uint16_t	length; /* Current packet length */
+	uint16_t	count; /* current records count */
+	uint16_t	mtu; /* max MTU shapshot */
+	uint16_t	flow_type; /* current flowset */
+	uint16_t	flow_header; /* offset pointing to current flow header */
+};
diff -urN sys/netgraph/_netflow.orig/ng_netflow.h sys/netgraph/netflow/ng_netflow.h
--- sys/netgraph/_netflow.orig/ng_netflow.h	2009-09-01 02:37:44.000000000 +0400
+++ sys/netgraph/netflow/ng_netflow.h	2009-09-06 16:36:17.000000000 +0400
@@ -51,6 +51,9 @@
     NGM_NETFLOW_SETIFINDEX	= 5, 	/* set interface index */
     NGM_NETFLOW_SETTIMEOUTS	= 6, 	/* set active/inactive flow timeouts */
     NGM_NETFLOW_SETCONFIG	= 7, 	/* set flow generation options */
+    NGM_NETFLOW_SETVERSION	= 8, 	/* set flow export version  */
+    NGM_NETFLOW_SETTEMPLATEPERIODIC	= 9, 	/* set v9 flow template periodic */
+    NGM_NETFLOW_SETMTU		= 10, 	/* set outgoing interface MTU */
 };
 
 /* This structure is returned by the NGM_NETFLOW_INFO message */
@@ -105,10 +108,34 @@
 	u_int32_t conf;			/* new config */
 };
 
+/* This structure is passed to NGM_NETFLOW_SETVERSION */
+struct ng_netflow_setversion {
+	u_int8_t version;		/* netflow version */
+};
+
+/* This structure is passed to NGM_NETFLOW_SETTEMPLATEPERIODIC */
+struct ng_netflow_settemplateperiodic {
+	u_int16_t time;		/* max time between announce */
+	u_int16_t packets;	/* max packets between announce */
+};
+
+/* This structure is passed to NGM_NETFLOW_SETMTU */
+struct ng_netflow_setmtu {
+	u_int16_t mtu;		/* MTU for packet */
+};
+
 /* This is unique data, which identifies flow */
 struct flow_rec {
-	struct in_addr	r_src;
-	struct in_addr	r_dst;
+	uint16_t	flow_type; /* IPv4 L4/L3 Ipv6 L4/L3 flow, see NETFLOW_V9_FLOW* */
+	uint16_t	ether_proto; /* unused */
+	union {
+		struct in_addr	r_src;
+		struct in6_addr	r_src6;
+	} src;
+	union {
+		struct in_addr	r_dst;
+		struct in6_addr	r_dst6;
+	} dst;
 	union {
 		struct {
 			uint16_t	s_port;	/* source TCP/UDP port */
@@ -137,7 +164,10 @@
 /* A flow entry which accumulates statistics */
 struct flow_entry_data {
 	struct flow_rec		r;
-	struct in_addr		next_hop;
+	union {
+		struct in_addr		next_hop;
+		struct in6_addr		next_hop6;
+	} n;
 	uint16_t		fle_o_ifx;	/* output interface index */
 #define				fle_i_ifx	r.misc.i.i_ifx
 	uint8_t		dst_mask;	/* destination route mask bits */
@@ -146,6 +176,7 @@
 	u_long			bytes;
 	long			first;	/* uptime on first packet */
 	long			last;	/* uptime on last packet */
+	uint16_t		proto; /* IP/IPv6 */
 	u_char			tcp_flags;	/* cumulative OR */
 };
 
@@ -227,6 +258,26 @@
 	{ NULL }					\
 }
 
+/* Parse the setversion structure */
+#define	NG_NETFLOW_SETVERSION_TYPE {			\
+	{ "version",	&ng_parse_uint8_type },		\
+	{ NULL }					\
+}
+
+/* Parse the settemplateperiodic structure */
+#define	NG_NETFLOW_SETTEMPLATEPERIODIC_TYPE {			\
+	{ "time",	&ng_parse_uint16_type },		\
+	{ "packets",	&ng_parse_uint16_type },		\
+	{ NULL }					\
+}
+
+/* Parse the setmtu structure */
+#define	NG_NETFLOW_SETMTU_TYPE {			\
+	{ "mtu",	&ng_parse_uint16_type },		\
+	{ NULL }					\
+}
+
+
 /* Private hook data */
 struct ng_netflow_iface {
 	hook_p		hook;		/* NULL when disconnected */
@@ -270,6 +321,20 @@
 	item_p			export_item;
 	struct mtx		export_mtx;
 	uint32_t		flow_seq;	/* current flow sequence */
+	/* 
+	 * RFC 3954 clause 7.3
+	 * "Both options MUST be configurable by the user on the Exporter."
+	 */
+	uint16_t		templ_time;	/* time between sending templates */
+	uint16_t		templ_packets;	/* packets between sending templates */
+	uint32_t		templ_last_ts;	/* unixtime of last template announce */
+	uint32_t		templ_last_pkt;	/* packets count on last template announce */
+	uint32_t		sent_packets;	/* packets sent by exporeter; can be reached another way ? */
+	u_char			version;	/* Netflow export version */
+	u_char			flowsets_count; /* current flowsets used */
+	u_char			flowset_records[NETFLOW_V9_MAX_FLOWSETS - 1]; /* Count of records in each flowset */
+	uint16_t		mtu;		/* export interface MTU */
+	struct netflow_v9_flowset_header	*v9_flowsets[NETFLOW_V9_MAX_FLOWSETS - 1]; /* Pointers to pre-compiled flowsets */
 
 	struct ng_netflow_iface	ifaces[NG_NETFLOW_MAXIFACES];
 };
@@ -286,14 +351,15 @@
 
 #define MTAG_NETFLOW		1221656444
 #define MTAG_NETFLOW_CALLED	0
+#define MTAG_NETFLOW_V9		1
 
 /* Prototypes for netflow.c */
 int	ng_netflow_cache_init(priv_p);
 void	ng_netflow_cache_flush(priv_p);
 void	ng_netflow_copyinfo(priv_p, struct ng_netflow_info *);
 timeout_t ng_netflow_expire;
-int	ng_netflow_flow_add(priv_p, struct ip *, unsigned int src_if_index);
+int	ng_netflow_flow_add(priv_p, caddr_t ip_ptr, caddr_t upper_ptr, uint8_t upper_proto, uint8_t is_frag, unsigned int src_if_index);
 int	ng_netflow_flow_show(priv_p, uint32_t last, struct ng_mesg *);
-
+void	ng_netflow_switch_version(priv_p priv, int ver, int boot);
 #endif	/* _KERNEL */
 #endif	/* _NG_NETFLOW_H_ */
diff -urN sys/modules/netgraph/netflow/Makefile.orig sys/modules/netgraph/netflow/Makefile
--- sys/modules/netgraph/netflow/Makefile.orig	2009-09-06 16:31:18.000000000 +0400
+++ sys/modules/netgraph/netflow/Makefile	2009-09-06 16:33:32.000000000 +0400
@@ -3,9 +3,19 @@
 # Author: Gleb Smirnoff <[email protected]>
 #
 
+.include <bsd.own.mk>
+
 .PATH: ${.CURDIR}/../../../netgraph/netflow
 
 KMOD=   ng_netflow
-SRCS=   ng_netflow.c netflow.c
+SRCS=   ng_netflow.c netflow.c opt_inet6.h
+
+.if !defined(KERNBUILDDIR)
+
+.if ${MK_INET6_SUPPORT} != "no"
+opt_inet6.h:
+	echo "#define INET6 1" > ${.TARGET}
+.endif
+.endif
 
 .include <bsd.kmod.mk>
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.