Ethereal-dev: [Ethereal-dev] netflow dissector patch

Note: This archive is from the project's previous web site, ethereal.com. This list is no longer active.

From: Bill Fumerola <billf@xxxxxx>
Date: Fri, 20 Sep 2002 16:24:00 -0700
attached is the patch to replace most of the guts of the netflow dissector.

apologies for the fiasco/pain i've caused with this. where possible, i
kept the existing code the same wherever possible to minimize diff deltas.

i'd be interested (but don't require or anything) reviewing any v9 dissectors.

also, the only part of this dissector where i am unsure of the accuracy
is in the handling of timestamps/sysuptime. cisco & juniper seem to mix
and match some millisecond/second/microsecond. i have yet to be able to
determine who is right.

i'll have more changes coming (the sample rate info should only be
dissected if its non-zero, some other stuff), but also look forward to
other changes people have.

finally - i'm moved! beer is on me for any ethereal developers in san
francisco...

thanks,
-- 
- bill fumerola / fumerola@xxxxxxxxxxxxx / billf@xxxxxxxxxxx / billf@xxxxxx


Index: packet-netflow.c
===================================================================
RCS file: /cvsroot/ethereal/packet-netflow.c,v
retrieving revision 1.4
diff -u -r1.4 packet-netflow.c
--- packet-netflow.c	2002/09/09 20:22:51	1.4
+++ packet-netflow.c	2002/09/20 22:53:08
@@ -1,26 +1,40 @@
-/* packet-netflow.c
- * Routines for Cisco NetFlow packet disassembly
- * Matthew Smart <smart@xxxxxxxxxx>
- *
- * $Id: packet-netflow.c,v 1.4 2002/09/09 20:22:51 guy Exp $
- *
- * Ethereal - Network traffic analyzer
- * By Gerald Combs <gerald@xxxxxxxxxxxx>
- * Copyright 1998 Gerald Combs
- * 
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License
- * as published by the Free Software Foundation; either version 2
- * of the License, or (at your option) any later version.
- * 
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- * 
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+/*
+ ** packet-netflow.c
+ ** 
+ *****************************************************************************
+ ** (c) 2002 bill fumerola <fumerola@xxxxxxxxxxxxx>
+ ** All rights reserved.
+ ** 
+ ** This program is free software; you can redistribute it and/or
+ ** modify it under the terms of the GNU General Public License
+ ** as published by the Free Software Foundation; either version 2
+ ** of the License, or (at your option) any later version.
+ ** 
+ ** This program is distributed in the hope that it will be useful,
+ ** but WITHOUT ANY WARRANTY; without even the implied warranty of
+ ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ ** GNU General Public License for more details.
+ ** 
+ ** You should have received a copy of the GNU General Public License
+ ** along with this program; if not, write to the Free Software
+ ** Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *****************************************************************************
+ **
+ ** previous netflow dissector written by Matthew Smart <smart@xxxxxxxxxx>
+ **
+ *****************************************************************************
+ **
+ ** this code was written from the following documentation:
+ **
+ ** http://www.cisco.com/univercd/cc/td/doc/product/rtrmgmt/nfc/nfc_3_6/iug/format.pdf
+ ** http://www.caida.org/tools/measurement/cflowd/configuration/configuration-9.html
+ **
+ ** some documentation is more accurate then others. in some cases, live data and
+ ** information contained in responses from vendors were also used. some fields
+ ** are dissected as vendor specific fields.
+ **
+ ** $Yahoo: //depot/fumerola/packet-netflow/packet-netflow.c#14 $
+ ** $Id$
  */
 
 #ifdef HAVE_CONFIG_H
@@ -30,453 +44,987 @@
 #include <glib.h>
 #include <epan/packet.h>
 
-#include <stdio.h>
-#include <string.h>
+#define UDP_PORT_NETFLOW	2055
 
-#include "packet-netflow.h"
+/*
+ * pdu identifiers & sizes 
+ */
+
+#define V1PDU_SIZE		(4 * 12)
+#define V5PDU_SIZE		(4 * 12)
+#define V7PDU_SIZE		(4 * 13)
+#define V8PDU_AS_SIZE		(4 * 7)
+#define V8PDU_PROTO_SIZE	(4 * 7)
+#define V8PDU_SPREFIX_SIZE	(4 * 8)
+#define V8PDU_DPREFIX_SIZE	(4 * 8)
+#define V8PDU_MATRIX_SIZE	(4 * 10)
+#define V8PDU_DESTONLY_SIZE	(4 * 8)
+#define V8PDU_SRCDEST_SIZE	(4 * 10)
+#define V8PDU_FULL_SIZE		(4 * 11)
+#define V8PDU_TOSAS_SIZE	(V8PDU_AS_SIZE + 4)
+#define V8PDU_TOSPROTOPORT_SIZE	(V8PDU_PROTO_SIZE + 4)
+#define V8PDU_TOSSRCPREFIX_SIZE	V8PDU_SPREFIX_SIZE
+#define V8PDU_TOSDSTPREFIX_SIZE	V8PDU_DPREFIX_SIZE
+#define V8PDU_TOSMATRIX_SIZE	V8PDU_MATRIX_SIZE
+#define V8PDU_PREPORTPROTOCOL_SIZE (4 * 10)
+
+enum {
+	V8PDU_NO_METHOD = 0,
+	V8PDU_AS_METHOD,
+	V8PDU_PROTO_METHOD,
+	V8PDU_SPREFIX_METHOD,
+	V8PDU_DPREFIX_METHOD,
+	V8PDU_MATRIX_METHOD,
+	V8PDU_DESTONLY_METHOD,
+	V8PDU_SRCDEST_METHOD,
+	V8PDU_FULL_METHOD,
+	V8PDU_TOSAS_METHOD,
+	V8PDU_TOSPROTOPORT_METHOD,
+	V8PDU_TOSSRCPREFIX_METHOD,
+	V8PDU_TOSDSTPREFIX_METHOD,
+	V8PDU_TOSMATRIX_METHOD,
+	V8PDU_PREPORTPROTOCOL_METHOD
+};
+
+static const value_string v8_agg[] = {
+	{V8PDU_AS_METHOD, "V8 AS aggregation"},
+	{V8PDU_PROTO_METHOD, "V8 Proto/Port aggregation"},
+	{V8PDU_SPREFIX_METHOD, "V8 Source Prefix aggregation"},
+	{V8PDU_DPREFIX_METHOD, "V8 Destination Prefix aggregation"},
+	{V8PDU_MATRIX_METHOD, "V8 Network Matrix aggregation"},
+	{V8PDU_DESTONLY_METHOD, "V8 Destination aggregation (Cisco Catalyst)"},
+	{V8PDU_SRCDEST_METHOD, "V8 Src/Dest aggregation (Cisco Catalyst)"},
+	{V8PDU_FULL_METHOD, "V8 Full aggregation (Cisco Catalyst)"},
+	{V8PDU_TOSAS_METHOD, "V8 TOS+AS aggregation aggregation"},
+	{V8PDU_TOSPROTOPORT_METHOD, "V8 TOS+Protocol aggregation"},
+	{V8PDU_TOSSRCPREFIX_METHOD, "V8 TOS+Source Prefix aggregation"},
+	{V8PDU_TOSDSTPREFIX_METHOD, "V8 TOS+Destination Prefix aggregation"},
+	{V8PDU_TOSMATRIX_METHOD, "V8 TOS+Prefix Matrix aggregation"},
+	{V8PDU_PREPORTPROTOCOL_METHOD, "V8 Port+Protocol aggregation"},
+	{0, NULL}
+};
+
+
+/*
+ * ethereal tree identifiers
+ */
+
+static int      proto_netflow = -1;
+static int      ett_netflow = -1;
+static int      ett_unixtime = -1;
+static int      ett_flow = -1;
+
+/*
+ * cflow header 
+ */
+
+static int      hf_cflow_version = -1;
+static int      hf_cflow_count = -1;
+static int      hf_cflow_sysuptime = -1;
+static int      hf_cflow_unix_secs = -1;
+static int      hf_cflow_unix_nsecs = -1;
+static int      hf_cflow_timestamp = -1;
+static int      hf_cflow_samplerate = -1;
+
+/*
+ * cflow version specific info 
+ */
+static int      hf_cflow_sequence = -1;
+static int      hf_cflow_engine_type = -1;
+static int      hf_cflow_engine_id = -1;
+
+static int      hf_cflow_aggmethod = -1;
+static int      hf_cflow_aggversion = -1;
 
