Ethereal-dev: [Ethereal-dev] Beginnings of a NetFlow v9 dissector

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

From: Matthew Smart <smart@xxxxxxxxxx>
Date: Tue, 11 Feb 2003 17:02:01 -0500
Below is a patch to packet-netflow.c that supports partial decoding
of NetFlow version 9.  The main functionality is decoding template
fields.

Matt Smart <smart@xxxxxxxxxx>


Index: packet-netflow.c
===================================================================
RCS file: /cvsroot/ethereal/packet-netflow.c,v
retrieving revision 1.7
diff -u -r1.7 packet-netflow.c
--- packet-netflow.c	8 Oct 2002 19:26:35 -0000	1.7
+++ packet-netflow.c	11 Feb 2003 21:58:52 -0000
@@ -20,7 +20,8 @@
  ** Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
  *****************************************************************************
  **
- ** previous netflow dissector written by Matthew Smart <smart@xxxxxxxxxx>
+ ** Previous NetFlow dissector written by Matthew Smart <smart@xxxxxxxxxx>
+ ** NetFlow v9 support added by same.
  **
  *****************************************************************************
  **
@@ -105,7 +106,6 @@
 	{0, NULL}
 };
 
-
 /*
  * ethereal tree identifiers
  */
@@ -114,6 +114,7 @@
 static int      ett_netflow = -1;
 static int      ett_unixtime = -1;
 static int      ett_flow = -1;
+static int      ett_template = -1;
 
 /*
  * cflow header 
@@ -133,10 +134,23 @@
 static int      hf_cflow_sequence = -1;
 static int      hf_cflow_engine_type = -1;
 static int      hf_cflow_engine_id = -1;
+static int      hf_cflow_source_id = -1;
 
 static int      hf_cflow_aggmethod = -1;
 static int      hf_cflow_aggversion = -1;
 
+/* Version 9 */
+
+static int	hf_cflow_template_flowset_id = -1;
+static int	hf_cflow_data_flowset_id = -1;
+static int	hf_cflow_options_flowset_id = -1;
+static int	hf_cflow_flowset_id = -1;
+static int	hf_cflow_flowset_length = -1;
+static int	hf_cflow_template_id = -1;
+static int	hf_cflow_template_field_count = -1;
+static int	hf_cflow_template_field_type = -1;
+static int	hf_cflow_template_field_length = -1;
+
 /*
  * pdu storage
  */
@@ -172,6 +186,17 @@
 				  int offset, int verspec);
 static int      dissect_v8_flowpdu(proto_tree * pdutree, tvbuff_t * tvb,
 				   int offset, int verspec);
+static int	dissect_v9_pdu(proto_tree * pdutree, tvbuff_t * tvb,
+			       int offset, int verspec);
+#if 0
+static int	dissect_v9_data(proto_tree * pdutree, tvbuff_t * tvb,
+			       int offset);
+static int	dissect_v9_options(proto_tree * pdutree, tvbuff_t * tvb,
+			       int offset);
+#endif
+static int	dissect_v9_template(proto_tree * pdutree, tvbuff_t * tvb,
+			       int offset);
+static gchar   *v9_template_type_to_string(guint16 type);
 
 static gchar   *getprefix(const guint32 * address, int prefix);
 static void     dissect_netflow(tvbuff_t * tvb, packet_info * pinfo,
@@ -234,6 +259,10 @@
 		pdusize = -1;	/* deferred */
 		pduptr = &dissect_v8_aggpdu;
 		break;
+	case 9:
+		pdusize = -1;	/* deferred */
+		pduptr = &dissect_v9_pdu;
+		break;
 	default:
 		return;
 	}
@@ -254,9 +283,15 @@
 	/*
 	 * 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);
+	if (check_col(pinfo->cinfo, COL_INFO)) {
+		if (ver == 9) {
+			col_add_fstr(pinfo->cinfo, COL_INFO,
+			    "total: %u (v%u) FlowSets", pdus, ver);
+		} else {
+			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
@@ -280,14 +315,16 @@
 			    offset, 4, FALSE);
 	offset += 4;
 
-	proto_tree_add_item(timetree, hf_cflow_unix_nsecs, tvb,
-			    offset, 4, FALSE);
-	offset += 4;
+	if (ver != 9) {
+		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) {
+	if (ver == 5 || ver == 7 || ver == 8 || ver == 9) {
 		proto_tree_add_item(netflow_tree, hf_cflow_sequence,
 				    tvb, offset, 4, FALSE);
 		offset += 4;
@@ -297,6 +334,10 @@
 				    tvb, offset++, 1, FALSE);
 		proto_tree_add_item(netflow_tree, hf_cflow_engine_id,
 				    tvb, offset++, 1, FALSE);
+	} else if (ver == 9) {
+		proto_tree_add_item(netflow_tree, hf_cflow_source_id,
+				    tvb, offset, 4, FALSE);
+		offset += 4;
 	}
 	if (ver == 8) {
 		vspec = tvb_get_guint8(tvb, offset);
@@ -373,12 +414,21 @@
 		 * make sure we have a pdu's worth of data 
 		 */
 		available = tvb_length_remaining(tvb, offset);
