Huge thanks to our Platinum Members Endace and LiveAction,
and our Silver Member Veeam, for supporting the Wireshark Foundation and project.

Ethereal-dev: [Ethereal-dev] Improved 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: Mon, 3 Mar 2003 17:06:00 -0500
Below is a patch to packet-netflow.c to add the following:

	1. Make NetFlow UDP port user configurable

	2. Cache v9 templates

	3. Decode v9 data

The template caching is not perfect (maybe I should look at
glib's hash table), but effective for small packet captures.

Matthew Smart <smart@xxxxxxxxxx>


Index: packet-netflow.c
===================================================================
RCS file: /cvsroot/ethereal/packet-netflow.c,v
retrieving revision 1.8
diff -u -r1.8 packet-netflow.c
--- packet-netflow.c	12 Feb 2003 08:36:48 -0000	1.8
+++ packet-netflow.c	3 Mar 2003 22:01:33 -0000
@@ -46,8 +46,13 @@
 #include <epan/packet.h>
 #include <string.h>
 
+#include "prefs.h"
+
 #define UDP_PORT_NETFLOW	2055
 
+static guint global_netflow_udp_port = UDP_PORT_NETFLOW;
+static guint netflow_udp_port = 0;
+
 /*
  * pdu identifiers & sizes 
  */
@@ -106,6 +111,26 @@
 	{0, NULL}
 };
 
+/* Version 9 template cache structures */
+#define V9TEMPLATE_MAX_ENTRIES		64
+#define V9TEMPLATE_CACHE_MAX_ENTRIES	100
+
+struct v9_template_entry {
+	guint16	type;
+	guint16	length;
+};
+
+struct v9_template {
+	guint16	id;
+	guint16	count;
+	guint32	length;
+	guint32 source_id;
+	guint32	source_addr;
+	struct v9_template_entry entries[V9TEMPLATE_MAX_ENTRIES];
+};
+
+static struct v9_template v9_template_cache[V9TEMPLATE_CACHE_MAX_ENTRIES];
+
 /*
  * ethereal tree identifiers
  */
@@ -115,6 +140,7 @@
 static int      ett_unixtime = -1;
 static int      ett_flow = -1;
 static int      ett_template = -1;
+static int      ett_dataflowset = -1;
 
 /*
  * cflow header 
@@ -155,15 +181,23 @@
  * pdu storage
  */
 static int      hf_cflow_srcaddr = -1;
+static int      hf_cflow_srcaddr_v6 = -1;
 static int      hf_cflow_srcnet = -1;
 static int      hf_cflow_dstaddr = -1;
+static int      hf_cflow_dstaddr_v6 = -1;
 static int      hf_cflow_dstnet = -1;
 static int      hf_cflow_nexthop = -1;
+static int      hf_cflow_nexthop_v6 = -1;
+static int      hf_cflow_bgpnexthop = -1;
+static int      hf_cflow_bgpnexthop_v6 = -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_packets64 = -1;
+static int      hf_cflow_packetsout = -1;
 static int      hf_cflow_octets = -1;
+static int      hf_cflow_octets64 = -1;
 static int      hf_cflow_timestart = -1;
 static int      hf_cflow_timeend = -1;
 static int      hf_cflow_srcport = -1;
@@ -177,6 +211,13 @@
 static int      hf_cflow_dstmask = -1;
 static int      hf_cflow_srcmask = -1;
 static int      hf_cflow_routersc = -1;
+static int      hf_cflow_mulpackets = -1;
+static int      hf_cflow_muloctets = -1;
+static int      hf_cflow_octets_exp = -1;
+static int      hf_cflow_packets_exp = -1;
+static int      hf_cflow_flows_exp = -1;
+
+void		proto_reg_handoff_netflow(void);
 
 typedef int     dissect_pdu_t(proto_tree * pdutree, tvbuff_t * tvb, int offset,
 			      int verspec);
@@ -186,17 +227,21 @@
 				  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_flowset(proto_tree * pdutree, tvbuff_t * tvb,
+				   int offset, int verspec);
 static int	dissect_v9_data(proto_tree * pdutree, tvbuff_t * tvb,
-			       int offset);
+			       int offset, guint16 id, int length);
+static void	dissect_v9_pdu(proto_tree * pdutree, tvbuff_t * tvb,
+			       int offset, struct v9_template * template);
+#if 0
 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);
