forked from osmocom/wireshark
483 lines
14 KiB
C
483 lines
14 KiB
C
/* packet-netflow.c
|
|
* Routines for Cisco NetFlow packet disassembly
|
|
* Matthew Smart <smart@monkey.org>
|
|
*
|
|
* $Id: packet-netflow.c,v 1.4 2002/09/09 20:22:51 guy Exp $
|
|
*
|
|
* 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.
|
|
*/
|
|
|
|
#ifdef HAVE_CONFIG_H
|
|
# include "config.h"
|
|
#endif
|
|
|
|
#include <glib.h>
|
|
#include <epan/packet.h>
|
|
|
|
#include <stdio.h>
|
|
#include <string.h>
|
|
|
|
#include "packet-netflow.h"
|
|
|
|
static int proto_netflow = -1;
|
|
static int hf_netflow_version = -1;
|
|
static int hf_netflow_count = -1;
|
|
static int hf_netflow_sys_uptime = -1;
|
|
static int hf_netflow_unix_sec = -1;
|
|
static int hf_netflow_unix_nsec = -1;
|
|
static int hf_netflow_sequence = -1;
|
|
static int hf_netflow_engine_type = -1;
|
|
static int hf_netflow_engine_id = -1;
|
|
static int hf_netflow_aggregation = -1;
|
|
static int hf_netflow_agg_version = -1;
|
|
static int hf_netflow_sample_rate = -1;
|
|
static int hf_netflow_record = -1;
|
|
|
|
static int hf_netflow_src_addr = -1;
|
|
static int hf_netflow_dst_addr = -1;
|
|
static int hf_netflow_next_hop = -1;
|
|
static int hf_netflow_input_iface = -1;
|
|
static int hf_netflow_output_iface = -1;
|
|
static int hf_netflow_packets = -1;
|
|
static int hf_netflow_bytes = -1;
|
|
static int hf_netflow_start_time = -1;
|
|
static int hf_netflow_end_time = -1;
|
|
static int hf_netflow_src_port = -1;
|
|
static int hf_netflow_dst_port = -1;
|
|
static int hf_netflow_v7_flags = -1;
|
|
static int hf_netflow_tcp_flags = -1;
|
|
static int hf_netflow_ip_prot = -1;
|
|
static int hf_netflow_tos = -1;
|
|
static int hf_netflow_src_as = -1;
|
|
static int hf_netflow_dst_as = -1;
|
|
static int hf_netflow_src_mask = -1;
|
|
static int hf_netflow_dst_mask = -1;
|
|
static int hf_netflow_router_sc = -1;
|
|
|
|
static gint ett_netflow = -1;
|
|
static gint ett_netflow_rec = -1;
|
|
|
|
static void
|
|
dissect_netflow_157(tvbuff_t *tvb, proto_tree *tree, guint16 version,
|
|
guint offset)
|
|
{
|
|
guint32 addr;
|
|
|
|
tvb_memcpy(tvb, (guint8 *)&addr, offset, 4);
|
|
proto_tree_add_ipv4(tree, hf_netflow_src_addr, tvb, offset, 4, addr);
|
|
offset += 4;
|
|
|
|
tvb_memcpy(tvb, (guint8 *)&addr, offset, 4);
|
|
proto_tree_add_ipv4(tree, hf_netflow_dst_addr, tvb, offset, 4, addr);
|
|
offset += 4;
|
|
|
|
tvb_memcpy(tvb, (guint8 *)&addr, offset, 4);
|
|
proto_tree_add_ipv4(tree, hf_netflow_next_hop, tvb, offset, 4, addr);
|
|
offset += 4;
|
|
|
|
proto_tree_add_item(tree, hf_netflow_input_iface,
|
|
tvb, offset, 2, FALSE);
|
|
offset += 2;
|
|
|
|
proto_tree_add_item(tree, hf_netflow_output_iface,
|
|
tvb, offset, 2, FALSE);
|
|
offset += 2;
|
|
|
|
proto_tree_add_item(tree, hf_netflow_packets,
|
|
tvb, offset, 4, FALSE);
|
|
offset += 4;
|
|
|
|
proto_tree_add_item(tree, hf_netflow_bytes,
|
|
tvb, offset, 4, FALSE);
|
|
offset += 4;
|
|
|
|
proto_tree_add_item(tree, hf_netflow_start_time,
|
|
tvb, offset, 4, FALSE);
|
|
offset += 4;
|
|
|
|
proto_tree_add_item(tree, hf_netflow_end_time,
|
|
tvb, offset, 4, FALSE);
|
|
offset += 4;
|
|
|
|
proto_tree_add_item(tree, hf_netflow_src_port,
|
|
tvb, offset, 2, FALSE);
|
|
offset += 2;
|
|
|
|
proto_tree_add_item(tree, hf_netflow_dst_port,
|
|
tvb, offset, 2, FALSE);
|
|
offset += 2;
|
|
|
|
if (version == 1) {
|
|
offset += 2; /* Skip pad bytes */
|
|
|
|
proto_tree_add_item(tree, hf_netflow_ip_prot,
|
|
tvb, offset, 1, FALSE);
|
|
offset += 1;
|
|
|
|
proto_tree_add_item(tree, hf_netflow_tos,
|
|
tvb, offset, 1, FALSE);
|
|
offset += 1;
|
|
|
|
proto_tree_add_item(tree, hf_netflow_tcp_flags,
|
|
tvb, offset, 1, FALSE);
|
|
offset += 1;
|
|
} else {
|
|
if (version == 7) {
|
|
proto_tree_add_item(tree, hf_netflow_v7_flags,
|
|
tvb, offset, 1, FALSE);
|
|
}
|
|
offset += 1; /* v5 pad byte, v7 flags */
|
|
|
|
proto_tree_add_item(tree, hf_netflow_tcp_flags,
|
|
tvb, offset, 1, FALSE);
|
|
offset += 1;
|
|
|
|
proto_tree_add_item(tree, hf_netflow_ip_prot,
|
|
tvb, offset, 1, FALSE);
|
|
offset += 1;
|
|
|
|
proto_tree_add_item(tree, hf_netflow_tos,
|
|
tvb, offset, 1, FALSE);
|
|
offset += 1;
|
|
|
|
proto_tree_add_item(tree, hf_netflow_src_as,
|
|
tvb, offset, 2, FALSE);
|
|
offset += 2;
|
|
|
|
proto_tree_add_item(tree, hf_netflow_dst_as,
|
|
tvb, offset, 2, FALSE);
|
|
offset += 2;
|
|
|
|
proto_tree_add_item(tree, hf_netflow_src_mask,
|
|
tvb, offset, 1, FALSE);
|
|
offset += 1;
|
|
|
|
proto_tree_add_item(tree, hf_netflow_dst_mask,
|
|
tvb, offset, 1, FALSE);
|
|
offset += 1;
|
|
|
|
offset += 2; /* Skip pad bytes */
|
|
|
|
if (version == 7) {
|
|
proto_tree_add_item(tree, hf_netflow_router_sc,
|
|
tvb, offset, 4, FALSE);
|
|
offset += 4;
|
|
}
|
|
}
|
|
}
|
|
|
|
static void
|
|
dissect_netflow(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree)
|
|
{
|
|
proto_tree *netflow_tree = NULL;
|
|
proto_tree *netflow_rec_tree = NULL;
|
|
proto_item *ti = NULL, *tf = NULL;
|
|
gint offset = 0;
|
|
guint16 nf_version, nf_count, nf_sample_rate;
|
|
guint32 nf_sequence;
|
|
gint header_size, record_size;
|
|
int i;
|
|
|
|
if (check_col(pinfo->cinfo, COL_PROTOCOL))
|
|
col_set_str(pinfo->cinfo, COL_PROTOCOL, "NetFlow");
|
|
if (check_col(pinfo->cinfo, COL_INFO))
|
|
col_clear(pinfo->cinfo, COL_INFO);
|
|
|
|
/* Determine NetFlow version and number of records */
|
|
nf_version = tvb_get_ntohs(tvb, offset);
|
|
offset += sizeof(nf_version);
|
|
|
|
nf_count = tvb_get_ntohs(tvb, offset);
|
|
offset += sizeof(nf_count);
|
|
|
|
if (check_col(pinfo->cinfo, COL_INFO))
|
|
col_add_fstr(pinfo->cinfo, COL_INFO,
|
|
"v%u, %u records", nf_version, nf_count);
|
|
|
|
/* Handle version-specific issues */
|
|
switch (nf_version) {
|
|
case 1:
|
|
header_size = NETFLOW_V1_HDR;
|
|
record_size = NETFLOW_V1_REC;
|
|
break;
|
|
case 5:
|
|
header_size = NETFLOW_V5_HDR;
|
|
record_size = NETFLOW_V5_REC;
|
|
break;
|
|
case 7:
|
|
header_size = NETFLOW_V7_HDR;
|
|
record_size = NETFLOW_V7_REC;
|
|
break;
|
|
case 8:
|
|
header_size = NETFLOW_V8_HDR;
|
|
record_size = NETFLOW_V8_REC;
|
|
case 9:
|
|
default:
|
|
return;
|
|
}
|
|
|
|
/* Add NetFlow to the tree */
|
|
if (tree != NULL) {
|
|
ti = proto_tree_add_protocol_format(tree, proto_netflow, tvb,
|
|
0, header_size, "NetFlow, v%u, %u records",
|
|
nf_version, nf_count);
|
|
netflow_tree = proto_item_add_subtree(ti, ett_netflow);
|
|
} else {
|
|
return;
|
|
}
|
|
|
|
/* Start adding header information */
|
|
offset = 0;
|
|
|
|
proto_tree_add_uint(netflow_tree, hf_netflow_version,
|
|
tvb, offset, sizeof(nf_version), nf_version);
|
|
offset += sizeof(nf_version);
|
|
|
|
proto_tree_add_uint(netflow_tree, hf_netflow_count,
|
|
tvb, offset, sizeof(nf_count), nf_count);
|
|
offset += sizeof(nf_count);
|
|
|
|
proto_tree_add_item(netflow_tree, hf_netflow_sys_uptime,
|
|
tvb, offset, 4, FALSE);
|
|
offset += 4;
|
|
|
|
proto_tree_add_item(netflow_tree, hf_netflow_unix_sec,
|
|
tvb, offset, 4, FALSE);
|
|
offset += 4;
|
|
|
|
proto_tree_add_item(netflow_tree, hf_netflow_unix_nsec,
|
|
tvb, offset, 4, FALSE);
|
|
offset += 4;
|
|
|
|
/* No more version 1 header */
|
|
|
|
if (nf_version != 1) {
|
|
nf_sequence = tvb_get_ntohl(tvb, offset);
|
|
proto_tree_add_uint(netflow_tree, hf_netflow_sequence,
|
|
tvb, offset, sizeof(nf_sequence), nf_sequence);
|
|
offset += sizeof(nf_sequence);
|
|
|
|
/* Add the sequence number */
|
|
if (check_col(pinfo->cinfo, COL_INFO)) {
|
|
col_clear(pinfo->cinfo, COL_INFO);
|
|
col_add_fstr(pinfo->cinfo, COL_INFO,
|
|
"v%u, %u records, sequence # %u",
|
|
nf_version, nf_count, nf_sequence);
|
|
}
|
|
|
|
/* No more version 7 header */
|
|
|
|
if (nf_version != 7) {
|
|
/* Engine type and ID */
|
|
proto_tree_add_item(netflow_tree,
|
|
hf_netflow_engine_type, tvb, offset,
|
|
1, FALSE);
|
|
offset += 1;
|
|
|
|
proto_tree_add_item(netflow_tree,
|
|
hf_netflow_engine_id, tvb, offset,
|
|
1, FALSE);
|
|
offset += 1;
|
|
|
|
if (nf_version == 8) {
|
|
/* Engine type and ID */
|
|
proto_tree_add_item(netflow_tree,
|
|
hf_netflow_aggregation, tvb, offset,
|
|
1, FALSE);
|
|
offset += 1;
|
|
|
|
proto_tree_add_item(netflow_tree,
|
|
hf_netflow_agg_version, tvb, offset,
|
|
1, FALSE);
|
|
offset += 1;
|
|
}
|
|
|
|
/*
|
|
* On high-speed interfaces often just
|
|
* statistical sample records are produced.
|
|
*/
|
|
nf_sample_rate = tvb_get_ntohs(tvb, offset);
|
|
if (nf_version == 5) {
|
|
/*
|
|
* Sample rate. Junipers and some Ciscos
|
|
* include sampling rate in the reserved
|
|
* header field. Not all the bits are used,
|
|
* however.
|
|
*/
|
|
if ((nf_sample_rate & 0xc000) == 0x4000) {
|
|
nf_sample_rate &= 0x3fff;
|
|
if (nf_sample_rate == 0)
|
|
nf_sample_rate = 1;
|
|
} else
|
|
nf_sample_rate = 1;
|
|
}
|
|
proto_tree_add_uint_format(netflow_tree,
|
|
hf_netflow_sample_rate, tvb, offset,
|
|
sizeof(nf_sample_rate), nf_sample_rate,
|
|
"Sample_rate: 1/%u", nf_sample_rate);
|
|
offset += sizeof(nf_sample_rate);
|
|
}
|
|
}
|
|
|
|
/* XXX Doesn't support v8 records, yet */
|
|
if (nf_version == 8)
|
|
return;
|
|
|
|
/* Handle the flow records */
|
|
for (i = 0; i < nf_count; i++) {
|
|
guint rec_offset = header_size + i * record_size;
|
|
|
|
tf = proto_tree_add_uint_format(netflow_tree,
|
|
hf_netflow_record, tvb, rec_offset, record_size,
|
|
i, "Record %d: %u packets, %u bytes", i + 1,
|
|
tvb_get_ntohl(tvb, rec_offset + 16),
|
|
tvb_get_ntohl(tvb, rec_offset + 20));
|
|
netflow_rec_tree = proto_item_add_subtree(tf,
|
|
ett_netflow_rec);
|
|
|
|
dissect_netflow_157(tvb, netflow_rec_tree,
|
|
nf_version, rec_offset);
|
|
}
|
|
}
|
|
|
|
void
|
|
proto_register_netflow(void)
|
|
{
|
|
static hf_register_info hf[] = {
|
|
/* Header */
|
|
{ &hf_netflow_version,
|
|
{ "Version", "netflow.version", FT_UINT16,
|
|
BASE_DEC, NULL, 0x0, "", HFILL }},
|
|
{ &hf_netflow_count,
|
|
{ "Number of records", "netflow.count", FT_UINT16,
|
|
BASE_DEC, NULL, 0x0, "", HFILL }},
|
|
{ &hf_netflow_sys_uptime,
|
|
{ "System uptime", "netflow.sys_uptime", FT_UINT32,
|
|
BASE_DEC, NULL, 0x0, "", HFILL }},
|
|
{ &hf_netflow_unix_sec,
|
|
{ "Unix seconds", "netflow.unix_sec", FT_UINT32,
|
|
BASE_DEC, NULL, 0x0, "", HFILL }},
|
|
{ &hf_netflow_unix_nsec,
|
|
{ "Unix nanonseconds", "netflow.unix_nsec", FT_UINT32,
|
|
BASE_DEC, NULL, 0x0, "", HFILL }},
|
|
{ &hf_netflow_sequence,
|
|
{ "Sequence number", "netflow.sequence", FT_UINT32,
|
|
BASE_DEC, NULL, 0x0, "", HFILL }},
|
|
{ &hf_netflow_engine_type,
|
|
{ "Engine type", "netflow.engine_type", FT_UINT8,
|
|
BASE_DEC, NULL, 0x0, "", HFILL }},
|
|
{ &hf_netflow_engine_id,
|
|
{ "Engine ID", "netflow.engine_id", FT_UINT8,
|
|
BASE_DEC, NULL, 0x0, "", HFILL }},
|
|
{ &hf_netflow_aggregation,
|
|
{ "Aggregation method", "netflow.aggregation", FT_UINT8,
|
|
BASE_DEC, NULL, 0x0, "", HFILL }},
|
|
{ &hf_netflow_agg_version,
|
|
{ "Aggregation version", "netflow.agg_version", FT_UINT8,
|
|
BASE_DEC, NULL, 0x0, "", HFILL }},
|
|
{ &hf_netflow_sample_rate,
|
|
{ "Sample rate", "netflow.sample_rate", FT_UINT16,
|
|
BASE_DEC, NULL, 0x0, "", HFILL }},
|
|
{ &hf_netflow_record,
|
|
{ "Record", "netflow.record", FT_UINT32,
|
|
BASE_DEC, NULL, 0x0, "", HFILL }},
|
|
/* Record */
|
|
{ &hf_netflow_src_addr,
|
|
{ "Source address", "netflow.src_addr", FT_IPv4,
|
|
BASE_NONE, NULL, 0x0, "", HFILL }},
|
|
{ &hf_netflow_dst_addr,
|
|
{ "Destination address", "netflow.dst_addr", FT_IPv4,
|
|
BASE_NONE, NULL, 0x0, "", HFILL }},
|
|
{ &hf_netflow_next_hop,
|
|
{ "Next hop", "netflow.next_hop", FT_IPv4,
|
|
BASE_NONE, NULL, 0x0, "", HFILL }},
|
|
{ &hf_netflow_input_iface,
|
|
{ "Input interface", "netflow.input_iface", FT_UINT16,
|
|
BASE_DEC, NULL, 0x0, "", HFILL }},
|
|
{ &hf_netflow_output_iface,
|
|
{ "Output interface", "netflow.output_iface", FT_UINT16,
|
|
BASE_DEC, NULL, 0x0, "", HFILL }},
|
|
{ &hf_netflow_packets,
|
|
{ "Packets sent", "netflow.packets", FT_UINT32,
|
|
BASE_DEC, NULL, 0x0, "", HFILL }},
|
|
{ &hf_netflow_bytes,
|
|
{ "Bytes sent", "netflow.bytes", FT_UINT32,
|
|
BASE_DEC, NULL, 0x0, "", HFILL }},
|
|
{ &hf_netflow_start_time,
|
|
{ "Start time", "netflow.start_time", FT_UINT32,
|
|
BASE_DEC, NULL, 0x0, "", HFILL }},
|
|
{ &hf_netflow_end_time,
|
|
{ "End time", "netflow.end_time", FT_UINT32,
|
|
BASE_DEC, NULL, 0x0, "", HFILL }},
|
|
{ &hf_netflow_src_port,
|
|
{ "Source port", "netflow.src_port", FT_UINT16,
|
|
BASE_DEC, NULL, 0x0, "", HFILL }},
|
|
{ &hf_netflow_dst_port,
|
|
{ "Destination port", "netflow.dst_port", FT_UINT16,
|
|
BASE_DEC, NULL, 0x0, "", HFILL }},
|
|
{ &hf_netflow_v7_flags,
|
|
{ "Valid flags", "netflow.flags", FT_UINT8,
|
|
BASE_HEX, NULL, 0x0, "", HFILL }},
|
|
{ &hf_netflow_tcp_flags,
|
|
{ "TCP flags", "netflow.tcp_flags", FT_UINT8,
|
|
BASE_HEX, NULL, 0x0, "", HFILL }},
|
|
{ &hf_netflow_ip_prot,
|
|
{ "IP protocol", "netflow.ip_prot", FT_UINT8,
|
|
BASE_DEC, NULL, 0x0, "", HFILL }},
|
|
{ &hf_netflow_tos,
|
|
{ "Type of service", "netflow.tos", FT_UINT8,
|
|
BASE_HEX, NULL, 0x0, "", HFILL }},
|
|
{ &hf_netflow_src_as,
|
|
{ "Source AS", "netflow.src_as", FT_UINT16,
|
|
BASE_DEC, NULL, 0x0, "", HFILL }},
|
|
{ &hf_netflow_dst_as,
|
|
{ "Destination AS", "netflow.dst_as", FT_UINT16,
|
|
BASE_DEC, NULL, 0x0, "", HFILL }},
|
|
{ &hf_netflow_src_mask,
|
|
{ "Source mask", "netflow.src_mask", FT_UINT8,
|
|
BASE_DEC, NULL, 0x0, "", HFILL }},
|
|
{ &hf_netflow_dst_mask,
|
|
{ "Destination mask", "netflow.dst_mask", FT_UINT8,
|
|
BASE_DEC, NULL, 0x0, "", HFILL }},
|
|
{ &hf_netflow_router_sc,
|
|
{ "Router bypass", "netflow.router_sc", FT_IPv4,
|
|
BASE_NONE, NULL, 0x0, "", HFILL }},
|
|
};
|
|
|
|
static gint *ett[] = {
|
|
&ett_netflow,
|
|
&ett_netflow_rec
|
|
};
|
|
|
|
proto_netflow = proto_register_protocol("NetFlow",
|
|
"NetFlow", "netflow");
|
|
proto_register_field_array(proto_netflow, hf, array_length(hf));
|
|
proto_register_subtree_array(ett, array_length(ett));
|
|
}
|
|
|
|
void
|
|
proto_reg_handoff_netflow(void)
|
|
{
|
|
dissector_handle_t netflow_handle;
|
|
|
|
netflow_handle = create_dissector_handle(dissect_netflow,
|
|
proto_netflow);
|
|
dissector_add("udp.port", UDP_PORT_NETFLOW, netflow_handle);
|
|
}
|