-static int proto_netflow = -1;
-static int hf_netflow_version = -1;
-static int hf_netflow_count = -1;
-static int hf_netflow_sys_uptime = -1;
-static int hf_netflow_unix_sec = -1;
-static int hf_netflow_unix_nsec = -1;
-static int hf_netflow_sequence = -1;
-static int hf_netflow_engine_type = -1;
-static int hf_netflow_engine_id = -1;
-static int hf_netflow_aggregation = -1;
-static int hf_netflow_agg_version = -1;
-static int hf_netflow_sample_rate = -1;
-static int hf_netflow_record = -1;
-
-static int hf_netflow_src_addr = -1;
-static int hf_netflow_dst_addr = -1;
-static int hf_netflow_next_hop = -1;
-static int hf_netflow_input_iface = -1;
-static int hf_netflow_output_iface = -1;
-static int hf_netflow_packets = -1;
-static int hf_netflow_bytes = -1;
-static int hf_netflow_start_time = -1;
-static int hf_netflow_end_time = -1;
-static int hf_netflow_src_port = -1;
-static int hf_netflow_dst_port = -1;
-static int hf_netflow_v7_flags = -1;
-static int hf_netflow_tcp_flags = -1;
-static int hf_netflow_ip_prot = -1;
-static int hf_netflow_tos = -1;
-static int hf_netflow_src_as = -1;
-static int hf_netflow_dst_as = -1;
-static int hf_netflow_src_mask = -1;
-static int hf_netflow_dst_mask = -1;
-static int hf_netflow_router_sc = -1;
+/*
+ * pdu storage
+ */
+static int      hf_cflow_srcaddr = -1;
+static int      hf_cflow_srcnet = -1;
+static int      hf_cflow_dstaddr = -1;
+static int      hf_cflow_dstnet = -1;
+static int      hf_cflow_nexthop = -1;
+static int      hf_cflow_inputint = -1;
+static int      hf_cflow_outputint = -1;
+static int      hf_cflow_flows = -1;
+static int      hf_cflow_packets = -1;
+static int      hf_cflow_octets = -1;
+static int      hf_cflow_timestart = -1;
+static int      hf_cflow_timeend = -1;
+static int      hf_cflow_srcport = -1;
+static int      hf_cflow_dstport = -1;
+static int      hf_cflow_prot = -1;
+static int      hf_cflow_tos = -1;
+static int      hf_cflow_flags = -1;
+static int      hf_cflow_tcpflags = -1;
+static int      hf_cflow_dstas = -1;
+static int      hf_cflow_srcas = -1;
+static int      hf_cflow_dstmask = -1;
+static int      hf_cflow_srcmask = -1;
+static int      hf_cflow_routersc = -1;
+
+typedef int     dissect_pdu_t(proto_tree * pdutree, tvbuff_t * tvb, int offset,
+			      int verspec);
+static int      dissect_pdu(proto_tree * tree, tvbuff_t * tvb, int offset,
+			    int verspec);
+static int      dissect_v8_aggpdu(proto_tree * pdutree, tvbuff_t * tvb,
+				  int offset, int verspec);
+static int      dissect_v8_flowpdu(proto_tree * pdutree, tvbuff_t * tvb,
+				   int offset, int verspec);
+
+static gchar   *getprefix(const guint32 * address, int prefix);
+static void     dissect_netflow(tvbuff_t * tvb, packet_info * pinfo,
+				proto_tree * tree);
+
+static int      flow_process_ints(proto_tree * pdutree, tvbuff_t * tvb,
+				  int offset);
+static int      flow_process_ports(proto_tree * pdutree, tvbuff_t * tvb,
+				   int offset);
+static int      flow_process_timeperiod(proto_tree * pdutree, tvbuff_t * tvb,
+					int offset);
+static int      flow_process_aspair(proto_tree * pdutree, tvbuff_t * tvb,
+				    int offset);
+static int      flow_process_sizecount(proto_tree * pdutree, tvbuff_t * tvb,
+				       int offset);
+static int      flow_process_textfield(proto_tree * pdutree, tvbuff_t * tvb,
+				       int offset, int bytes,
+				       const char *text);
 
-static gint ett_netflow = -1;
-static gint ett_netflow_rec = -1;
 
 static void