+				    int offset);
+static void	v9_template_add(struct v9_template * template);
+static struct v9_template *v9_template_get(guint16 id, guint32 src_addr,
+					   guint32 src_id);
 
 static gchar   *getprefix(const guint32 * address, int prefix);
 static void     dissect_netflow(tvbuff_t * tvb, packet_info * pinfo,
@@ -261,7 +306,7 @@
 		break;
 	case 9:
 		pdusize = -1;	/* deferred */
-		pduptr = &dissect_v9_pdu;
+		pduptr = &dissect_v9_flowset;
 		break;
 	default:
 		return;
@@ -732,12 +777,13 @@
 	return (offset - startoffset);
 }
 
-/* Dissect a version 9 PDU and return the length of the PDU we processed. */
+/* Dissect a version 9 FlowSet and return the length we processed. */
 
 static int
-dissect_v9_pdu(proto_tree * pdutree, tvbuff_t * tvb, int offset, int ver)
+dissect_v9_flowset(proto_tree * pdutree, tvbuff_t * tvb, int offset, int ver)
 {
-	guint16	flowset_id, length;
+	int length;
+	guint16	flowset_id;
 
 	if (ver != 9)
 		return (0);
@@ -788,19 +834,280 @@
 		    offset, 2, FALSE);
 		offset += 2;
 
-		/* dissect_v9_data(pdutree, tvb, offset) */
+		dissect_v9_data(pdutree, tvb, offset, flowset_id, length);
 	}
 
 	return (length);
 }
 
-#if 0
 static int
-dissect_v9_data(proto_tree * pdutree, tvbuff_t * tvb, int offset)
+dissect_v9_data(proto_tree * pdutree, tvbuff_t * tvb, int offset,
+    guint16 id, int length)
 {
+	struct v9_template *template;
+	proto_tree *data_tree;
+	proto_item *data_item;
+
+	template = v9_template_get(id, 0, 0);
+	if (template != NULL && template->length > 0) {
+		int count;
+
+		count = 1;
+		while (length > 0) {
+			int available_length;
+
+			available_length = tvb_length(tvb) - offset;
+			if (available_length < template->length) {
+				break;
+			}
+
+			data_item = proto_tree_add_text(pdutree, tvb,
+			    offset, template->length, "pdu %d", count++);
+			data_tree = proto_item_add_subtree(data_item,
+			    ett_dataflowset);
+
+			dissect_v9_pdu(data_tree, tvb, offset, template);
+
+			offset += template->length;
+			length -= template->length;
+		}
+	}
+
 	return (0);
 }
 
