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",
- Follow-Ups:
- Re: [Ethereal-dev] Beginnings of a NetFlow v9 dissector
- From: Guy Harris
- Re: [Ethereal-dev] Beginnings of a NetFlow v9 dissector
- Prev by Date: RE: [Ethereal-dev] Prevent generation of register.c
- Next by Date: [Ethereal-dev] Patch to packet-wbxml.c and packet-wbxml.h
- Previous by thread: RE: [Ethereal-dev] Prevent generation of register.c
- Next by thread: Re: [Ethereal-dev] Beginnings of a NetFlow v9 dissector
- Index(es):