-dissect_netflow_157(tvbuff_t *tvb, proto_tree *tree, guint16 version,
-    guint offset)
+dissect_netflow(tvbuff_t * tvb, packet_info * pinfo, proto_tree * tree)
 {
-	guint32 addr;
+	proto_tree     *netflow_tree = NULL;
+	proto_tree     *ti;
+	proto_item     *timeitem, *pduitem;
+	proto_tree     *timetree, *pdutree;
+	unsigned int    pduret, ver = 0, pdus = 0, x = 1, vspec;
+	size_t          available, pdusize, offset = 0;
+	nstime_t        ts;
+	dissect_pdu_t  *pduptr;
+
+	if (check_col(pinfo->cinfo, COL_PROTOCOL))
+		col_set_str(pinfo->cinfo, COL_PROTOCOL, "CFLOW");
+	if (check_col(pinfo->cinfo, COL_INFO))
+		col_clear(pinfo->cinfo, COL_INFO);
+
+	if (tree) {
+		ti = proto_tree_add_item(tree, proto_netflow, tvb,
+					 offset, -1, FALSE);
+		netflow_tree = proto_item_add_subtree(ti, ett_netflow);
+	}
+
+	ver = tvb_get_ntohs(tvb, offset);
+	vspec = ver;
+	switch (ver) {
+	case 1:
+		pdusize = V1PDU_SIZE;
+		pduptr = &dissect_pdu;
+		break;
+	case 5:
+		pdusize = V5PDU_SIZE;
+		pduptr = &dissect_pdu;
+		break;
+	case 7:
+		pdusize = V7PDU_SIZE;
+		pduptr = &dissect_pdu;
+		break;
+	case 8:
+		pdusize = -1;	/* deferred */
+		pduptr = &dissect_v8_aggpdu;
+		break;
+	default:
+		return;
+	}
+
+	if (tree)
+		proto_tree_add_uint(netflow_tree, hf_cflow_version, tvb,
+				    offset, 2, ver);
+	offset += 2;
+
+	pdus = tvb_get_ntohs(tvb, offset);
+	if (pdus <= 0)
+		return;
+	if (tree)
+		proto_tree_add_uint(netflow_tree, hf_cflow_count, tvb,
+				    offset, 2, pdus);
+	offset += 2;
+
+	/*
+	 * set something interesting in the display now that we have info 
+	 */
+	if (check_col(pinfo->cinfo, COL_INFO))
+		col_add_fstr(pinfo->cinfo, COL_INFO, "total: %u (v%u) flows",
+			     pdus, ver);
+
+	/*
+	 * the rest is only interesting if we're displaying/searching the
+	 * packet 
+	 */
+	if (!tree)
+		return;
 
-	tvb_memcpy(tvb, (guint8 *)&addr, offset, 4);
-	proto_tree_add_ipv4(tree, hf_netflow_src_addr, tvb, offset, 4, addr);
+	proto_tree_add_item(netflow_tree, hf_cflow_sysuptime, tvb,
+			    offset, 4, FALSE);
 	offset += 4;
+
+	ts.secs = tvb_get_ntohl(tvb, offset);
+	ts.nsecs = tvb_get_ntohl(tvb, offset + 4);
+	timeitem = proto_tree_add_time(netflow_tree,
+				       hf_cflow_timestamp, tvb, offset,
+				       8, &ts);
+	timetree = proto_item_add_subtree(timeitem, ett_unixtime);
 
-	tvb_memcpy(tvb, (guint8 *)&addr, offset, 4);
-	proto_tree_add_ipv4(tree, hf_netflow_dst_addr, tvb, offset, 4, addr);
+	proto_tree_add_item(timetree, hf_cflow_unix_secs, tvb,
+			    offset, 4, FALSE);
 	offset += 4;
 
-	tvb_memcpy(tvb, (guint8 *)&addr, offset, 4);
-	proto_tree_add_ipv4(tree, hf_netflow_next_hop, tvb, offset, 4, addr);
+	proto_tree_add_item(timetree, hf_cflow_unix_nsecs, tvb,
+			    offset, 4, FALSE);
 	offset += 4;
+
+	/*
+	 * version specific header 
+	 */
+	if (ver == 5 || ver == 7 || ver == 8) {
+		proto_tree_add_item(netflow_tree, hf_cflow_sequence,
+				    tvb, offset, 4, FALSE);
+		offset += 4;
+	}
+	if (ver == 5 || ver == 8) {
+		proto_tree_add_item(netflow_tree, hf_cflow_engine_type,
+				    tvb, offset++, 1, FALSE);
+		proto_tree_add_item(netflow_tree, hf_cflow_engine_id,
+				    tvb, offset++, 1, FALSE);
+	}
+	if (ver == 8) {
+		vspec = tvb_get_guint8(tvb, offset);
+		switch (vspec) {
+		case V8PDU_AS_METHOD:
+			pdusize = V8PDU_AS_SIZE;
+			break;
+		case V8PDU_PROTO_METHOD:
+			pdusize = V8PDU_PROTO_SIZE;
+			break;
+		case V8PDU_SPREFIX_METHOD:
+			pdusize = V8PDU_SPREFIX_SIZE;
+			break;
+		case V8PDU_DPREFIX_METHOD:
+			pdusize = V8PDU_DPREFIX_SIZE;
+			break;
+		case V8PDU_MATRIX_METHOD:
+			pdusize = V8PDU_MATRIX_SIZE;
+			break;
+		case V8PDU_DESTONLY_METHOD:
+			pdusize = V8PDU_DESTONLY_SIZE;
+			pduptr = &dissect_v8_flowpdu;
+			break;
+		case V8PDU_SRCDEST_METHOD:
+			pdusize = V8PDU_SRCDEST_SIZE;
+			pduptr = &dissect_v8_flowpdu;
+			break;
+		case V8PDU_FULL_METHOD:
+			pdusize = V8PDU_FULL_SIZE;
+			pduptr = &dissect_v8_flowpdu;
+			break;
+		case V8PDU_TOSAS_METHOD:
+			pdusize = V8PDU_TOSAS_SIZE;
+			break;
+		case V8PDU_TOSPROTOPORT_METHOD:
+			pdusize = V8PDU_TOSPROTOPORT_SIZE;
+			break;
+		case V8PDU_TOSSRCPREFIX_METHOD:
+			pdusize = V8PDU_TOSSRCPREFIX_SIZE;
+			break;
+		case V8PDU_TOSDSTPREFIX_METHOD:
+			pdusize = V8PDU_TOSDSTPREFIX_SIZE;
+			break;
+		case V8PDU_TOSMATRIX_METHOD:
+			pdusize = V8PDU_TOSMATRIX_SIZE;
+			break;
+		case V8PDU_PREPORTPROTOCOL_METHOD:
+			pdusize = V8PDU_PREPORTPROTOCOL_SIZE;
+			break;
+		default:
+			pdusize = -1;
+			vspec = 0;
+			break;
+		}
+		proto_tree_add_uint(netflow_tree, hf_cflow_aggmethod,
+				    tvb, offset++, 1, vspec);
+		proto_tree_add_item(netflow_tree, hf_cflow_aggversion,
+				    tvb, offset++, 1, FALSE);
+	}
+	if (ver == 7 || ver == 8)
+		offset = flow_process_textfield(netflow_tree, tvb, offset, 4,
+						"reserved");
+	else if (ver == 5) {
+		proto_tree_add_item(netflow_tree, hf_cflow_samplerate,
+				    tvb, offset, 2, FALSE);
+		offset += 2;
+	}
+
+	/*
+	 * everything below here should be payload 
+	 */
+	for (x = 1; x < pdus + 1; x++) {
+		/*
+		 * make sure we have a pdu's worth of data 
+		 */
+		available = tvb_length_remaining(tvb, offset);
+		if (available < pdusize)
+			break;
+
+		pduitem =
+		    proto_tree_add_text(netflow_tree, tvb, offset, pdusize,
+					"pdu %u/%u", x, pdus);
+		pdutree = proto_item_add_subtree(pduitem, ett_flow);
+
+		pduret = pduptr(pdutree, tvb, offset, vspec);
+
+		/*
+		 * if we came up short, stop processing 
+		 */
+		if (pduret == pdusize)
+			offset += pduret;
+		else
+			break;
+	}
+
+	return;
+}
+
+/*
+ * flow_process_* == common groups of fields, probably could be inline 
+ */
 
-	proto_tree_add_item(tree, hf_netflow_input_iface,
-	    tvb, offset, 2, FALSE);
+static int
+flow_process_ints(proto_tree * pdutree, tvbuff_t * tvb, int offset)
+{
+	proto_tree_add_item(pdutree, hf_cflow_inputint, tvb, offset, 2, FALSE);
 	offset += 2;
 
-	proto_tree_add_item(tree, hf_netflow_output_iface,
-	    tvb, offset, 2, FALSE);
+	proto_tree_add_item(pdutree, hf_cflow_outputint, tvb, offset, 2,
+			    FALSE);
 	offset += 2;
 
-	proto_tree_add_item(tree, hf_netflow_packets,
-	    tvb, offset, 4, FALSE);
-	offset += 4;
+	return offset;
+}
 
-	proto_tree_add_item(tree, hf_netflow_bytes,
-	    tvb, offset, 4, FALSE);
-	offset += 4;
+static int
+flow_process_ports(proto_tree * pdutree, tvbuff_t * tvb, int offset)
+{
+	proto_tree_add_item(pdutree, hf_cflow_srcport, tvb, offset, 2, FALSE);
+	offset += 2;
+
+	proto_tree_add_item(pdutree, hf_cflow_dstport, tvb, offset, 2, FALSE);
+	offset += 2;
+
+	return offset;
+}
 
-	proto_tree_add_item(tree, hf_netflow_start_time,
-	    tvb, offset, 4, FALSE);
+static int
+flow_process_timeperiod(proto_tree * pdutree, tvbuff_t * tvb, int offset)
+{
+	nstime_t        ts;
+
+	ts.secs = tvb_get_ntohl(tvb, offset) / 1000;
+	ts.nsecs = ((tvb_get_ntohl(tvb, offset) % 1000) * 1000000);
+	proto_tree_add_time(pdutree, hf_cflow_timestart, tvb, offset, 4, &ts);
 	offset += 4;
 
-	proto_tree_add_item(tree, hf_netflow_end_time,
-	    tvb, offset, 4, FALSE);
+	ts.secs = tvb_get_ntohl(tvb, offset) / 1000;
+	ts.nsecs = ((tvb_get_ntohl(tvb, offset) % 1000) * 1000000);
+	proto_tree_add_time(pdutree, hf_cflow_timeend, tvb, offset, 4, &ts);
 	offset += 4;
+
+	return offset;
+}
+
 