+static void
+dissect_v9_pdu(proto_tree * pdutree, tvbuff_t * tvb, int offset,
+    struct v9_template * template)
+{
+	int i;
+
+	for (i = 0; i < template->count; i++) {
+		guint32	ipv4addr;
+		guint8	ipv6addr[16];
+		guint16 type, length;
+		nstime_t ts;
+
+		type = template->entries[i].type;
+		length = template->entries[i].length;
+
+		switch (type) {
+		case 1: /* bytes */
+			if (length == 4) {
+				proto_tree_add_item(pdutree, hf_cflow_octets,
+				    tvb, offset, length, FALSE);
+			} else if (length == 8) {
+				proto_tree_add_item(pdutree, hf_cflow_octets64,
+				    tvb, offset, length, FALSE);
+			} else {
+				proto_tree_add_text(pdutree,
+				    tvb, offset, length,
+				    "Octets: length %u", length);
+			}
+			break;
+
+		case 2: /* packets */
+			if (length == 4) {
+				proto_tree_add_item(pdutree, hf_cflow_packets,
+				    tvb, offset, length, FALSE);
+			} else if (length == 8) {
+				proto_tree_add_item(pdutree, hf_cflow_packets64,
+				    tvb, offset, length, FALSE);
+			} else {
+				proto_tree_add_text(pdutree,
+				    tvb, offset, length,
+				    "Packets: length %u", length);
+			}
+			break;
+
+		case 3: /* flows */
+			if (length == 4) {
+				proto_tree_add_item(pdutree, hf_cflow_flows,
+				    tvb, offset, length, FALSE);
+			} else {
+				proto_tree_add_text(pdutree,
+				    tvb, offset, length,
+				    "Flows: length %u", length);
+			}
+			break;
+
+		case 4: /* proto */
+			proto_tree_add_item(pdutree, hf_cflow_prot,
+			    tvb, offset, length, FALSE);
+			break;
+
+		case 5: /* TOS */
+			proto_tree_add_item(pdutree, hf_cflow_tos,
+			    tvb, offset, length, FALSE);
+			break;
+
+		case 6: /* TCP flags */
+			proto_tree_add_item(pdutree, hf_cflow_tcpflags,
+			    tvb, offset, length, FALSE);
+			break;
+
+		case 7: /* source port */
+			proto_tree_add_item(pdutree, hf_cflow_srcport,
+			    tvb, offset, length, FALSE);
+			break;
+
+		case 8: /* source IP */
+			if (length == 4) {
+				tvb_memcpy(tvb, (guint8 *)&ipv4addr, offset,
+				    sizeof(ipv4addr));
+				proto_tree_add_ipv4(pdutree, hf_cflow_srcaddr,
+				    tvb, offset, length, ipv4addr);
+			} else if (length == 16) {
+				tvb_memcpy(tvb, ipv6addr, offset,
+				    sizeof(ipv6addr));
+				proto_tree_add_ipv6(pdutree, hf_cflow_srcaddr_v6,
+				    tvb, offset, length, ipv6addr);
+			} else {
+				proto_tree_add_text(pdutree,
+				    tvb, offset, length,
+				    "SrcAddr: length %u", length);
+			}
+			break;
+
+		case 9: /* source mask */
+			proto_tree_add_item(pdutree, hf_cflow_srcmask,
+			    tvb, offset, length, FALSE);
+			break;
+
+		case 10: /* input SNMP */
+			proto_tree_add_item(pdutree, hf_cflow_inputint,
+			    tvb, offset, length, FALSE);
+			break;
+
+		case 11: /* dest port */
+			proto_tree_add_item(pdutree, hf_cflow_dstport,
+			    tvb, offset, length, FALSE);
+			break;
+
+		case 12: /* dest IP */
+			if (length == 4) {
+				tvb_memcpy(tvb, (guint8 *)&ipv4addr, offset,
+				    sizeof(ipv4addr));
+				proto_tree_add_ipv4(pdutree, hf_cflow_dstaddr,
+				    tvb, offset, length, ipv4addr);
+			} else if (length == 16) {
+				tvb_memcpy(tvb, ipv6addr, offset,
+				    sizeof(ipv6addr));
+				proto_tree_add_ipv6(pdutree, hf_cflow_dstaddr_v6,
+				    tvb, offset, length, ipv6addr);
+			} else {
+				proto_tree_add_text(pdutree,
+				    tvb, offset, length,
+				    "DstAddr: length %u", length);
+			}
+			break;
+
+		case 13: /* dest mask */
+			proto_tree_add_item(pdutree, hf_cflow_dstmask,
+			    tvb, offset, length, FALSE);
+			break;
+
+		case 14: /* output SNMP */
+			proto_tree_add_item(pdutree, hf_cflow_outputint,
+			    tvb, offset, length, FALSE);
+			break;
+
+		case 15: /* nexthop IP */
+			if (length == 4) {
+				tvb_memcpy(tvb, (guint8 *)&ipv4addr, offset,
+				    sizeof(ipv4addr));
+				proto_tree_add_ipv4(pdutree, hf_cflow_nexthop,
+				    tvb, offset, length, ipv4addr);
+			} else if (length == 16) {
+				tvb_memcpy(tvb, ipv6addr, offset,
+				    sizeof(ipv6addr));
+				proto_tree_add_ipv6(pdutree, hf_cflow_nexthop_v6,
+				    tvb, offset, length, ipv6addr);
+			} else {
+				proto_tree_add_text(pdutree,
+				    tvb, offset, length,
+				    "NextHop: length %u", length);
+			}
+			break;
+
+		case 16: /* source AS */
+			proto_tree_add_item(pdutree, hf_cflow_srcas,
+			    tvb, offset, length, FALSE);
+			break;
+
+		case 17: /* dest AS */
+			proto_tree_add_item(pdutree, hf_cflow_dstas,
+			    tvb, offset, length, FALSE);
+			break;
+
+		case 18: /* BGP nexthop IP */
+			if (length == 4) {
+				tvb_memcpy(tvb, (guint8 *)&ipv4addr, offset,
+				    sizeof(ipv4addr));
+				proto_tree_add_ipv4(pdutree, hf_cflow_bgpnexthop,
+				    tvb, offset, length, ipv4addr);
+			} else if (length == 16) {
+				tvb_memcpy(tvb, ipv6addr, offset,
+				    sizeof(ipv6addr));
+				proto_tree_add_ipv6(pdutree, hf_cflow_bgpnexthop_v6,
+				    tvb, offset, length, ipv6addr);
+			} else {
+				proto_tree_add_text(pdutree,
+				    tvb, offset, length,
+				    "BGPNextHop: length %u", length);
+			}
+			break;
+
+		case 19: /* multicast packets */
+			proto_tree_add_item(pdutree, hf_cflow_mulpackets,
+			    tvb, offset, length, FALSE);
+			break;
+
+		case 20: /* multicast octets */
+			proto_tree_add_item(pdutree, hf_cflow_muloctets,
+			    tvb, offset, length, FALSE);
+			break;
+
+		case 21: /* last switched */
+			ts.secs = tvb_get_ntohl(tvb, offset) / 1000;
+			ts.nsecs = 0;
+			proto_tree_add_time(pdutree, hf_cflow_timeend,
+			    tvb, offset, length, &ts);
+			break;
+
+		case 22: /* first switched */
+			ts.secs = tvb_get_ntohl(tvb, offset) / 1000;
+			ts.nsecs = 0;
+			proto_tree_add_time(pdutree, hf_cflow_timestart,
+			    tvb, offset, length, &ts);
+			break;
+
+		case 40: /* bytes exported */
+			proto_tree_add_item(pdutree, hf_cflow_octets_exp,
+			    tvb, offset, length, FALSE);
+			break;
+
+		case 41: /* packets exported */
+			proto_tree_add_item(pdutree, hf_cflow_packets_exp,
+			    tvb, offset, length, FALSE);
+			break;
+
+		case 42: /* flows exported */
+			proto_tree_add_item(pdutree, hf_cflow_flows_exp,
+			    tvb, offset, length, FALSE);
+			break;
+
+		default:
+			proto_tree_add_text(pdutree, tvb, offset, length,
+			    "Type %u", type);
+			break;
+		}
+
+		offset += length;
+	}
+}
+
+#if 0
 static int
 dissect_v9_options(proto_tree * pdutree, tvbuff_t * tvb, int offset)
 {
@@ -811,11 +1118,13 @@
 static int
 dissect_v9_template(proto_tree * pdutree, tvbuff_t * tvb, int offset)
 {
+	struct v9_template template;
 	proto_tree *template_tree;
 	proto_item *template_item;
-	guint16 count;
+	guint16 id, count;
 	gint32 i;
 
+	id = tvb_get_ntohs(tvb, offset);
 	proto_tree_add_item(pdutree, hf_cflow_template_id, tvb,
 	    offset, 2, FALSE);
 	offset += 2;
@@ -825,6 +1134,16 @@
 	    offset, 2, FALSE);
 	offset += 2;
 
+	/* Cache template */
+	memset(&template, 0, sizeof(template));
+	template.id = id;
+	template.count = count;
+	template.source_addr = 0;	/* XXX */
+	template.source_id = 0;		/* XXX */
+	tvb_memcpy(tvb, (guint8 *)template.entries, offset,
+	    count * sizeof(struct v9_template_entry));
+	v9_template_add(&template);
+
 	for (i = 1; i <= count; i++) {
 		guint16 type, length;
 
@@ -832,8 +1151,7 @@
 		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));
