Skip site navigation (1)Skip section navigation (2)
Date:      Sun, 06 Sep 2009 23:46:26 +0400
From:      "Alexander V. Chernikov" <melifaro@ipfw.ru>
To:        freebsd-net@freebsd.org
Cc:        freebsd-isp@freebsd.org
Subject:   Call for testers: ng_netflow with v9 and IPv6 support
Message-ID:  <4AA41192.6080708@ipfw.ru>

next in thread | raw e-mail | index | archive | help
This is a multi-part message in MIME format.
--------------010607050501030003050405
Content-Type: text/plain; charset=ISO-8859-1; format=flowed
Content-Transfer-Encoding: 7bit

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



--------------010607050501030003050405
Content-Type: text/plain;
 name="netflow_v9_20090906_curr.diff"
Content-Transfer-Encoding: 7bit
Content-Disposition: inline;
 filename="netflow_v9_20090906_curr.diff"

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 <glebius@freebsd.org>
 #
 
+.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>

--------------010607050501030003050405
Content-Type: text/plain;
 name="netflow_v9_20090906_72.diff"
Content-Transfer-Encoding: 7bit
Content-Disposition: inline;
 filename="netflow_v9_20090906_72.diff"

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 <glebius@freebsd.org>
 #
 
+.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>

--------------010607050501030003050405--



Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?4AA41192.6080708>