aboutsummaryrefslogtreecommitdiffstats
path: root/epan/dissectors/packet-sflow.c
diff options
context:
space:
mode:
Diffstat (limited to 'epan/dissectors/packet-sflow.c')
-rw-r--r--epan/dissectors/packet-sflow.c1068
1 files changed, 1068 insertions, 0 deletions
diff --git a/epan/dissectors/packet-sflow.c b/epan/dissectors/packet-sflow.c
new file mode 100644
index 0000000000..9177a7be74
--- /dev/null
+++ b/epan/dissectors/packet-sflow.c
@@ -0,0 +1,1068 @@
+/* packet-sflow.c
+ * Routines for sFlow dissection
+ * Copyright 2003, Jeff Rizzo <riz@boogers.sf.ca.us>
+ *
+ * $Id$
+ *
+ * Ethereal - Network traffic analyzer
+ * By Gerald Combs <gerald@ethereal.com>
+ * 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.
+ */
+
+/* This file (mostly) implements a dissector for sFlow (RFC3176),
+ * from the version 4 spec at http://www.sflow.org/SFLOW-DATAGRAM.txt .
+ *
+ * TODO:
+ * Fix the highlighting of the datastream when bits are selected
+ * split things out into packet-sflow.h ?
+ * make routines more consistent as to whether they return
+ * 'offset' or bytes consumed ('len')
+ * implement sampled_ipv4 and sampled_ipv6 packet data types
+ * implement extended_gateway
+ * implement extended_user
+ * implement extended_url
+ * implement non-generic counters sampling
+ * implement the draft version 5 spec
+ */
+
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include <glib.h>
+
+#ifdef NEED_SNPRINTF_H
+# include "snprintf.h"
+#endif
+
+#include <epan/packet.h>
+/*#include "packet-sflow.h"*/
+
+#define UDP_PORT_SFLOW 6343
+
+#define ADDRESS_IPV4 1
+#define ADDRESS_IPV6 2
+
+#define FLOWSAMPLE 1
+#define COUNTERSSAMPLE 2
+
+static const value_string sflow_sampletype[] = {
+ { FLOWSAMPLE, "Flow sample" },
+ { COUNTERSSAMPLE, "Counters sample" },
+ { 0, NULL }
+};
+
+/* interface counter types */
+#define SFLOW_COUNTERS_GENERIC 1
+#define SFLOW_COUNTERS_ETHERNET 2
+#define SFLOW_COUNTERS_TOKENRING 3
+#define SFLOW_COUNTERS_FDDI 4
+#define SFLOW_COUNTERS_VG 5
+#define SFLOW_COUNTERS_WAN 6
+#define SFLOW_COUNTERS_VLAN 7
+
+static const value_string sflow_counterstype [] = {
+ { SFLOW_COUNTERS_GENERIC, "Generic counters" },
+ { SFLOW_COUNTERS_ETHERNET, "Ethernet counters" },
+ { SFLOW_COUNTERS_FDDI, "FDDI counters" },
+ { SFLOW_COUNTERS_VG, "100baseVG counters" },
+ { SFLOW_COUNTERS_WAN, "WAN counters" },
+ { SFLOW_COUNTERS_VLAN, "VLAN counters" },
+ { 0, NULL }
+};
+
+#define MAX_HEADER_SIZE 256
+
+#define SFLOW_PACKET_DATA_TYPE_HEADER 1
+#define SFLOW_PACKET_DATA_TYPE_IPV4 2
+#define SFLOW_PACKET_DATA_TYPE_IPV6 3
+
+static const value_string sflow_packet_information_type [] = {
+ { SFLOW_PACKET_DATA_TYPE_HEADER, "Packet headers are sampled" },
+ { SFLOW_PACKET_DATA_TYPE_IPV4, "IP Version 4 data" },
+ { SFLOW_PACKET_DATA_TYPE_IPV6, "IP Version 6 data" },
+ { 0, NULL}
+};
+
+#define SFLOW_HEADER_ETHERNET 1
+#define SFLOW_HEADER_TOKENBUS 2
+#define SFLOW_HEADER_TOKENRING 3
+#define SFLOW_HEADER_FDDI 4
+#define SFLOW_HEADER_FRAME_RELAY 5
+#define SFLOW_HEADER_X25 6
+#define SFLOW_HEADER_PPP 7
+#define SFLOW_HEADER_SMDS 8
+#define SFLOW_HEADER_AAL5 9
+#define SFLOW_HEADER_AAL5_IP 10
+#define SFLOW_HEADER_IPv4 11
+#define SFLOW_HEADER_IPv6 12
+#define SFLOW_HEADER_MPLS 13
+
+static const value_string sflow_header_protocol[] = {
+ { SFLOW_HEADER_ETHERNET, "Ethernet" },
+ { SFLOW_HEADER_TOKENBUS, "Token Bus" },
+ { SFLOW_HEADER_TOKENRING, "Token Ring" },
+ { SFLOW_HEADER_FDDI, "FDDI" },
+ { SFLOW_HEADER_FRAME_RELAY, "Frame Relay" },
+ { SFLOW_HEADER_X25, "X.25" },
+ { SFLOW_HEADER_PPP, "PPP" },
+ { SFLOW_HEADER_SMDS, "SMDS" },
+ { SFLOW_HEADER_AAL5, "ATM AAL5" },
+ { SFLOW_HEADER_AAL5_IP, "ATM AAL5-IP (e.g., Cisco AAL5 mux)" },
+ { SFLOW_HEADER_IPv4, "IPv4" },
+ { SFLOW_HEADER_IPv6, "IPv6" },
+ { SFLOW_HEADER_MPLS, "MPLS" },
+ { 0, NULL }
+};
+
+/* extended data types */
+#define SFLOW_EXTENDED_SWITCH 1
+#define SFLOW_EXTENDED_ROUTER 2
+#define SFLOW_EXTENDED_GATEWAY 3
+#define SFLOW_EXTENDED_USER 4
+#define SFLOW_EXTENDED_URL 5
+
+static const value_string sflow_extended_data_types[] = {
+ { SFLOW_EXTENDED_SWITCH, "Extended switch information" },
+ { SFLOW_EXTENDED_ROUTER, "Extended router information" },
+ { SFLOW_EXTENDED_GATEWAY, "Extended gateway information" },
+ { SFLOW_EXTENDED_USER, "Extended user information" },
+ { SFLOW_EXTENDED_URL, "Extended URL information" },
+ { 0, NULL }
+};
+
+
+/* flow sample header */
+struct sflow_flow_sample_header {
+ guint32 sequence_number;
+ guint32 source_id;
+ guint32 sampling_rate;
+ guint32 sample_pool;
+ guint32 drops;
+ guint32 input;
+ guint32 output;
+};
+
+/* counters sample header */
+struct sflow_counters_sample_header {
+ guint32 sequence_number;
+ guint32 source_id;
+ guint32 sampling_interval;
+ guint32 counters_type;
+};
+
+/* generic interface counters */
+struct if_counters {
+ guint32 ifIndex;
+ guint32 ifType;
+ guint64 ifSpeed;
+ guint32 ifDirection;
+ guint32 ifStatus;
+ guint64 ifInOctets;
+ guint32 ifInUcastPkts;
+ guint32 ifInMulticastPkts;
+ guint32 ifInBroadcastPkts;
+ guint32 ifInDiscards;
+ guint32 ifInErrors;
+ guint32 ifInUnknownProtos;
+ guint64 ifOutOctets;
+ guint32 ifOutUcastPkts;
+ guint32 ifOutMulticastPkts;
+ guint32 ifOutBroadcastPkts;
+ guint32 ifOutDiscards;
+ guint32 ifOutErrors;
+ guint32 ifPromiscuousMode;
+};
+
+/* ethernet counters. These will be preceded by generic counters. */
+struct ethernet_counters {
+ guint32 dot3StatsAlignmentErrors;
+ guint32 dot3StatsFCSErrors;
+ guint32 dot3StatsSingleCollisionFrames;
+ guint32 dot3StatsMultipleCollisionFrames;
+ guint32 dot3StatsSQETestErrors;
+ guint32 dot3StatsDeferredTransmissions;
+ guint32 dot3StatsLateCollisions;
+ guint32 dot3StatsExcessiveCollisions;
+ guint32 dot3StatsInternalMacTransmitErrors;
+ guint32 dot3StatsCarrierSenseErrors;
+ guint32 dot3StatsFrameTooLongs;
+ guint32 dot3StatsInternalMacReceiveErrors;
+ guint32 dot3StatsSymbolErrors;
+};
+
+/* Token Ring counters */
+struct token_ring_counters {
+ guint32 dot5StatsLineErrors;
+ guint32 dot5StatsBurstErrors;
+ guint32 dot5StatsACErrors;
+ guint32 dot5StatsAbortTransErrors;
+ guint32 dot5StatsInternalErrors;
+ guint32 dot5StatsLostFrameErrors;
+ guint32 dot5StatsReceiveCongestions;
+ guint32 dot5StatsFrameCopiedErrors;
+ guint32 dot5StatsTokenErrors;
+ guint32 dot5StatsSoftErrors;
+ guint32 dot5StatsHardErrors;
+ guint32 dot5StatsSignalLoss;
+ guint32 dot5StatsTransmitBeacons;
+ guint32 dot5StatsRecoverys;
+ guint32 dot5StatsLobeWires;
+ guint32 dot5StatsRemoves;
+ guint32 dot5StatsSingles;
+ guint32 dot5StatsFreqErrors;
+};
+
+/* 100BaseVG counters */
+
+struct vg_counters {
+ guint32 dot12InHighPriorityFrames;
+ guint64 dot12InHighPriorityOctets;
+ guint32 dot12InNormPriorityFrames;
+ guint64 dot12InNormPriorityOctets;
+ guint32 dot12InIPMErrors;
+ guint32 dot12InOversizeFrameErrors;
+ guint32 dot12InDataErrors;
+ guint32 dot12InNullAddressedFrames;
+ guint32 dot12OutHighPriorityFrames;
+ guint64 dot12OutHighPriorityOctets;
+ guint32 dot12TransitionIntoTrainings;
+ guint64 dot12HCInHighPriorityOctets;
+ guint64 dot12HCInNormPriorityOctets;
+ guint64 dot12HCOutHighPriorityOctets;
+};
+
+/* VLAN counters */
+
+struct vlan_counters {
+ guint32 vlan_id;
+ guint32 octets;
+ guint32 ucastPkts;
+ guint32 multicastPkts;
+ guint32 broadcastPkts;
+ guint32 discards;
+};
+
+/* Initialize the protocol and registered fields */
+static int proto_sflow = -1;
+static int hf_sflow_version = -1;
+/*static int hf_sflow_agent_address_type = -1; */
+static int hf_sflow_agent_address_v4 = -1;
+static int hf_sflow_agent_address_v6 = -1;
+static int hf_sflow_seqnum = -1;
+static int hf_sflow_sysuptime = -1;
+static int hf_sflow_numsamples = -1;
+static int hf_sflow_header_protocol = -1;
+static int hf_sflow_sampletype = -1;
+static int hf_sflow_header = -1;
+static int hf_sflow_packet_information_type = -1;
+static int hf_sflow_vlan_in = -1; /* incoming 802.1q VLAN ID */
+static int hf_sflow_vlan_out = -1; /* outgoing 802.1q VLAN ID */
+static int hf_sflow_pri_in = -1; /* incominging 802.1p priority */
+static int hf_sflow_pri_out = -1; /* outgoing 802.1p priority */
+static int hf_sflow_nexthop_v4 = -1; /* nexthop address */
+static int hf_sflow_nexthop_v6 = -1; /* nexthop address */
+static int hf_sflow_ifindex = -1;
+static int hf_sflow_iftype = -1;
+static int hf_sflow_ifspeed = -1;
+static int hf_sflow_ifdirection = -1;
+static int hf_sflow_ifstatus = -1;
+static int hf_sflow_ifinoct = -1;
+static int hf_sflow_ifinpkt = -1;
+static int hf_sflow_ifinmcast = -1;
+static int hf_sflow_ifinbcast = -1;
+static int hf_sflow_ifinerr = -1;
+static int hf_sflow_ifindisc = -1;
+static int hf_sflow_ifinunk = -1;
+static int hf_sflow_ifoutoct = -1;
+static int hf_sflow_ifoutpkt = -1;
+static int hf_sflow_ifoutmcast = -1;
+static int hf_sflow_ifoutbcast = -1;
+static int hf_sflow_ifoutdisc = -1;
+static int hf_sflow_ifouterr = -1;
+static int hf_sflow_ifpromisc = -1;
+
+/* Initialize the subtree pointers */
+static gint ett_sflow = -1;
+static gint ett_sflow_sample = -1;
+static gint ett_sflow_extended_data = -1;
+static gint ett_sflow_sampled_header = -1;
+
+/* dissectors for other protocols */
+static dissector_handle_t eth_handle;
+static dissector_handle_t tr_handle;
+static dissector_handle_t fddi_handle;
+static dissector_handle_t fr_handle;
+static dissector_handle_t x25_handle;
+static dissector_handle_t ppp_handle;
+static dissector_handle_t smds_handle;
+static dissector_handle_t aal5_handle;
+static dissector_handle_t ipv4_handle;
+static dissector_handle_t ipv6_handle;
+static dissector_handle_t mpls_handle;
+
+/* dissect a sampled header - layer 2 protocols */
+static gint
+dissect_sflow_sampled_header(tvbuff_t *tvb, packet_info *pinfo,
+ proto_tree *tree, volatile gint offset)
+{
+ guint32 header_proto, frame_length;
+ volatile guint32 header_length;
+ tvbuff_t *next_tvb;
+ proto_tree *sflow_header_tree;
+ proto_item *ti;
+ /* stuff for saving column state before calling other dissectors.
+ * Thanks to Guy Harris for the tip. */
+ gboolean save_writable;
+ volatile address save_dl_src;
+ volatile address save_dl_dst;
+ volatile address save_net_src;
+ volatile address save_net_dst;
+ volatile address save_src;
+ volatile address save_dst;
+
+ header_proto = tvb_get_ntohl(tvb,offset);
+ proto_tree_add_item(tree, hf_sflow_header_protocol, tvb, offset,
+ 4, FALSE);
+ offset += 4;
+ frame_length = tvb_get_ntohl(tvb,offset);
+ proto_tree_add_text(tree, tvb, offset, 4, "Frame Length: %d bytes",
+ frame_length);
+ offset += 4;
+ header_length = tvb_get_ntohl(tvb,offset);
+ offset += 4;
+
+ if (header_length % 4) /* XDR requires 4-byte alignment */
+ header_length += 4 - (header_length % 4);
+
+
+ ti = proto_tree_add_item(tree, hf_sflow_header, tvb, offset,
+ header_length, FALSE);
+ sflow_header_tree = proto_item_add_subtree(ti, ett_sflow_sampled_header);
+
+ /* hand the header off to the appropriate dissector. It's probably
+ * a short frame, so ignore any exceptions. */
+ next_tvb = tvb_new_subset(tvb, offset, header_length, frame_length);
+
+ /* save some state */
+ save_writable = col_get_writable(pinfo->cinfo);
+ col_set_writable(pinfo->cinfo, FALSE);
+ save_dl_src = pinfo->dl_src;
+ save_dl_dst = pinfo->dl_dst;
+ save_net_src = pinfo->net_src;
+ save_net_dst = pinfo->net_dst;
+ save_src = pinfo->src;
+ save_dst = pinfo->dst;
+
+ TRY {
+ switch (header_proto) {
+ case SFLOW_HEADER_ETHERNET:
+ call_dissector(eth_handle, next_tvb, pinfo, sflow_header_tree);
+ break;
+ case SFLOW_HEADER_TOKENRING:
+ call_dissector(tr_handle, next_tvb, pinfo, sflow_header_tree);
+ break;
+ case SFLOW_HEADER_FDDI:
+ call_dissector(fddi_handle, next_tvb, pinfo, sflow_header_tree);
+ break;
+ case SFLOW_HEADER_FRAME_RELAY:
+ call_dissector(fr_handle, next_tvb, pinfo, sflow_header_tree);
+ break;
+ case SFLOW_HEADER_X25:
+ call_dissector(x25_handle, next_tvb, pinfo, sflow_header_tree);
+ break;
+ case SFLOW_HEADER_PPP:
+ call_dissector(ppp_handle, next_tvb, pinfo, sflow_header_tree);
+ break;
+ case SFLOW_HEADER_SMDS:
+ call_dissector(smds_handle, next_tvb, pinfo, sflow_header_tree);
+ break;
+ case SFLOW_HEADER_AAL5:
+ case SFLOW_HEADER_AAL5_IP:
+ /* I'll be surprised if this works! I have no AAL5 captures
+ * to test with, and I'm not sure how the encapsulation goes */
+ call_dissector(aal5_handle, next_tvb, pinfo, sflow_header_tree);
+ break;
+ case SFLOW_HEADER_IPv4:
+ call_dissector(ipv4_handle, next_tvb, pinfo, sflow_header_tree);
+ break;
+ case SFLOW_HEADER_IPv6:
+ call_dissector(ipv6_handle, next_tvb, pinfo, sflow_header_tree);
+ break;
+ case SFLOW_HEADER_MPLS:
+ call_dissector(mpls_handle, next_tvb, pinfo, sflow_header_tree);
+ break;
+ default:
+ /* some of the protocols, I have no clue where to begin. */
+ break;
+ };
+ }
+ CATCH2(BoundsError, ReportedBoundsError) {
+ ; /* do nothing */
+ }
+ ENDTRY;
+
+ /* restore saved state */
+ col_set_writable(pinfo->cinfo, save_writable);
+ pinfo->dl_src = save_dl_src;
+ pinfo->dl_dst = save_dl_dst;
+ pinfo->net_src = save_net_src;
+ pinfo->net_dst = save_net_dst;
+ pinfo->src = save_src;
+ pinfo->dst = save_dst;
+
+ offset += header_length;
+ return offset;
+}
+
+/* extended switch data, after the packet data */
+static gint
+dissect_sflow_extended_switch(tvbuff_t *tvb, proto_tree *tree, gint offset)
+{
+ gint32 len = 0;
+
+ proto_tree_add_item(tree, hf_sflow_vlan_in, tvb, offset + len, 4, FALSE);
+ len += 4;
+ proto_tree_add_item(tree, hf_sflow_vlan_out, tvb, offset + len, 4, FALSE);
+ len += 4;
+ proto_tree_add_item(tree, hf_sflow_pri_in, tvb, offset + len, 4, FALSE);
+ len += 4;
+ proto_tree_add_item(tree, hf_sflow_pri_out, tvb, offset + len, 4, FALSE);
+ len += 4;
+
+ return len;
+}
+
+/* extended router data, after the packet data */
+static gint
+dissect_sflow_extended_router(tvbuff_t *tvb, proto_tree *tree, gint offset)
+{
+ gint32 len = 0;
+ guint32 address_type, mask_bits;
+
+ address_type = tvb_get_ntohl(tvb, offset);
+ switch (address_type) {
+ case ADDRESS_IPV4:
+ proto_tree_add_ipv4(tree, hf_sflow_nexthop_v4, tvb, offset + len,
+ 8, FALSE);
+ len += 8;
+ break;
+ case ADDRESS_IPV6:
+ proto_tree_add_ipv6(tree, hf_sflow_nexthop_v6, tvb, offset + len,
+ 20, FALSE);
+ len += 20;
+ break;
+ default:
+ proto_tree_add_text(tree, tvb, offset + len, 4,
+ "Unknown address type (%d)", address_type);
+ len += 4; /* not perfect, but what else to do? */
+ return len; /* again, this is wrong. but... ? */
+ break;
+ };
+
+ mask_bits = tvb_get_ntohl(tvb, offset + len);
+ proto_tree_add_text(tree, tvb, offset + len, 4,
+ "Source address prefix is %d bits long", mask_bits);
+ len += 4;
+ mask_bits = tvb_get_ntohl(tvb, offset + len);
+ proto_tree_add_text(tree, tvb, offset + len, 4,
+ "Destination address prefix is %d bits long",
+ mask_bits);
+ len += 4;
+ return len;
+}
+
+/* dissect a flow sample */
+static gint
+dissect_sflow_flow_sample(tvbuff_t *tvb, packet_info *pinfo,
+ proto_tree *tree, gint offset, proto_item *parent)
+{
+ struct sflow_flow_sample_header flow_header;
+ proto_tree *sflow_sample_tree;
+ proto_item *ti;
+ guint32 packet_type, extended_data, ext_type, i;
+
+ /* grab the flow header. This will remain in network byte
+ order, so must convert each item before use */
+ tvb_memcpy(tvb,(guint8 *)&flow_header,offset,sizeof(flow_header));
+ proto_tree_add_text(tree, tvb, offset, 4,
+ "Sequence number: %u",
+ g_ntohl(flow_header.sequence_number));
+ proto_item_append_text(parent, ", seq %u",
+ g_ntohl(flow_header.sequence_number));
+ proto_tree_add_text(tree, tvb, offset+4, 4,
+ "Source ID class: %u index: %u",
+ g_ntohl(flow_header.source_id) >> 24,
+ g_ntohl(flow_header.source_id) & 0x00ffffff);
+ proto_tree_add_text(tree, tvb, offset+8, 4,
+ "Sampling rate: 1 out of %u packets",
+ g_ntohl(flow_header.sampling_rate));
+ proto_tree_add_text(tree, tvb, offset+12, 4,
+ "Sample pool: %u total packets",
+ g_ntohl(flow_header.sample_pool));
+ proto_tree_add_text(tree, tvb, offset+16, 4,
+ "Dropped packets: %u",
+ g_ntohl(flow_header.drops));
+ proto_tree_add_text(tree, tvb, offset+20, 4,
+ "Input Interface: ifIndex %u",
+ g_ntohl(flow_header.input));
+ if (g_ntohl(flow_header.output) >> 31)
+ proto_tree_add_text(tree, tvb, offset+24, 4,
+ "multiple outputs: %u interfaces",
+ g_ntohl(flow_header.output) & 0x00ffffff);
+ else
+ proto_tree_add_text(tree, tvb, offset+24, 4,
+ "Output interface: ifIndex %u",
+ g_ntohl(flow_header.output) & 0x00ffffff);
+ offset += sizeof(flow_header);
+
+ /* what kind of flow sample is it? */
+ packet_type = tvb_get_ntohl(tvb, offset);
+ offset += 4;
+ switch (packet_type) {
+ case SFLOW_PACKET_DATA_TYPE_HEADER:
+ offset = dissect_sflow_sampled_header(tvb, pinfo, tree, offset);
+ break;
+ case SFLOW_PACKET_DATA_TYPE_IPV4:
+ case SFLOW_PACKET_DATA_TYPE_IPV6:
+ default:
+ break;
+ };
+ /* still need to dissect extended data */
+ extended_data = tvb_get_ntohl(tvb,offset);
+ offset += 4;
+
+ for (i=0; i < extended_data; i++) {
+ /* figure out what kind of extended data it is */
+ ext_type = tvb_get_ntohl(tvb,offset);
+
+ /* create a subtree. Might want to move this to
+ * the end, so more info can be correct.
+ */
+ ti = proto_tree_add_text(tree, tvb, offset, 4, "%s",
+ val_to_str(ext_type,
+ sflow_extended_data_types,
+ "Unknown extended information"));
+ offset += 4;
+ sflow_sample_tree = proto_item_add_subtree(ti, ett_sflow_sample);
+
+ switch (ext_type) {
+ case SFLOW_EXTENDED_SWITCH:
+ offset += dissect_sflow_extended_switch(tvb, sflow_sample_tree,
+ offset);
+ break;
+ case SFLOW_EXTENDED_ROUTER:
+ offset += dissect_sflow_extended_router(tvb, sflow_sample_tree,
+ offset);
+ break;
+ case SFLOW_EXTENDED_GATEWAY:
+ break;
+ case SFLOW_EXTENDED_USER:
+ break;
+ case SFLOW_EXTENDED_URL:
+ break;
+ default:
+ break;
+ }
+ }
+ return offset;
+
+}
+
+/* dissect a counters sample */
+static gint
+dissect_sflow_counters_sample(tvbuff_t *tvb, proto_tree *tree,
+ gint offset, proto_item *parent)
+{
+ struct sflow_counters_sample_header counters_header;
+ struct if_counters ifc;
+ struct ethernet_counters ethc;
+ struct token_ring_counters tokc;
+ struct vg_counters vgc;
+ struct vlan_counters vlanc;
+
+ /* grab the flow header. This will remain in network byte
+ order, so must convert each item before use */
+ tvb_memcpy(tvb,(guint8 *)&counters_header,offset,sizeof(counters_header));
+ proto_tree_add_text(tree, tvb, offset, 4,
+ "Sequence number: %u",
+ g_ntohl(counters_header.sequence_number));
+ proto_item_append_text(parent, ", seq %u",
+ g_ntohl(counters_header.sequence_number));
+ proto_tree_add_text(tree, tvb, offset + 4, 4,
+ "Source ID class: %u index: %u",
+ g_ntohl(counters_header.source_id) >> 24,
+ g_ntohl(counters_header.source_id) & 0x00ffffff);
+ proto_tree_add_text(tree, tvb, offset + 8, 4,
+ "Sampling Interval: %u",
+ g_ntohl(counters_header.sampling_interval));
+ proto_tree_add_text(tree, tvb, offset + 12, 4, "Counters type: %s",
+ val_to_str(g_ntohl(counters_header.counters_type),
+ sflow_counterstype, "Unknown type"));
+
+ offset += sizeof(counters_header);
+
+ /* most counters types have the "generic" counters first */
+ switch (g_ntohl(counters_header.counters_type)) {
+ case SFLOW_COUNTERS_GENERIC:
+ case SFLOW_COUNTERS_ETHERNET:
+ case SFLOW_COUNTERS_TOKENRING:
+ case SFLOW_COUNTERS_FDDI:
+ case SFLOW_COUNTERS_VG:
+ case SFLOW_COUNTERS_WAN:
+ tvb_memcpy(tvb,(guint8 *)&ifc, offset, sizeof(ifc));
+ proto_item_append_text(parent, ", ifIndex %u",
+ g_ntohl(ifc.ifIndex));
+ proto_tree_add_item(tree, hf_sflow_ifindex, tvb, offset, 4, FALSE);
+ offset += 4;
+ proto_tree_add_item(tree, hf_sflow_iftype, tvb, offset, 4, FALSE);
+ offset += 4;
+ proto_tree_add_item(tree, hf_sflow_ifspeed, tvb, offset, 8, FALSE);
+ offset += 8;
+ proto_tree_add_item(tree, hf_sflow_ifdirection, tvb, offset,
+ 4, FALSE);
+ offset += 4;
+ proto_tree_add_item(tree, hf_sflow_ifstatus, tvb, offset, 4, FALSE);
+ offset += 4;
+ proto_tree_add_item(tree, hf_sflow_ifinoct, tvb, offset, 8, FALSE);
+ offset += 8;
+ proto_tree_add_item(tree, hf_sflow_ifinpkt, tvb, offset, 4, FALSE);
+ offset += 4;
+ proto_tree_add_item(tree, hf_sflow_ifinmcast, tvb, offset,
+ 4, FALSE);
+ offset += 4;
+ proto_tree_add_item(tree, hf_sflow_ifinbcast, tvb, offset,
+ 4, FALSE);
+ offset += 4;
+ proto_tree_add_item(tree, hf_sflow_ifindisc, tvb, offset,
+ 4, FALSE);
+ offset += 4;
+ proto_tree_add_item(tree, hf_sflow_ifinerr, tvb, offset,
+ 4, FALSE);
+ offset += 4;
+ proto_tree_add_item(tree, hf_sflow_ifinunk, tvb, offset,
+ 4, FALSE);
+ offset += 4;
+ proto_tree_add_item(tree, hf_sflow_ifoutoct, tvb, offset, 8, FALSE);
+ offset += 8;
+ proto_tree_add_item(tree, hf_sflow_ifoutpkt, tvb, offset, 4, FALSE);
+ offset += 4;
+ proto_tree_add_item(tree, hf_sflow_ifoutmcast, tvb, offset,
+ 4, FALSE);
+ offset += 4;
+ proto_tree_add_item(tree, hf_sflow_ifoutbcast, tvb, offset,
+ 4, FALSE);
+ offset += 4;
+ proto_tree_add_item(tree, hf_sflow_ifoutdisc, tvb, offset,
+ 4, FALSE);
+ offset += 4;
+ proto_tree_add_item(tree, hf_sflow_ifouterr, tvb, offset,
+ 4, FALSE);
+ offset += 4;
+ proto_tree_add_item(tree, hf_sflow_ifpromisc, tvb, offset,
+ 4, FALSE);
+ offset += 4;
+ break;
+ };
+
+ /* Some counter types have other info to gather */
+ switch (g_ntohl(counters_header.counters_type)) {
+ case SFLOW_COUNTERS_ETHERNET:
+ tvb_memcpy(tvb,(guint8 *)&ethc, offset, sizeof(ethc));
+ offset += sizeof(ethc);
+ break;
+ case SFLOW_COUNTERS_TOKENRING:
+ tvb_memcpy(tvb,(guint8 *)&tokc, offset, sizeof(tokc));
+ offset += sizeof(tokc);
+ break;
+ case SFLOW_COUNTERS_VG:
+ tvb_memcpy(tvb,(guint8 *)&vgc, offset, sizeof(vgc));
+ offset += sizeof(vgc);
+ break;
+ case SFLOW_COUNTERS_VLAN:
+ tvb_memcpy(tvb,(guint8 *)&vlanc, offset, sizeof(vlanc));
+ offset += sizeof(vlanc);
+ break;
+ default:
+ break;
+ }
+ return offset;
+}
+
+/* Code to dissect the sflow samples */
+static gint
+dissect_sflow_samples(tvbuff_t *tvb, packet_info *pinfo,
+ proto_tree *tree, gint offset)
+{
+ proto_tree *sflow_sample_tree;
+ proto_item *ti; /* tree item */
+ guint32 sample_type;
+
+ /* decide what kind of sample it is. */
+ sample_type = tvb_get_ntohl(tvb,offset);
+
+ ti = proto_tree_add_text(tree, tvb, offset, 4, "%s",
+ val_to_str(sample_type, sflow_sampletype,
+ "Unknown sample type"));
+ sflow_sample_tree = proto_item_add_subtree(ti, ett_sflow_sample);
+
+ proto_tree_add_item(sflow_sample_tree, hf_sflow_sampletype, tvb,
+ offset, 4, FALSE);
+ offset += 4;
+
+ switch (sample_type) {
+ case FLOWSAMPLE:
+ return dissect_sflow_flow_sample(tvb, pinfo, sflow_sample_tree,
+ offset, ti);
+ break;
+ case COUNTERSSAMPLE:
+ return dissect_sflow_counters_sample(tvb, sflow_sample_tree,
+ offset, ti);
+ break;
+ default:
+ break;
+ };
+ return offset;
+}
+
+/* Code to actually dissect the packets */
+static void
+dissect_sflow(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree)
+{
+
+/* Set up structures needed to add the protocol subtree and manage it */
+ proto_item *ti;
+ proto_tree *sflow_tree;
+ guint32 version, seqnum;
+ guint32 agent_address_type;
+ union {
+ guint8 v4[4];
+ guint8 v6[16];
+ } agent_address;
+ guint32 numsamples;
+ volatile guint offset=0;
+ guint i=0;
+
+/* Make entries in Protocol column and Info column on summary display */
+ if (check_col(pinfo->cinfo, COL_PROTOCOL))
+ col_set_str(pinfo->cinfo, COL_PROTOCOL, "sflow");
+
+
+ /* create display subtree for the protocol */
+ ti = proto_tree_add_item(tree, proto_sflow, tvb, 0, -1, FALSE);
+
+ sflow_tree = proto_item_add_subtree(ti, ett_sflow);
+
+ version = tvb_get_ntohl(tvb, offset);
+ if (check_col(pinfo->cinfo, COL_INFO))
+ col_add_fstr(pinfo->cinfo, COL_INFO, "sFlow V%u",
+ version);
+ proto_tree_add_item(sflow_tree,
+ hf_sflow_version, tvb, offset, 4, FALSE);
+ offset += 4;
+
+ agent_address_type = tvb_get_ntohl(tvb, offset);
+ offset += 4;
+ switch (agent_address_type) {
+ case ADDRESS_IPV4:
+ tvb_memcpy(tvb, agent_address.v4, offset, 4);
+ if (check_col(pinfo->cinfo, COL_INFO))
+ col_append_fstr(pinfo->cinfo, COL_INFO, ", agent %s",
+ ip_to_str(agent_address.v4));
+ proto_tree_add_item(sflow_tree,
+ hf_sflow_agent_address_v4, tvb, offset,
+ 4, FALSE);
+ offset += 4;
+ break;
+ case ADDRESS_IPV6:
+ tvb_memcpy(tvb, agent_address.v6, offset, 16);
+ if (check_col(pinfo->cinfo, COL_INFO))
+ col_append_fstr(pinfo->cinfo, COL_INFO, ", agent %s",
+ ip6_to_str((struct e_in6_addr *)agent_address.v6));
+ proto_tree_add_item(sflow_tree,
+ hf_sflow_agent_address_v6, tvb, offset,
+ 16, FALSE);
+ offset += 16;
+ break;
+ default:
+ /* unknown address. this will cause a malformed packet. */
+ break;
+ };
+
+ seqnum = tvb_get_ntohl(tvb, offset);
+ proto_tree_add_item(sflow_tree, hf_sflow_seqnum, tvb,
+ offset, 4, FALSE);
+ offset += 4;
+ proto_tree_add_item(sflow_tree, hf_sflow_sysuptime, tvb,
+ offset+4, 4, FALSE);
+ offset += 4;
+ numsamples = tvb_get_ntohl(tvb,offset);
+ if (check_col(pinfo->cinfo, COL_INFO))
+ col_append_fstr(pinfo->cinfo, COL_INFO, ", seq %u, %u samples",
+ seqnum, numsamples);
+ proto_tree_add_item(sflow_tree, hf_sflow_numsamples, tvb,
+ offset, 4, FALSE);
+ offset += 4;
+
+ /* Ok, we're now at the end of the sflow datagram header;
+ * everything from here out should be samples. Loop over
+ * the expected number of samples, and pass them to the appropriate
+ * dissectors.
+ */
+ for (i=0; i < numsamples; i++) {
+ offset = dissect_sflow_samples(tvb, pinfo, sflow_tree, offset);
+ }
+
+}
+
+
+/* Register the protocol with Ethereal */
+
+/* this format is require because a script is used to build the C function
+ that calls all the protocol registration.
+*/
+
+void
+proto_register_sflow(void)
+{
+
+/* Setup list of header fields See Section 1.6.1 for details*/
+ static hf_register_info hf[] = {
+ { &hf_sflow_version,
+ { "datagram version", "sflow.version",
+ FT_UINT32, BASE_DEC, NULL, 0x0,
+ "sFlow datagram version", HFILL }
+ },
+ { &hf_sflow_agent_address_v4,
+ { "agent address", "sflow.agent",
+ FT_IPv4, BASE_NONE, NULL, 0x0,
+ "sFlow Agent IP address", HFILL }
+ },
+ { &hf_sflow_agent_address_v6,
+ { "agent address", "sflow.agent.v6",
+ FT_IPv6, BASE_NONE, NULL, 0x0,
+ "sFlow Agent IPv6 address", HFILL }
+ },
+ { &hf_sflow_seqnum,
+ { "Sequence number", "sflow.sequence_number",
+ FT_UINT32, BASE_DEC, NULL, 0x0,
+ "sFlow datagram sequence number", HFILL }
+ },
+ { &hf_sflow_sysuptime,
+ { "SysUptime", "sflow.sysuptime",
+ FT_UINT32, BASE_DEC, NULL, 0x0,
+ "System Uptime", HFILL }
+ },
+ { &hf_sflow_numsamples,
+ { "NumSamples", "sflow.numsamples",
+ FT_UINT32, BASE_DEC, NULL, 0x0,
+ "Number of samples in sFlow datagram", HFILL }
+ },
+ { &hf_sflow_sampletype,
+ { "sFlow sample type", "sflow.sampletype",
+ FT_UINT32, BASE_DEC, VALS(sflow_sampletype), 0x0,
+ "Type of sFlow sample", HFILL }
+ },
+ { &hf_sflow_header_protocol,
+ { "Header protocol", "sflow.header_protocol",
+ FT_UINT32, BASE_DEC, VALS(sflow_header_protocol), 0x0,
+ "Protocol of sampled header", HFILL }
+ },
+ { &hf_sflow_header,
+ { "Header of sampled packet", "sflow.header",
+ FT_BYTES, BASE_HEX, NULL, 0x0,
+ "Data from sampled header", HFILL }
+ },
+ { &hf_sflow_packet_information_type,
+ { "Sample type", "sflow.packet_information_type",
+ FT_UINT32, BASE_DEC, VALS(sflow_packet_information_type), 0x0,
+ "Type of sampled information", HFILL }
+ },
+ { &hf_sflow_vlan_in,
+ { "Incoming 802.1q VLAN", "sflow.vlan.in",
+ FT_UINT32, BASE_DEC, NULL, 0x0,
+ "Incoming VLAN ID", HFILL }
+ },
+ { &hf_sflow_vlan_out,
+ { "Outgoing 802.1q VLAN", "sflow.vlan.out",
+ FT_UINT32, BASE_DEC, NULL, 0x0,
+ "Outgoing VLAN ID", HFILL }
+ },
+ { &hf_sflow_pri_in,
+ { "Incoming 802.1p priority", "sflow.pri.in",
+ FT_UINT32, BASE_DEC, NULL, 0x0,
+ "Incoming 802.1p priority", HFILL }
+ },
+ { &hf_sflow_pri_out,
+ { "Outgoing 802.1p priority", "sflow.pri.out",
+ FT_UINT32, BASE_DEC, NULL, 0x0,
+ "Outgoing 802.1p priority", HFILL }
+ },
+ { &hf_sflow_nexthop_v4,
+ { "Next Hop", "sflow.nexthop",
+ FT_IPv4, BASE_DEC, NULL, 0x0,
+ "Next Hop address", HFILL }
+ },
+ { &hf_sflow_nexthop_v6,
+ { "Next Hop", "sflow.nexthop",
+ FT_IPv6, BASE_HEX, NULL, 0x0,
+ "Next Hop address", HFILL }
+ },
+ { &hf_sflow_ifindex,
+ { "Interface index", "sflow.ifindex",
+ FT_UINT32, BASE_DEC, NULL, 0x0,
+ "Interface Index", HFILL }
+ },
+ { &hf_sflow_iftype,
+ { "Interface Type", "sflow.iftype",
+ FT_UINT32, BASE_DEC, NULL, 0x0,
+ "Interface Type", HFILL }
+ },
+ { &hf_sflow_ifspeed,
+ { "Interface Speed", "sflow.ifspeed",
+ FT_UINT64, BASE_DEC, NULL, 0x0,
+ "Interface Speed", HFILL }
+ },
+ { &hf_sflow_ifdirection,
+ { "Interface Direction", "sflow.ifdirection",
+ FT_UINT32, BASE_DEC, NULL, 0x0,
+ "Interface Direction", HFILL }
+ },
+ { &hf_sflow_ifstatus,
+ { "Interface Status", "sflow.ifstatus",
+ FT_UINT32, BASE_DEC, NULL, 0x0,
+ "Interface Status", HFILL }
+ },
+ { &hf_sflow_ifinoct,
+ { "Input Octets", "sflow.ifinoct",
+ FT_UINT64, BASE_DEC, NULL, 0x0,
+ "Interface Input Octets", HFILL }
+ },
+ { &hf_sflow_ifinpkt,
+ { "Input Packets", "sflow.ifinpkt",
+ FT_UINT32, BASE_DEC, NULL, 0x0,
+ "Interface Input Packets", HFILL }
+ },
+ { &hf_sflow_ifinmcast,
+ { "Input Multicast Packets", "sflow.ifinmcast",
+ FT_UINT32, BASE_DEC, NULL, 0x0,
+ "Interface Input Multicast Packets", HFILL }
+ },
+ { &hf_sflow_ifinbcast,
+ { "Input Broadcast Packets", "sflow.ifinbcast",
+ FT_UINT32, BASE_DEC, NULL, 0x0,
+ "Interface Input Broadcast Packets", HFILL }
+ },
+ { &hf_sflow_ifindisc,
+ { "Input Discarded Packets", "sflow.ifindisc",
+ FT_UINT32, BASE_DEC, NULL, 0x0,
+ "Interface Input Discarded Packets", HFILL }
+ },
+ { &hf_sflow_ifinerr,
+ { "Input Errors", "sflow.ifinerr",
+ FT_UINT32, BASE_DEC, NULL, 0x0,
+ "Interface Input Errors", HFILL }
+ },
+ { &hf_sflow_ifinunk,
+ { "Input Unknown Protocol Packets", "sflow.ifinunk",
+ FT_UINT32, BASE_DEC, NULL, 0x0,
+ "Interface Input Unknown Protocol Packets", HFILL }
+ },
+ { &hf_sflow_ifoutoct,
+ { "Output Octets", "sflow.ifoutoct",
+ FT_UINT64, BASE_DEC, NULL, 0x0,
+ "Outterface Output Octets", HFILL }
+ },
+ { &hf_sflow_ifoutpkt,
+ { "Output Packets", "sflow.ifoutpkt",
+ FT_UINT32, BASE_DEC, NULL, 0x0,
+ "Interface Output Packets", HFILL }
+ },
+ { &hf_sflow_ifoutmcast,
+ { "Output Multicast Packets", "sflow.ifoutmcast",
+ FT_UINT32, BASE_DEC, NULL, 0x0,
+ "Interface Output Multicast Packets", HFILL }
+ },
+ { &hf_sflow_ifoutbcast,
+ { "Output Broadcast Packets", "sflow.ifoutbcast",
+ FT_UINT32, BASE_DEC, NULL, 0x0,
+ "Interface Output Broadcast Packets", HFILL }
+ },
+ { &hf_sflow_ifoutdisc,
+ { "Output Discarded Packets", "sflow.ifoutdisc",
+ FT_UINT32, BASE_DEC, NULL, 0x0,
+ "Interface Output Discarded Packets", HFILL }
+ },
+ { &hf_sflow_ifouterr,
+ { "Output Errors", "sflow.ifouterr",
+ FT_UINT32, BASE_DEC, NULL, 0x0,
+ "Interface Output Errors", HFILL }
+ },
+ { &hf_sflow_ifpromisc,
+ { "Promiscuous Mode", "sflow.ifpromisc",
+ FT_UINT32, BASE_DEC, NULL, 0x0,
+ "Interface Promiscuous Mode", HFILL }
+ },
+ };
+
+/* Setup protocol subtree array */
+ static gint *ett[] = {
+ &ett_sflow,
+ &ett_sflow_sample,
+ &ett_sflow_extended_data,
+ &ett_sflow_sampled_header,
+ };
+
+/* Register the protocol name and description */
+ proto_sflow = proto_register_protocol("InMon sFlow",
+ "sFlow", "sflow");
+
+/* Required function calls to register the header fields and subtrees used */
+ proto_register_field_array(proto_sflow, hf, array_length(hf));
+ proto_register_subtree_array(ett, array_length(ett));
+}
+
+
+/* If this dissector uses sub-dissector registration add a registration routine.
+ This format is required because a script is used to find these routines and
+ create the code that calls these routines.
+*/
+void
+proto_reg_handoff_sflow(void)
+{
+ dissector_handle_t sflow_handle;
+
+ eth_handle = find_dissector("eth");
+ tr_handle = find_dissector("tr");
+ fddi_handle = find_dissector("fddi");
+ fr_handle = find_dissector("fr");
+ x25_handle = find_dissector("x25");
+ ppp_handle = find_dissector("ppp");
+ smds_handle = find_dissector("smds");
+ aal5_handle = find_dissector("atm");
+ ipv4_handle = find_dissector("ip");
+ ipv6_handle = find_dissector("ipv6");
+ mpls_handle = find_dissector("mpls");
+
+ sflow_handle = create_dissector_handle(dissect_sflow,
+ proto_sflow);
+ dissector_add("udp.port", UDP_PORT_SFLOW, sflow_handle);
+}