-	proto_tree_add_item(tree, hf_netflow_src_port,
-	    tvb, offset, 2, FALSE);
+static int
+flow_process_aspair(proto_tree * pdutree, tvbuff_t * tvb, int offset)
+{
+	proto_tree_add_item(pdutree, hf_cflow_srcas, tvb, offset, 2, FALSE);
 	offset += 2;
 
-	proto_tree_add_item(tree, hf_netflow_dst_port,
-	    tvb, offset, 2, FALSE);
+	proto_tree_add_item(pdutree, hf_cflow_dstas, tvb, offset, 2, FALSE);
 	offset += 2;
 
-	if (version == 1) {
-		offset += 2;	/* Skip pad bytes */
+	return offset;
+}
 
-		proto_tree_add_item(tree, hf_netflow_ip_prot,
-		    tvb, offset, 1, FALSE);
-		offset += 1;
-
-		proto_tree_add_item(tree, hf_netflow_tos,
-		    tvb, offset, 1, FALSE);
-		offset += 1;
-
-		proto_tree_add_item(tree, hf_netflow_tcp_flags,
-		    tvb, offset, 1, FALSE);
-		offset += 1;
-	} else {
-		if (version == 7) {
-			proto_tree_add_item(tree, hf_netflow_v7_flags,
-			    tvb, offset, 1, FALSE);
-		}
-		offset += 1;	/* v5 pad byte, v7 flags */
+static int
+flow_process_sizecount(proto_tree * pdutree, tvbuff_t * tvb, int offset)
+{
+	proto_tree_add_item(pdutree, hf_cflow_packets, tvb, offset, 4, FALSE);
+	offset += 4;
 
-		proto_tree_add_item(tree, hf_netflow_tcp_flags,
-		    tvb, offset, 1, FALSE);
-		offset += 1;
-
-		proto_tree_add_item(tree, hf_netflow_ip_prot,
-		    tvb, offset, 1, FALSE);
-		offset += 1;
-
-		proto_tree_add_item(tree, hf_netflow_tos,
-		    tvb, offset, 1, FALSE);
-		offset += 1;
+	proto_tree_add_item(pdutree, hf_cflow_octets, tvb, offset, 4, FALSE);
+	offset += 4;
 
-		proto_tree_add_item(tree, hf_netflow_src_as,
-		    tvb, offset, 2, FALSE);
-		offset += 2;
+	return offset;
+}
 
-		proto_tree_add_item(tree, hf_netflow_dst_as,
-		    tvb, offset, 2, FALSE);
-		offset += 2;
+static int
+flow_process_textfield(proto_tree * pdutree, tvbuff_t * tvb, int offset,
+		       int bytes, const char *text)
+{
+	proto_tree_add_text(pdutree, tvb, offset, bytes, text);
+	offset += bytes;
 
-		proto_tree_add_item(tree, hf_netflow_src_mask,
-		    tvb, offset, 1, FALSE);
-		offset += 1;
-
-		proto_tree_add_item(tree, hf_netflow_dst_mask,
-		    tvb, offset, 1, FALSE);
-		offset += 1;
-
-		offset += 2;	/* Skip pad bytes */
-
-		if (version == 7) {
-			proto_tree_add_item(tree, hf_netflow_router_sc,
-			    tvb, offset, 4, FALSE);
-			offset += 4;
-		}
-	}
+	return offset;
 }
 