+		if (ver == 9 && available >= 4) {
+			/* pdusize can be different for each v9 flowset */
+			pdusize = tvb_get_ntohs(tvb, offset + 2);
+		}
+
 		if (available < pdusize)
 			break;
 
-		pduitem =
-		    proto_tree_add_text(netflow_tree, tvb, offset, pdusize,
-					"pdu %u/%u", x, pdus);
+		if (ver == 9) {
+			pduitem = proto_tree_add_text(netflow_tree, tvb,
+			    offset, pdusize, "FlowSet %u/%u", x, pdus);
+		} else {
+			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);
@@ -391,8 +441,6 @@
 		else
 			break;
 	}
-
-	return;
 }
 
 /*
@@ -684,6 +732,170 @@
 	return (offset - startoffset);
 }
 
+/* Dissect a version 9 PDU and return the length of the PDU we processed. */
+
+static int
+dissect_v9_pdu(proto_tree * pdutree, tvbuff_t * tvb, int offset, int ver)
+{
+	guint16	flowset_id, length;
+
+	if (ver != 9)
+		return (0);
+
+	flowset_id = tvb_get_ntohs(tvb, offset);
+	if (flowset_id == 0) {
+		/* Template */
+		proto_tree_add_item(pdutree, hf_cflow_template_flowset_id, tvb,
+		    offset, 2, FALSE);
+		offset += 2;
+
+		length = tvb_get_ntohs(tvb, offset);
+		proto_tree_add_item(pdutree, hf_cflow_flowset_length, tvb,
+		    offset, 2, FALSE);
+		offset += 2;
+
+		dissect_v9_template(pdutree, tvb, offset);
+	} else if (flowset_id == 1) {
+		/* Options */
+		proto_tree_add_item(pdutree, hf_cflow_options_flowset_id, tvb,
+		    offset, 2, FALSE);
+		offset += 2;
+
+		length = tvb_get_ntohs(tvb, offset);
+		proto_tree_add_item(pdutree, hf_cflow_flowset_length, tvb,
+		    offset, 2, FALSE);
+		offset += 2;
+
+		/* dissect_v9_options(pdutree, tvb, offset); */
+	} else if (flowset_id >= 2 && flowset_id <= 255) {
+		/* Reserved */
+		proto_tree_add_item(pdutree, hf_cflow_flowset_id, tvb,
+		    offset, 2, FALSE);
+		offset += 2;
+
+		length = tvb_get_ntohs(tvb, offset);
+		proto_tree_add_item(pdutree, hf_cflow_flowset_length, tvb,
+		    offset, 2, FALSE);
+		offset += 2;
+	} else {
+		/* Data */
+		proto_tree_add_item(pdutree, hf_cflow_data_flowset_id, tvb,
+		    offset, 2, FALSE);
+		offset += 2;
+
+		length = tvb_get_ntohs(tvb, offset);
+		proto_tree_add_item(pdutree, hf_cflow_flowset_length, tvb,
+		    offset, 2, FALSE);
+		offset += 2;
+
+		/* dissect_v9_data(pdutree, tvb, offset) */
+	}
+
+	return (length);
+}
+
+#if 0
+static int
+dissect_v9_data(proto_tree * pdutree, tvbuff_t * tvb, int offset)
+{
+	return (0);
+}
+
+static int
+dissect_v9_options(proto_tree * pdutree, tvbuff_t * tvb, int offset)
+{
+	return (0);
+}
+#endif
+
+static int
+dissect_v9_template(proto_tree * pdutree, tvbuff_t * tvb, int offset)
+{
+	proto_tree *template_tree;
+	proto_item *template_item;
+	guint16 count;
+	gint32 i;
+
+	proto_tree_add_item(pdutree, hf_cflow_template_id, tvb,
+	    offset, 2, FALSE);
+	offset += 2;
+
+	count = tvb_get_ntohs(tvb, offset);
+	proto_tree_add_item(pdutree, hf_cflow_template_field_count, tvb,
+	    offset, 2, FALSE);
+	offset += 2;
+
+	for (i = 1; i <= count; i++) {
+		guint16 type, length;
+
+		type = tvb_get_ntohs(tvb, offset);
+		length = tvb_get_ntohs(tvb, offset + 2);
+
+		template_item = proto_tree_add_text(pdutree, tvb,
+		    offset, 4, "Field (%u/%u) %s", i, count,
+		    v9_template_type_to_string(type));
+		template_tree = proto_item_add_subtree(template_item, ett_template);
+
+		proto_tree_add_item(template_tree,
+		    hf_cflow_template_field_type, tvb, offset, 2, FALSE);
+		offset += 2;
+
+		proto_tree_add_item(template_tree,
+		    hf_cflow_template_field_length, tvb, offset, 2, FALSE);
+		offset += 2;
+	}
+
+	return (0);
+}
+
+struct _v9_template_type {
+	guint16	type;
+	gchar	*name;
+};
+
+static struct _v9_template_type v9_template_types[] = {
+	{ 1, "BYTES" },
+	{ 2, "PKTS" },
+	{ 3, "FLOWS" },
+	{ 4, "PROT" },
+	{ 5, "TOS" },
+	{ 6, "TCP_FLAGS" },
+	{ 7, "L4_SRC_PORT" },
+	{ 8, "IP_SRC_ADDR" },
+	{ 9, "SRC_MASK" },
+	{ 10, "INPUT_SNMP" },
+	{ 11, "L4_DST_PORT" },
+	{ 12, "IP_DST_ADDR" },
+	{ 13, "DST_MASK" },
+	{ 14, "OUTPUT_SNMP" },
+	{ 15, "IP_NEXT_HOP" },
+	{ 16, "SRC_AS" },
+	{ 17, "DST_AS" },
+	{ 18, "BGP_NEXT_HOP" },
+	{ 19, "MUL_DPKTS" },
+	{ 20, "MUL_DOCTETS" },
+	{ 21, "LAST_SWITCHED" },
+	{ 22, "FIRST_SWITCHED" },
+	{ 24, "OUT_PKTS" },
+	{ 40, "TOTAL_BYTES_EXP" },
+	{ 41, "TOTAL_PKTS_EXP" },
+	{ 42, "TOTAL_FLOWS_EXP" },
+	{ 0, "UNKNOWN" }
+};
+
+static gchar *
+v9_template_type_to_string(guint16 type)
+{
+	struct _v9_template_type *p;
+
+	for (p = v9_template_types; p->type != 0; p++) {
+		if (type == p->type)
+			break;
+	}
+
+	return (p->name);
+}
+
 /*
  * dissect a version 1, 5, or 7 pdu and return the length of the pdu we
  * processed
@@ -865,6 +1077,11 @@
 		  FT_UINT8, BASE_DEC, NULL, 0x0,
 		  "Slot number of switching engine", HFILL}
 		 },
+		{&hf_cflow_source_id,
+		 {"SourceId", "cflow.source_id",
+		  FT_UINT32, BASE_DEC, NULL, 0x0,
+		  "Identifier for export device", HFILL}
+		 },
 		{&hf_cflow_aggmethod,
 		 {"AggMethod", "cflow.aggmethod",
 		  FT_UINT8, BASE_DEC, VALS(v8_agg), 0x0,
@@ -879,6 +1096,54 @@
 		 * end version specific header storage 
 		 */
 		/*
+		 * Version 9
+		 */
+		{&hf_cflow_flowset_id,
+		 {"FlowSet Id", "cflow.flowset_id",
+		  FT_UINT16, BASE_DEC, NULL, 0x0,
+		  "FlowSet Id", HFILL}
+		 },
+		{&hf_cflow_data_flowset_id,
+		 {"Data FlowSet (Template Id)", "cflow.data_flowset_id",
+		  FT_UINT16, BASE_DEC, NULL, 0x0,
+		  "Data FlowSet with corresponding to a template Id", HFILL}
+		 },
+		{&hf_cflow_options_flowset_id,
+		 {"Options FlowSet", "cflow.options_flowset_id",
+		  FT_UINT16, BASE_DEC, NULL, 0x0,
+		  "Options FlowSet", HFILL}
+		 },
+		{&hf_cflow_template_flowset_id,
+		 {"Template FlowSet", "cflow.template_flowset_id",
+		  FT_UINT16, BASE_DEC, NULL, 0x0,
+		  "Template FlowSet", HFILL}
+		 },
+		{&hf_cflow_flowset_length,
+		 {"FlowSet Length", "cflow.flowset_length",
+		  FT_UINT16, BASE_DEC, NULL, 0x0,
+		  "FlowSet length", HFILL}
+		 },
+		{&hf_cflow_template_id,
+		 {"Template Id", "cflow.template_id",
+		  FT_UINT16, BASE_DEC, NULL, 0x0,
+		  "Template Id", HFILL}
+		 },
+		{&hf_cflow_template_field_count,
+		 {"Field Count", "cflow.template_field_count",
+		  FT_UINT16, BASE_DEC, NULL, 0x0,
+		  "Template field count", HFILL}
+		 },
+		{&hf_cflow_template_field_type,
+		 {"Type", "cflow.template_field_type",
+		  FT_UINT16, BASE_DEC, NULL, 0x0,
+		  "Template field type", HFILL}
+		 },
+		{&hf_cflow_template_field_length,
+		 {"Length", "cflow.template_field_length",
+		  FT_UINT16, BASE_DEC, NULL, 0x0,
+		  "Template field length", HFILL}
+		 },
+		/*
 		 * begin pdu content storage 
 		 */
 		{&hf_cflow_srcaddr,
@@ -1004,7 +1269,8 @@
 	static gint    *ett[] = {
 		&ett_netflow,
 		&ett_unixtime,
-		&ett_flow
+		&ett_flow,
+		&ett_template
 	};
 
 	proto_netflow = proto_register_protocol("Cisco NetFlow", "CFLOW",