diff options
author | Gilbert Ramirez <gram@alumni.rice.edu> | 2004-07-18 18:06:47 +0000 |
---|---|---|
committer | Gilbert Ramirez <gram@alumni.rice.edu> | 2004-07-18 18:06:47 +0000 |
commit | 669db206cb1f270046ad400fff7655e20c63e723 (patch) | |
tree | 4eff24a2e16c8963e497e1fc575f35e6af59bd26 /epan/dissectors/packet-sflow.c | |
parent | ae46c27a38700af669ef907491081f09df6f6b2c (diff) |
Move dissectors to epan/dissectors directory.
Also move ncp222.py, x11-fields, process-x11-fields.pl,
make-reg-dotc, and make-reg-dotc.py.
Adjust #include lines in files that include packet-*.h
files.
svn path=/trunk/; revision=11410
Diffstat (limited to 'epan/dissectors/packet-sflow.c')
-rw-r--r-- | epan/dissectors/packet-sflow.c | 1068 |
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 *)ðc, 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); +} |