-static void 
-dissect_netflow(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree)
+static int
+dissect_v8_flowpdu(proto_tree * pdutree, tvbuff_t * tvb, int offset,
+		   int verspec)
 {
-	proto_tree *netflow_tree = NULL;
-	proto_tree *netflow_rec_tree = NULL;
-	proto_item *ti = NULL, *tf = NULL; 
-	gint offset = 0;
-	guint16 nf_version, nf_count, nf_sample_rate;
-	guint32 nf_sequence;
-	gint header_size, record_size;
-	int i;
+	int             startoffset = offset;
 
-	if (check_col(pinfo->cinfo, COL_PROTOCOL))
-		col_set_str(pinfo->cinfo, COL_PROTOCOL, "NetFlow");
-	if (check_col(pinfo->cinfo, COL_INFO))
-		col_clear(pinfo->cinfo, COL_INFO);
+	proto_tree_add_item(pdutree, hf_cflow_dstaddr, tvb, offset, 4, FALSE);
+	offset += 4;
+
+	if (verspec != V8PDU_DESTONLY_METHOD) {
+		proto_tree_add_item(pdutree, hf_cflow_srcaddr, tvb, offset, 4,
+				    FALSE);
+		offset += 4;
+	}
+	if (verspec == V8PDU_FULL_METHOD) {
+		proto_tree_add_item(pdutree, hf_cflow_dstport, tvb, offset, 2,
+				    FALSE);
+		offset += 2;
+		proto_tree_add_item(pdutree, hf_cflow_srcport, tvb, offset, 2,
+				    FALSE);
+		offset += 2;
+	}
 
-	/* Determine NetFlow version and number of records */
-	nf_version = tvb_get_ntohs(tvb, offset);
-	offset += sizeof(nf_version);
+	offset = flow_process_sizecount(pdutree, tvb, offset);
+	offset = flow_process_timeperiod(pdutree, tvb, offset);
 
-	nf_count = tvb_get_ntohs(tvb, offset);
-	offset += sizeof(nf_count);
+	proto_tree_add_item(pdutree, hf_cflow_outputint, tvb, offset, 2,
+			    FALSE);
+	offset += 2;
 
-	if (check_col(pinfo->cinfo, COL_INFO))
-		col_add_fstr(pinfo->cinfo, COL_INFO,
-		    "v%u, %u records", nf_version, nf_count);
+	if (verspec != V8PDU_DESTONLY_METHOD) {
+		proto_tree_add_item(pdutree, hf_cflow_inputint, tvb, offset, 2,
+				    FALSE);
+		offset += 2;
+	}
 
-	/* Handle version-specific issues */
-	switch (nf_version) {
-	case 1:
-		header_size = NETFLOW_V1_HDR;
-		record_size = NETFLOW_V1_REC;
+	proto_tree_add_item(pdutree, hf_cflow_tos, tvb, offset++, 1, FALSE);
+	if (verspec == V8PDU_FULL_METHOD)
+		proto_tree_add_item(pdutree, hf_cflow_prot, tvb, offset++, 1,
+				    FALSE);
+	offset = flow_process_textfield(pdutree, tvb, offset, 1, "marked tos");
+
+	if (verspec == V8PDU_SRCDEST_METHOD)
+		offset =
+		    flow_process_textfield(pdutree, tvb, offset, 2,
+					   "reserved");
+	else if (verspec == V8PDU_FULL_METHOD)
+		offset =
+		    flow_process_textfield(pdutree, tvb, offset, 1, "padding");
+
+	offset =
+	    flow_process_textfield(pdutree, tvb, offset, 4, "extra packets");
+
+	proto_tree_add_item(pdutree, hf_cflow_routersc, tvb, offset, 4, FALSE);
+	offset += 4;
+
+	return (offset - startoffset);
+}
+
+/*
+ * dissect a version 8 pdu, returning the length of the pdu processed 
+ */
+
+static int
+dissect_v8_aggpdu(proto_tree * pdutree, tvbuff_t * tvb, int offset,
+		  int verspec)
+{
+	int             startoffset = offset;
+
+	proto_tree_add_item(pdutree, hf_cflow_flows, tvb, offset, 4, FALSE);
+	offset += 4;
+
+	offset = flow_process_sizecount(pdutree, tvb, offset);
+	offset = flow_process_timeperiod(pdutree, tvb, offset);
+
+	switch (verspec) {
+	case V8PDU_AS_METHOD:
+	case V8PDU_TOSAS_METHOD:
+		offset = flow_process_aspair(pdutree, tvb, offset);
+
+		if (verspec == V8PDU_TOSAS_METHOD) {
+			proto_tree_add_item(pdutree, hf_cflow_tos, tvb,
+					    offset++, 1, FALSE);
+			offset =
+			    flow_process_textfield(pdutree, tvb, offset, 1,
+						   "padding");
+			offset =
+			    flow_process_textfield(pdutree, tvb, offset, 2,
+						   "reserved");
+		}
 		break;
-	case 5:
-		header_size = NETFLOW_V5_HDR;
-		record_size = NETFLOW_V5_REC;
+	case V8PDU_PROTO_METHOD:
+	case V8PDU_TOSPROTOPORT_METHOD:
+		proto_tree_add_item(pdutree, hf_cflow_prot, tvb, offset++, 1,
+				    FALSE);
+
+		if (verspec == V8PDU_PROTO_METHOD)
+			offset =
+			    flow_process_textfield(pdutree, tvb, offset, 1,
+						   "padding");
+		else if (verspec == V8PDU_TOSPROTOPORT_METHOD)
+			proto_tree_add_item(pdutree, hf_cflow_tos, tvb,
+					    offset++, 1, FALSE);
+
+		offset =
+		    flow_process_textfield(pdutree, tvb, offset, 2,
+					   "reserved");
+		offset = flow_process_ports(pdutree, tvb, offset);
+
+		if (verspec == V8PDU_TOSPROTOPORT_METHOD)
+			offset = flow_process_ints(pdutree, tvb, offset);
 		break;
-	case 7:
-		header_size = NETFLOW_V7_HDR;
-		record_size = NETFLOW_V7_REC;
+	case V8PDU_SPREFIX_METHOD:
+	case V8PDU_DPREFIX_METHOD:
+	case V8PDU_TOSSRCPREFIX_METHOD:
+	case V8PDU_TOSDSTPREFIX_METHOD:
+		proto_tree_add_item(pdutree,
+				    verspec ==
+				    V8PDU_SPREFIX_METHOD ?
+				    hf_cflow_srcnet : hf_cflow_dstnet, tvb,
+				    offset, 4, FALSE);
+		offset += 4;
+
+		proto_tree_add_item(pdutree,
+				    verspec ==
+				    V8PDU_SPREFIX_METHOD ?
+				    hf_cflow_srcmask : hf_cflow_dstmask, tvb,
+				    offset++, 1, FALSE);
+
+		if (verspec == V8PDU_SPREFIX_METHOD
+		    || verspec == V8PDU_DPREFIX_METHOD)
+			offset =
+			    flow_process_textfield(pdutree, tvb, offset, 1,
+						   "padding");
+		else if (verspec == V8PDU_TOSSRCPREFIX_METHOD
+			 || verspec == V8PDU_TOSDSTPREFIX_METHOD)
+			proto_tree_add_item(pdutree, hf_cflow_tos, tvb,
+					    offset++, 1, FALSE);
+
+		proto_tree_add_item(pdutree,
+				    verspec ==
+				    V8PDU_SPREFIX_METHOD ? hf_cflow_srcas
+				    : hf_cflow_dstas, tvb, offset, 2, FALSE);
+		offset += 2;
+
+		proto_tree_add_item(pdutree,
+				    verspec ==
+				    V8PDU_SPREFIX_METHOD ?
+				    hf_cflow_inputint : hf_cflow_outputint,
+				    tvb, offset, 2, FALSE);
+		offset += 2;
+
+		offset =
+		    flow_process_textfield(pdutree, tvb, offset, 2,
+					   "reserved");
 		break;
-	case 8:
-		header_size = NETFLOW_V8_HDR;
-		record_size = NETFLOW_V8_REC;
-	case 9:
-	default:
-		return;
-	}
+	case V8PDU_MATRIX_METHOD:
+	case V8PDU_TOSMATRIX_METHOD:
+	case V8PDU_PREPORTPROTOCOL_METHOD:
+		proto_tree_add_item(pdutree, hf_cflow_srcnet, tvb, offset, 4,
+				    FALSE);
+		offset += 4;
+
+		proto_tree_add_item(pdutree, hf_cflow_dstnet, tvb, offset, 4,
+				    FALSE);
+		offset += 4;
 
-	/* Add NetFlow to the tree */
-	if (tree != NULL) {
-		ti = proto_tree_add_protocol_format(tree, proto_netflow, tvb,
-		    0, header_size, "NetFlow, v%u, %u records",
-		    nf_version, nf_count);
-		netflow_tree = proto_item_add_subtree(ti, ett_netflow);
-	} else {
-		return;
+		proto_tree_add_item(pdutree, hf_cflow_srcmask, tvb, offset++,
+				    1, FALSE);
+
+		proto_tree_add_item(pdutree, hf_cflow_dstmask, tvb, offset++,
+				    1, FALSE);
+
+		if (verspec == V8PDU_TOSMATRIX_METHOD ||
+		    verspec == V8PDU_PREPORTPROTOCOL_METHOD) {
+			proto_tree_add_item(pdutree, hf_cflow_tos, tvb,
+					    offset++, 1, FALSE);
+			if (verspec == V8PDU_TOSMATRIX_METHOD) {
+				offset =
+				    flow_process_textfield(pdutree, tvb,
+							   offset, 1,
+							   "padding");
+			} else if (verspec == V8PDU_PREPORTPROTOCOL_METHOD) {
+				proto_tree_add_item(pdutree, hf_cflow_prot,
+						    tvb, offset++, 1, FALSE);
+			}
+		} else {
+			offset =
+			    flow_process_textfield(pdutree, tvb, offset, 2,
+						   "reserved");
+		}
+
+		if (verspec == V8PDU_MATRIX_METHOD
+		    || verspec == V8PDU_TOSMATRIX_METHOD) {
+			offset = flow_process_aspair(pdutree, tvb, offset);
+		} else if (verspec == V8PDU_PREPORTPROTOCOL_METHOD) {
+			offset = flow_process_ports(pdutree, tvb, offset);
+		}
+
+		offset = flow_process_ints(pdutree, tvb, offset);
+		break;
 	}
 
-	/* Start adding header information */
-	offset = 0;
 
-	proto_tree_add_uint(netflow_tree, hf_netflow_version,
-	    tvb, offset, sizeof(nf_version), nf_version);
-	offset += sizeof(nf_version);
+	return (offset - startoffset);
+}
 
-	proto_tree_add_uint(netflow_tree, hf_netflow_count,
-	    tvb, offset, sizeof(nf_count), nf_count);
-	offset += sizeof(nf_count);
+/*
+ * dissect a version 1, 5, or 7 pdu and return the length of the pdu we
+ * processed
+ */
 
-	proto_tree_add_item(netflow_tree, hf_netflow_sys_uptime,
-	    tvb, offset, 4, FALSE);
+static int
+dissect_pdu(proto_tree * pdutree, tvbuff_t * tvb, int offset, int ver)
+{
+	int             startoffset = offset;
+	guint32         srcaddr, dstaddr;
+	guint8          mask;
+	nstime_t        ts;
+
+	memset(&ts, '\0', sizeof(ts));
+
+	/*
+	 * memcpy so we can use the values later to calculate a prefix 
+	 */
+	tvb_memcpy(tvb, (guint8 *) & srcaddr, offset, 4);
+	proto_tree_add_ipv4(pdutree, hf_cflow_srcaddr, tvb, offset, 4,
+			    srcaddr);
 	offset += 4;
 
-	proto_tree_add_item(netflow_tree, hf_netflow_unix_sec,
-	    tvb, offset, 4, FALSE);
+	tvb_memcpy(tvb, (guint8 *) & dstaddr, offset, 4);
+	proto_tree_add_ipv4(pdutree, hf_cflow_dstaddr, tvb, offset, 4,
+			    dstaddr);
 	offset += 4;
 
-	proto_tree_add_item(netflow_tree, hf_netflow_unix_nsec,
-	    tvb, offset, 4, FALSE);
+	proto_tree_add_item(pdutree, hf_cflow_nexthop, tvb, offset, 4, FALSE);
 	offset += 4;
-
-	/* No more version 1 header */
 
-	if (nf_version != 1) {
-		nf_sequence = tvb_get_ntohl(tvb, offset);
-		proto_tree_add_uint(netflow_tree, hf_netflow_sequence,
-		    tvb, offset, sizeof(nf_sequence), nf_sequence);
-		offset += sizeof(nf_sequence);
+	offset = flow_process_ints(pdutree, tvb, offset);
+	offset = flow_process_sizecount(pdutree, tvb, offset);
+	offset = flow_process_timeperiod(pdutree, tvb, offset);
+	offset = flow_process_ports(pdutree, tvb, offset);
+
+	/*
+	 * and the similarities end here 
+	 */
+	if (ver == 1) {
+		offset =
+		    flow_process_textfield(pdutree, tvb, offset, 2, "padding");
 
-		/* Add the sequence number */
-		if (check_col(pinfo->cinfo, COL_INFO)) {
-			col_clear(pinfo->cinfo, COL_INFO);
-			col_add_fstr(pinfo->cinfo, COL_INFO,
-			    "v%u, %u records, sequence # %u",
-			    nf_version, nf_count, nf_sequence);
-		}
+		proto_tree_add_item(pdutree, hf_cflow_prot, tvb, offset++, 1,
+				    FALSE);
 
-		/* No more version 7 header */
+		proto_tree_add_item(pdutree, hf_cflow_tos, tvb, offset++, 1,
+				    FALSE);
 
-		if (nf_version != 7) {
-			/* Engine type and ID */
-			proto_tree_add_item(netflow_tree,
-			    hf_netflow_engine_type, tvb, offset,
-			    1, FALSE);
-			offset += 1;
-
-			proto_tree_add_item(netflow_tree,
-			    hf_netflow_engine_id, tvb, offset,
-			    1, FALSE);
-			offset += 1;
-
-			if (nf_version == 8) {
-				/* Engine type and ID */
-				proto_tree_add_item(netflow_tree,
-				    hf_netflow_aggregation, tvb, offset,
+		proto_tree_add_item(pdutree, hf_cflow_tcpflags, tvb, offset++,
 				    1, FALSE);
-				offset += 1;
+
+		offset =
+		    flow_process_textfield(pdutree, tvb, offset, 3, "padding");
+
+		offset =
+		    flow_process_textfield(pdutree, tvb, offset, 4,
+					   "reserved");
+	} else {
+		if (ver == 5)
+			offset =
+			    flow_process_textfield(pdutree, tvb, offset, 1,
+						   "padding");
+		else {
+			proto_tree_add_item(pdutree, hf_cflow_flags, tvb,
+					    offset++, 1, FALSE);
+		}
 
-				proto_tree_add_item(netflow_tree,
-				    hf_netflow_agg_version, tvb, offset,
+		proto_tree_add_item(pdutree, hf_cflow_tcpflags, tvb, offset++,
 				    1, FALSE);
-				offset += 1;
-			}
 
-			/*
-			 * On high-speed interfaces often just
-			 * statistical sample records are produced.
-			 */
-			nf_sample_rate = tvb_get_ntohs(tvb, offset);
-			if (nf_version == 5) {
-				/*
-				 * Sample rate.  Junipers and some Ciscos
-				 * include sampling rate in the reserved
-				 * header field.  Not all the bits are used,
-				 * however.
-				 */
-				if ((nf_sample_rate & 0xc000) == 0x4000) {
-					nf_sample_rate &= 0x3fff;
-					if (nf_sample_rate == 0)
-						nf_sample_rate = 1;
-				} else
-					nf_sample_rate = 1;
-			}
-			proto_tree_add_uint_format(netflow_tree,
-			    hf_netflow_sample_rate, tvb, offset,
-			    sizeof(nf_sample_rate), nf_sample_rate,
-			    "Sample_rate: 1/%u", nf_sample_rate);
-			offset += sizeof(nf_sample_rate);
+		proto_tree_add_item(pdutree, hf_cflow_prot, tvb, offset++, 1,
+				    FALSE);
+
+		proto_tree_add_item(pdutree, hf_cflow_tos, tvb, offset++, 1,
+				    FALSE);
+
+		offset = flow_process_aspair(pdutree, tvb, offset);
+
+		mask = tvb_get_guint8(tvb, offset);
+		proto_tree_add_text(pdutree, tvb, offset, 1,
+				    "SrcMask: %u (prefix: %s/%u)",
+				    mask, getprefix(&srcaddr, mask),
+				    mask != 0 ? mask : 32);
+		proto_tree_add_uint_hidden(pdutree, hf_cflow_srcmask, tvb,
+					   offset++, 1, mask);
+
+		mask = tvb_get_guint8(tvb, offset);
+		proto_tree_add_text(pdutree, tvb, offset, 1,
+				    "DstMask: %u (prefix: %s/%u)",
+				    mask, getprefix(&dstaddr, mask),
+				    mask != 0 ? mask : 32);
+		proto_tree_add_uint_hidden(pdutree, hf_cflow_dstmask, tvb,
+					   offset++, 1, mask);
+
+		offset =
+		    flow_process_textfield(pdutree, tvb, offset, 2, "padding");
+
+		if (ver == 7) {
+			proto_tree_add_item(pdutree, hf_cflow_routersc, tvb,
+					    offset, 4, FALSE);
+			offset += 4;
 		}
 	}
 
-	/* XXX Doesn't support v8 records, yet */
-	if (nf_version == 8)
-		return;
+	return (offset - startoffset);
+}
 
-	/* Handle the flow records */
-	for (i = 0; i < nf_count; i++) {
-		guint rec_offset = header_size + i * record_size;
-
-		tf = proto_tree_add_uint_format(netflow_tree,
-		    hf_netflow_record, tvb, rec_offset, record_size,
-		    i, "Record %d: %u packets, %u bytes", i + 1,
-		    tvb_get_ntohl(tvb, rec_offset + 16),
-		    tvb_get_ntohl(tvb, rec_offset + 20));
-		netflow_rec_tree = proto_item_add_subtree(tf,
-		    ett_netflow_rec);
+static gchar   *
+getprefix(const guint32 * address, int prefix)
+{
+	guint32         gprefix;
 
-		dissect_netflow_157(tvb, netflow_rec_tree,
-		    nf_version, rec_offset);
-	}
+	gprefix = *address & htonl((0xffffffff << (32 - prefix)));
+
+	return (ip_to_str(&gprefix));
 }
 
 void
 proto_register_netflow(void)
 {
 	static hf_register_info hf[] = {
-		/* Header */
-		{ &hf_netflow_version,
-		{ "Version", "netflow.version", FT_UINT16,
-		  BASE_DEC, NULL, 0x0, "", HFILL }},
-		{ &hf_netflow_count,
-		{ "Number of records", "netflow.count", FT_UINT16,
-		  BASE_DEC, NULL, 0x0, "", HFILL }},
-		{ &hf_netflow_sys_uptime,
-		{ "System uptime", "netflow.sys_uptime", FT_UINT32,
-		  BASE_DEC, NULL, 0x0, "", HFILL }},
-		{ &hf_netflow_unix_sec,
-		{ "Unix seconds", "netflow.unix_sec", FT_UINT32,
-		  BASE_DEC, NULL, 0x0, "", HFILL }},
-		{ &hf_netflow_unix_nsec,
-		{ "Unix nanonseconds", "netflow.unix_nsec", FT_UINT32,
-		  BASE_DEC, NULL, 0x0, "", HFILL }},
-		{ &hf_netflow_sequence,
-		{ "Sequence number", "netflow.sequence", FT_UINT32,
-		  BASE_DEC, NULL, 0x0, "", HFILL }},
-		{ &hf_netflow_engine_type,
-		{ "Engine type", "netflow.engine_type", FT_UINT8,
-		  BASE_DEC, NULL, 0x0, "", HFILL }},
-		{ &hf_netflow_engine_id,
-		{ "Engine ID", "netflow.engine_id", FT_UINT8,
-		  BASE_DEC, NULL, 0x0, "", HFILL }},
-		{ &hf_netflow_aggregation,
-		{ "Aggregation method", "netflow.aggregation", FT_UINT8,
-		  BASE_DEC, NULL, 0x0, "", HFILL }},
-		{ &hf_netflow_agg_version,
-		{ "Aggregation version", "netflow.agg_version", FT_UINT8,
-		  BASE_DEC, NULL, 0x0, "", HFILL }},
-		{ &hf_netflow_sample_rate,
-		{ "Sample rate", "netflow.sample_rate", FT_UINT16,
-		  BASE_DEC, NULL, 0x0, "", HFILL }},
-		{ &hf_netflow_record,
-		{ "Record", "netflow.record", FT_UINT32,
-		  BASE_DEC, NULL, 0x0, "", HFILL }},
-		/* Record */
-		{ &hf_netflow_src_addr,
-		{ "Source address", "netflow.src_addr", FT_IPv4,
-		  BASE_NONE, NULL, 0x0, "", HFILL }},
-		{ &hf_netflow_dst_addr,
-		{ "Destination address", "netflow.dst_addr", FT_IPv4,
-		  BASE_NONE, NULL, 0x0, "", HFILL }},
-		{ &hf_netflow_next_hop,
-		{ "Next hop", "netflow.next_hop", FT_IPv4,
-		  BASE_NONE, NULL, 0x0, "", HFILL }},
-		{ &hf_netflow_input_iface,
-		{ "Input interface", "netflow.input_iface", FT_UINT16,
-		  BASE_DEC, NULL, 0x0, "", HFILL }},
-		{ &hf_netflow_output_iface,
-		{ "Output interface", "netflow.output_iface", FT_UINT16,
-		  BASE_DEC, NULL, 0x0, "", HFILL }},
-		{ &hf_netflow_packets,
-		{ "Packets sent", "netflow.packets", FT_UINT32,
-		  BASE_DEC, NULL, 0x0, "", HFILL }},
-		{ &hf_netflow_bytes,
-		{ "Bytes sent", "netflow.bytes", FT_UINT32,
-		  BASE_DEC, NULL, 0x0, "", HFILL }},
-		{ &hf_netflow_start_time,
-		{ "Start time", "netflow.start_time", FT_UINT32,
-		  BASE_DEC, NULL, 0x0, "", HFILL }},
-		{ &hf_netflow_end_time,
-		{ "End time", "netflow.end_time", FT_UINT32,
-		  BASE_DEC, NULL, 0x0, "", HFILL }},
-		{ &hf_netflow_src_port,
-		{ "Source port", "netflow.src_port", FT_UINT16,
-		  BASE_DEC, NULL, 0x0, "", HFILL }},
-		{ &hf_netflow_dst_port,
-		{ "Destination port", "netflow.dst_port", FT_UINT16,
-		  BASE_DEC, NULL, 0x0, "", HFILL }},
-		{ &hf_netflow_v7_flags,
-		{ "Valid flags", "netflow.flags", FT_UINT8,
-		  BASE_HEX, NULL, 0x0, "", HFILL }},
-		{ &hf_netflow_tcp_flags,
-		{ "TCP flags", "netflow.tcp_flags", FT_UINT8,
-		  BASE_HEX, NULL, 0x0, "", HFILL }},
-		{ &hf_netflow_ip_prot,
-		{ "IP protocol", "netflow.ip_prot", FT_UINT8,
-		  BASE_DEC, NULL, 0x0, "", HFILL }},
-		{ &hf_netflow_tos,
-		{ "Type of service", "netflow.tos", FT_UINT8,
-		  BASE_HEX, NULL, 0x0, "", HFILL }},
-		{ &hf_netflow_src_as,
-		{ "Source AS", "netflow.src_as", FT_UINT16,
-		  BASE_DEC, NULL, 0x0, "", HFILL }},
-		{ &hf_netflow_dst_as,
-		{ "Destination AS", "netflow.dst_as", FT_UINT16,
-		  BASE_DEC, NULL, 0x0, "", HFILL }},
-		{ &hf_netflow_src_mask,
-		{ "Source mask", "netflow.src_mask", FT_UINT8,
-		  BASE_DEC, NULL, 0x0, "", HFILL }},
-		{ &hf_netflow_dst_mask,
-		{ "Destination mask", "netflow.dst_mask", FT_UINT8,
-		  BASE_DEC, NULL, 0x0, "", HFILL }},
-		{ &hf_netflow_router_sc,
-		{ "Router bypass", "netflow.router_sc", FT_IPv4,
-		  BASE_NONE, NULL, 0x0, "", HFILL }},
+		/*
+		 * flow header 
+		 */
+		{&hf_cflow_version,
+		 {"Version", "cflow.version",
+		  FT_UINT16, BASE_DEC, NULL, 0x0,
+		  "NetFlow Version", HFILL}
+		 },
+		{&hf_cflow_count,
+		 {"Count", "cflow.count",
+		  FT_UINT16, BASE_DEC, NULL, 0x0,
+		  "Count of PDUs", HFILL}
+		 },
+		{&hf_cflow_sysuptime,
+		 {"SysUptime", "cflow.sysuptime",
+		  FT_UINT32, BASE_DEC, NULL, 0x0,
+		  "Time since router booted (in milliseconds)", HFILL}
+		 },
+
+		{&hf_cflow_timestamp,
+		 {"Timestamp", "cflow.timestamp",
+		  FT_ABSOLUTE_TIME, BASE_NONE, NULL, 0x0,
+		  "Current seconds since epoch", HFILL}
+		 },
+		{&hf_cflow_unix_secs,
+		 {"CurrentSecs", "cflow.unix_secs",
+		  FT_UINT32, BASE_DEC, NULL, 0x0,
+		  "Current seconds since epoch", HFILL}
+		 },
+		{&hf_cflow_unix_nsecs,
+		 {"CurrentNSecs", "cflow.unix_nsecs",
+		  FT_UINT32, BASE_DEC, NULL, 0x0,
+		  "Residual nanoseconds since epoch", HFILL}
+		 },
+		{&hf_cflow_samplerate,
+		 {"SampleRate", "cflow.samplerate",
+		  FT_UINT16, BASE_DEC, NULL, 0x0,
+		  "Sample Frequency of exporter", HFILL}
+		 },
+
+		/*
+		 * end version-agnostic header
+		 * version-specific flow header 
+		 */
+		{&hf_cflow_sequence,
+		 {"FlowSequence", "cflow.sequence",
+		  FT_UINT32, BASE_DEC, NULL, 0x0,
+		  "Sequence number of flows seen", HFILL}
+		 },
+		{&hf_cflow_engine_type,
+		 {"EngineType", "cflow.engine_type",
+		  FT_UINT8, BASE_DEC, NULL, 0x0,
+		  "Flow switching engine type", HFILL}
+		 },
+		{&hf_cflow_engine_id,
+		 {"EngineId", "cflow.engine_id",
+		  FT_UINT8, BASE_DEC, NULL, 0x0,
+		  "Slot number of switching engine", HFILL}
+		 },
+		{&hf_cflow_aggmethod,
+		 {"AggMethod", "cflow.aggmethod",
+		  FT_UINT8, BASE_DEC, VALS(v8_agg), 0x0,
+		  "CFlow V8 Aggregation Method", HFILL}
+		 },
+		{&hf_cflow_aggversion,
+		 {"AggVersion", "cflow.aggversion",
+		  FT_UINT8, BASE_DEC, NULL, 0x0,
+		  "CFlow V8 Aggregation Version", HFILL}
+		 },
+		/*
+		 * end version specific header storage 
+		 */
+		/*
+		 * begin pdu content storage 
+		 */
+		{&hf_cflow_srcaddr,
+		 {"SrcAddr", "cflow.srcaddr",
+		  FT_IPv4, BASE_NONE, NULL, 0x0,
+		  "Flow Source Address", HFILL}
+		 },
+		{&hf_cflow_srcnet,
+		 {"SrcNet", "cflow.srcnet",
+		  FT_IPv4, BASE_NONE, NULL, 0x0,
+		  "Flow Source Network", HFILL}
+		 },
+		{&hf_cflow_dstaddr,
+		 {"DstAddr", "cflow.dstaddr",
+		  FT_IPv4, BASE_NONE, NULL, 0x0,
+		  "Flow Destination Address", HFILL}
+		 },
+		{&hf_cflow_dstnet,
+		 {"DstNet", "cflow.dstaddr",
+		  FT_IPv4, BASE_NONE, NULL, 0x0,
+		  "Flow Destination Network", HFILL}
+		 },
+		{&hf_cflow_nexthop,
+		 {"NextHop", "cflow.nexthop",
+		  FT_IPv4, BASE_NONE, NULL, 0x0,
+		  "Router nexthop", HFILL}
+		 },
+		{&hf_cflow_inputint,
+		 {"InputInt", "cflow.inputint",
+		  FT_UINT16, BASE_DEC, NULL, 0x0,
+		  "Flow Input Interface", HFILL}
+		 },
+		{&hf_cflow_outputint,
+		 {"OutputInt", "cflow.outputint",
+		  FT_UINT16, BASE_DEC, NULL, 0x0,
+		  "Flow Output Interface", HFILL}
+		 },
+		{&hf_cflow_flows,
+		 {"Flows", "cflow.flows",
+		  FT_UINT32, BASE_DEC, NULL, 0x0,
+		  "Flows Aggregated in PDU", HFILL}
+		 },
+		{&hf_cflow_packets,
+		 {"Packets", "cflow.packets",
+		  FT_UINT32, BASE_DEC, NULL, 0x0,
+		  "Count of packets", HFILL}
+		 },
+		{&hf_cflow_octets,
+		 {"Octets", "cflow.octets",
+		  FT_UINT32, BASE_DEC, NULL, 0x0,
+		  "Count of bytes", HFILL}
+		 },
+		{&hf_cflow_timestart,
+		 {"StartTime", "cflow.timestart",
+		  FT_RELATIVE_TIME, BASE_NONE, NULL, 0x0,
+		  "Uptime at start of flow", HFILL}
+		 },
+		{&hf_cflow_timeend,
+		 {"EndTime", "cflow.timeend",
+		  FT_RELATIVE_TIME, BASE_NONE, NULL, 0x0,
+		  "Uptime at end of flow", HFILL}
+		 },
+		{&hf_cflow_srcport,
+		 {"SrcPort", "cflow.srcport",
+		  FT_UINT16, BASE_DEC, NULL, 0x0,
+		  "Flow Source Port", HFILL}
+		 },
+		{&hf_cflow_dstport,
+		 {"DstPort", "cflow.dstport",
+		  FT_UINT16, BASE_DEC, NULL, 0x0,
+		  "Flow Destination Port", HFILL}
+		 },
+		{&hf_cflow_prot,
+		 {"Protocol", "cflow.protocol",
+		  FT_UINT8, BASE_DEC, NULL, 0x0,
+		  "IP Protocol", HFILL}
+		 },
+		{&hf_cflow_tos,
+		 {"IP ToS", "cflow.tos",
+		  FT_UINT8, BASE_HEX, NULL, 0x0,
+		  "IP Type of Service", HFILL}
+		 },
+		{&hf_cflow_flags,
+		 {"Export Flags", "cflow.flags",
+		  FT_UINT8, BASE_HEX, NULL, 0x0,
+		  "CFlow Flags", HFILL}
+		 },
+		{&hf_cflow_tcpflags,
+		 {"TCP Flags", "cflow.tcpflags",
+		  FT_UINT8, BASE_HEX, NULL, 0x0,
+		  "TCP Flags", HFILL}
+		 },
+		{&hf_cflow_srcas,
+		 {"SrcAS", "cflow.srcas",
+		  FT_UINT16, BASE_DEC, NULL, 0x0,
+		  "Source AS", HFILL}
+		 },
+		{&hf_cflow_dstas,
+		 {"DstAS", "cflow.dstas",
+		  FT_UINT16, BASE_DEC, NULL, 0x0,
+		  "Destination AS", HFILL}
+		 },
+		{&hf_cflow_srcmask,
+		 {"SrcMask", "cflow.srcmask",
+		  FT_UINT8, BASE_DEC, NULL, 0x0,
+		  "Source Prefix Mask", HFILL}
+		 },
+		{&hf_cflow_dstmask,
+		 {"DstMask", "cflow.dstmask",
+		  FT_UINT8, BASE_DEC, NULL, 0x0,
+		  "Destination Prefix Mask", HFILL}
+		 },
+		{&hf_cflow_routersc,
+		 {"Router Shortcut", "cflow.routersc",
+		  FT_IPv4, BASE_NONE, NULL, 0x0,
+		  "Router shortcut by switch", HFILL}
+		 }
+		/*
+		 * end pdu content storage 
+		 */
 	};
 
-	static gint *ett[] = {
+	static gint    *ett[] = {
 		&ett_netflow,
-		&ett_netflow_rec
+		&ett_unixtime,
+		&ett_flow
 	};
+
+	proto_netflow = proto_register_protocol("Cisco NetFlow", "CFLOW",
+						"cflow");
 
-	proto_netflow = proto_register_protocol("NetFlow",
-	    "NetFlow", "netflow");
 	proto_register_field_array(proto_netflow, hf, array_length(hf));
 	proto_register_subtree_array(ett, array_length(ett));
+
+	register_dissector("cflow", dissect_netflow, proto_netflow);
 }
 
+
+/*
+ * protocol/port association 
+ */
 void
 proto_reg_handoff_netflow(void)
 {
 	dissector_handle_t netflow_handle;
 
 	netflow_handle = create_dissector_handle(dissect_netflow,
-	    proto_netflow);
+						 proto_netflow);
 	dissector_add("udp.port", UDP_PORT_NETFLOW, netflow_handle);
 }