+		    offset, 4, "Field (%u/%u)", i, count);
 		template_tree = proto_item_add_subtree(template_item, ett_template);
 
 		proto_tree_add_item(template_tree,
@@ -848,12 +1166,7 @@
 	return (0);
 }
 
-struct _v9_template_type {
-	guint16	type;
-	gchar	*name;
-};
-
-static struct _v9_template_type v9_template_types[] = {
+static value_string v9_template_types[] = {
 	{ 1, "BYTES" },
 	{ 2, "PKTS" },
 	{ 3, "FLOWS" },
@@ -880,20 +1193,41 @@
 	{ 40, "TOTAL_BYTES_EXP" },
 	{ 41, "TOTAL_PKTS_EXP" },
 	{ 42, "TOTAL_FLOWS_EXP" },
-	{ 0, "UNKNOWN" }
+	{ 0, NULL },
 };
 
-static gchar *
-v9_template_type_to_string(guint16 type)
+static void
+v9_template_add(struct v9_template *template)
 {
-	struct _v9_template_type *p;
+	int i;
 
-	for (p = v9_template_types; p->type != 0; p++) {
-		if (type == p->type)
-			break;
+	/* Add up the actual length of the data and store in proper byte order */
+	template->length = 0;
+	for (i = 0; i < template->count; i++) {
+		template->entries[i].type = ntohs(template->entries[i].type);
+		template->entries[i].length = ntohs(template->entries[i].length);
+		template->length += template->entries[i].length;
 	}
 
-	return (p->name);
+	memmove(&v9_template_cache[template->id % V9TEMPLATE_CACHE_MAX_ENTRIES],
+	    template, sizeof(*template));
+}
+
+static struct v9_template *
+v9_template_get(guint16 id, guint32 src_addr, guint32 src_id)
+{
+	struct v9_template *template;
+
+	src_addr = 0;
+	template = &v9_template_cache[id % V9TEMPLATE_CACHE_MAX_ENTRIES];
+
+	if (template->id != id ||
+	    template->source_addr != src_addr ||
+	    template->source_id != src_id) {
+		template = NULL;
+	}
+
+	return (template);
 }
 
 /*
@@ -1135,7 +1469,7 @@
 		 },
 		{&hf_cflow_template_field_type,
 		 {"Type", "cflow.template_field_type",
-		  FT_UINT16, BASE_DEC, NULL, 0x0,
+		  FT_UINT16, BASE_DEC, VALS(v9_template_types), 0x0,
 		  "Template field type", HFILL}
 		 },
 		{&hf_cflow_template_field_length,
@@ -1151,6 +1485,11 @@
 		  FT_IPv4, BASE_NONE, NULL, 0x0,
 		  "Flow Source Address", HFILL}
 		 },
+		{&hf_cflow_srcaddr_v6,
+		 {"SrcAddr", "cflow.srcaddrv6",
+		  FT_IPv6, BASE_NONE, NULL, 0x0,
+		  "Flow Source Address", HFILL}
+		 },
 		{&hf_cflow_srcnet,
 		 {"SrcNet", "cflow.srcnet",
 		  FT_IPv4, BASE_NONE, NULL, 0x0,
@@ -1161,6 +1500,11 @@
 		  FT_IPv4, BASE_NONE, NULL, 0x0,
 		  "Flow Destination Address", HFILL}
 		 },
+		{&hf_cflow_dstaddr_v6,
+		 {"DstAddr", "cflow.dstaddrv6",
+		  FT_IPv6, BASE_NONE, NULL, 0x0,
+		  "Flow Destination Address", HFILL}
+		 },
 		{&hf_cflow_dstnet,
 		 {"DstNet", "cflow.dstaddr",
 		  FT_IPv4, BASE_NONE, NULL, 0x0,
@@ -1171,6 +1515,21 @@
 		  FT_IPv4, BASE_NONE, NULL, 0x0,
 		  "Router nexthop", HFILL}
 		 },
+		{&hf_cflow_nexthop_v6,
+		 {"NextHop", "cflow.nexthopv6",
+		  FT_IPv6, BASE_NONE, NULL, 0x0,
+		  "Router nexthop", HFILL}
+		 },
+		{&hf_cflow_bgpnexthop,
+		 {"BGPNextHop", "cflow.bgpnexthop",
+		  FT_IPv4, BASE_NONE, NULL, 0x0,
+		  "BGP Router Nexthop", HFILL}
+		 },
+		{&hf_cflow_bgpnexthop_v6,
+		 {"BGPNextHop", "cflow.bgpnexthopv6",
+		  FT_IPv6, BASE_NONE, NULL, 0x0,
+		  "BGP Router Nexthop", HFILL}
+		 },
 		{&hf_cflow_inputint,
 		 {"InputInt", "cflow.inputint",
 		  FT_UINT16, BASE_DEC, NULL, 0x0,
@@ -1191,11 +1550,26 @@
 		  FT_UINT32, BASE_DEC, NULL, 0x0,
 		  "Count of packets", HFILL}
 		 },
+		{&hf_cflow_packets64,
+		 {"Packets", "cflow.packets64",
+		  FT_UINT64, BASE_DEC, NULL, 0x0,
+		  "Count of packets", HFILL}
+		 },
+		{&hf_cflow_packetsout,
+		 {"PacketsOut", "cflow.packetsout",
+		  FT_UINT64, BASE_DEC, NULL, 0x0,
+		  "Count of packets going out", HFILL}
+		 },
 		{&hf_cflow_octets,
 		 {"Octets", "cflow.octets",
 		  FT_UINT32, BASE_DEC, NULL, 0x0,
 		  "Count of bytes", HFILL}
 		 },
+		{&hf_cflow_octets64,
+		 {"Octets", "cflow.octets64",
+		  FT_UINT64, BASE_DEC, NULL, 0x0,
+		  "Count of bytes", HFILL}
+		 },
 		{&hf_cflow_timestart,
 		 {"StartTime", "cflow.timestart",
 		  FT_RELATIVE_TIME, BASE_NONE, NULL, 0x0,
@@ -1260,6 +1634,31 @@
 		 {"Router Shortcut", "cflow.routersc",
 		  FT_IPv4, BASE_NONE, NULL, 0x0,
 		  "Router shortcut by switch", HFILL}
+		 },
+		{&hf_cflow_mulpackets,
+		 {"MulticastPackets", "cflow.mulpackets",
+		  FT_UINT32, BASE_DEC, NULL, 0x0,
+		  "Count of multicast packets", HFILL}
+		 },
+		{&hf_cflow_muloctets,
+		 {"MulticastOctets", "cflow.muloctets",
+		  FT_UINT32, BASE_DEC, NULL, 0x0,
+		  "Count of multicast octets", HFILL}
+		 },
+		{&hf_cflow_octets_exp,
+		 {"OctetsExp", "cflow.octetsexp",
+		  FT_UINT32, BASE_DEC, NULL, 0x0,
+		  "Octets exported", HFILL}
+		 },
+		{&hf_cflow_packets_exp,
+		 {"PacketsExp", "cflow.packetsexp",
+		  FT_UINT32, BASE_DEC, NULL, 0x0,
+		  "Packets exported", HFILL}
+		 },
+		{&hf_cflow_flows_exp,
+		 {"FlowsExp", "cflow.flowsexp",
+		  FT_UINT32, BASE_DEC, NULL, 0x0,
+		  "Flows exported", HFILL}
 		 }
 		/*
 		 * end pdu content storage 
@@ -1270,15 +1669,26 @@
 		&ett_netflow,
 		&ett_unixtime,
 		&ett_flow,
-		&ett_template
+		&ett_template,
+		&ett_dataflowset
 	};
 
+	module_t *netflow_module;
+
 	proto_netflow = proto_register_protocol("Cisco NetFlow", "CFLOW",
 						"cflow");
 
 	proto_register_field_array(proto_netflow, hf, array_length(hf));
 	proto_register_subtree_array(ett, array_length(ett));
 
+	/* Register our configuration options for NetFlow */
+	netflow_module = prefs_register_protocol(proto_netflow,
+	    proto_reg_handoff_netflow);
+
+	prefs_register_uint_preference(netflow_module, "udp.port",
+	    "NetFlow UDP Port", "Set the port for NetFlow messages",
+	    10, &global_netflow_udp_port);
+
 	register_dissector("cflow", dissect_netflow, proto_netflow);
 }
 
@@ -1289,9 +1699,19 @@
 void
 proto_reg_handoff_netflow(void)
 {
-	dissector_handle_t netflow_handle;
+	static int netflow_prefs_initialized = FALSE;
+	static dissector_handle_t netflow_handle;
+
+	if (!netflow_prefs_initialized) {
+		netflow_handle = create_dissector_handle(dissect_netflow,
+		    proto_netflow);
+		netflow_prefs_initialized = TRUE;
+	} else {
+		dissector_delete("udp.port", netflow_udp_port, netflow_handle);
+	}
+
+	/* Set out port number for future use */
+	netflow_udp_port = global_netflow_udp_port;
 
-	netflow_handle = create_dissector_handle(dissect_netflow,
-						 proto_netflow);
-	dissector_add("udp.port", UDP_PORT_NETFLOW, netflow_handle);
+	dissector_add("udp.port", netflow_udp_port, netflow_handle);
 }