/* ** packet-netflow.c ** ***************************************************************************** ** (c) 2002 bill fumerola ** All rights reserved. ** ** 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. ***************************************************************************** ** ** Previous NetFlow dissector written by Matthew Smart ** NetFlow v9 support added by same. ** ** See ** ** http://www.cisco.com/warp/public/cc/pd/iosw/prodlit/tflow_wp.htm ** ** for NetFlow v9 information. ** ***************************************************************************** ** ** this code was written from the following documentation: ** ** http://www.cisco.com/univercd/cc/td/doc/product/rtrmgmt/nfc/nfc_3_6/iug/format.pdf ** http://www.caida.org/tools/measurement/cflowd/configuration/configuration-9.html ** ** some documentation is more accurate then others. in some cases, live data and ** information contained in responses from vendors were also used. some fields ** are dissected as vendor specific fields. ** ** See also ** ** http://www.cisco.com/univercd/cc/td/doc/cisintwk/intsolns/netflsol/nfwhite.htm ** ** $Yahoo: //depot/fumerola/packet-netflow/packet-netflow.c#14 $ ** $Id: packet-netflow.c,v 1.10 2003/03/07 00:43:30 guy Exp $ */ #ifdef HAVE_CONFIG_H # include "config.h" #endif #include #include #include #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 */ #define V1PDU_SIZE (4 * 12) #define V5PDU_SIZE (4 * 12) #define V7PDU_SIZE (4 * 13) #define V8PDU_AS_SIZE (4 * 7) #define V8PDU_PROTO_SIZE (4 * 7) #define V8PDU_SPREFIX_SIZE (4 * 8) #define V8PDU_DPREFIX_SIZE (4 * 8) #define V8PDU_MATRIX_SIZE (4 * 10) #define V8PDU_DESTONLY_SIZE (4 * 8) #define V8PDU_SRCDEST_SIZE (4 * 10) #define V8PDU_FULL_SIZE (4 * 11) #define V8PDU_TOSAS_SIZE (V8PDU_AS_SIZE + 4) #define V8PDU_TOSPROTOPORT_SIZE (V8PDU_PROTO_SIZE + 4) #define V8PDU_TOSSRCPREFIX_SIZE V8PDU_SPREFIX_SIZE #define V8PDU_TOSDSTPREFIX_SIZE V8PDU_DPREFIX_SIZE #define V8PDU_TOSMATRIX_SIZE V8PDU_MATRIX_SIZE #define V8PDU_PREPORTPROTOCOL_SIZE (4 * 10) enum { V8PDU_NO_METHOD = 0, V8PDU_AS_METHOD, V8PDU_PROTO_METHOD, V8PDU_SPREFIX_METHOD, V8PDU_DPREFIX_METHOD, V8PDU_MATRIX_METHOD, V8PDU_DESTONLY_METHOD, V8PDU_SRCDEST_METHOD, V8PDU_FULL_METHOD, V8PDU_TOSAS_METHOD, V8PDU_TOSPROTOPORT_METHOD, V8PDU_TOSSRCPREFIX_METHOD, V8PDU_TOSDSTPREFIX_METHOD, V8PDU_TOSMATRIX_METHOD, V8PDU_PREPORTPROTOCOL_METHOD }; static const value_string v8_agg[] = { {V8PDU_AS_METHOD, "V8 AS aggregation"}, {V8PDU_PROTO_METHOD, "V8 Proto/Port aggregation"}, {V8PDU_SPREFIX_METHOD, "V8 Source Prefix aggregation"}, {V8PDU_DPREFIX_METHOD, "V8 Destination Prefix aggregation"}, {V8PDU_MATRIX_METHOD, "V8 Network Matrix aggregation"}, {V8PDU_DESTONLY_METHOD, "V8 Destination aggregation (Cisco Catalyst)"}, {V8PDU_SRCDEST_METHOD, "V8 Src/Dest aggregation (Cisco Catalyst)"}, {V8PDU_FULL_METHOD, "V8 Full aggregation (Cisco Catalyst)"}, {V8PDU_TOSAS_METHOD, "V8 TOS+AS aggregation aggregation"}, {V8PDU_TOSPROTOPORT_METHOD, "V8 TOS+Protocol aggregation"}, {V8PDU_TOSSRCPREFIX_METHOD, "V8 TOS+Source Prefix aggregation"}, {V8PDU_TOSDSTPREFIX_METHOD, "V8 TOS+Destination Prefix aggregation"}, {V8PDU_TOSMATRIX_METHOD, "V8 TOS+Prefix Matrix aggregation"}, {V8PDU_PREPORTPROTOCOL_METHOD, "V8 Port+Protocol aggregation"}, {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 */ static int proto_netflow = -1; static int ett_netflow = -1; static int ett_unixtime = -1; static int ett_flow = -1; static int ett_template = -1; static int ett_dataflowset = -1; /* * cflow header */ static int hf_cflow_version = -1; static int hf_cflow_count = -1; static int hf_cflow_sysuptime = -1; static int hf_cflow_unix_secs = -1; static int hf_cflow_unix_nsecs = -1; static int hf_cflow_timestamp = -1; static int hf_cflow_samplerate = -1; /* * cflow version specific info */ static int hf_cflow_sequence = -1; static int hf_cflow_engine_type = -1; static int hf_cflow_engine_id = -1; static int hf_cflow_source_id = -1; static int hf_cflow_aggmethod = -1; static int hf_cflow_aggversion = -1; /* Version 9 */ static int hf_cflow_template_flowset_id = -1; static int hf_cflow_data_flowset_id = -1; static int hf_cflow_options_flowset_id = -1; static int hf_cflow_flowset_id = -1; static int hf_cflow_flowset_length = -1; static int hf_cflow_template_id = -1; static int hf_cflow_template_field_count = -1; static int hf_cflow_template_field_type = -1; static int hf_cflow_template_field_length = -1; /* * pdu storage */ 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; static int hf_cflow_dstport = -1; static int hf_cflow_prot = -1; static int hf_cflow_tos = -1; static int hf_cflow_flags = -1; static int hf_cflow_tcpflags = -1; static int hf_cflow_dstas = -1; static int hf_cflow_srcas = -1; 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); static int dissect_pdu(proto_tree * tree, tvbuff_t * tvb, int offset, int verspec); static int dissect_v8_aggpdu(proto_tree * pdutree, tvbuff_t * tvb, int offset, int verspec); static int dissect_v8_flowpdu(proto_tree * pdutree, tvbuff_t * tvb, int offset, int verspec); 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, guint16 id, guint 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 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, proto_tree * tree); static int flow_process_ints(proto_tree * pdutree, tvbuff_t * tvb, int offset); static int flow_process_ports(proto_tree * pdutree, tvbuff_t * tvb, int offset); static int flow_process_timeperiod(proto_tree * pdutree, tvbuff_t * tvb, int offset); static int flow_process_aspair(proto_tree * pdutree, tvbuff_t * tvb, int offset); static int flow_process_sizecount(proto_tree * pdutree, tvbuff_t * tvb, int offset); static int flow_process_textfield(proto_tree * pdutree, tvbuff_t * tvb, int offset, int bytes, const char *text); static void dissect_netflow(tvbuff_t * tvb, packet_info * pinfo, proto_tree * tree) { proto_tree *netflow_tree = NULL; proto_tree *ti; proto_item *timeitem, *pduitem; proto_tree *timetree, *pdutree; unsigned int pduret, ver = 0, pdus = 0, x = 1, vspec; size_t available, pdusize, offset = 0; nstime_t ts; dissect_pdu_t *pduptr; if (check_col(pinfo->cinfo, COL_PROTOCOL)) col_set_str(pinfo->cinfo, COL_PROTOCOL, "CFLOW"); if (check_col(pinfo->cinfo, COL_INFO)) col_clear(pinfo->cinfo, COL_INFO); if (tree) { ti = proto_tree_add_item(tree, proto_netflow, tvb, offset, -1, FALSE); netflow_tree = proto_item_add_subtree(ti, ett_netflow); } ver = tvb_get_ntohs(tvb, offset); vspec = ver; switch (ver) { case 1: pdusize = V1PDU_SIZE; pduptr = &dissect_pdu; break; case 5: pdusize = V5PDU_SIZE; pduptr = &dissect_pdu; break; case 7: pdusize = V7PDU_SIZE; pduptr = &dissect_pdu; break; case 8: pdusize = -1; /* deferred */ pduptr = &dissect_v8_aggpdu; break; case 9: pdusize = -1; /* deferred */ pduptr = &dissect_v9_flowset; break; default: return; } if (tree) proto_tree_add_uint(netflow_tree, hf_cflow_version, tvb, offset, 2, ver); offset += 2; pdus = tvb_get_ntohs(tvb, offset); if (pdus <= 0) return; if (tree) proto_tree_add_uint(netflow_tree, hf_cflow_count, tvb, offset, 2, pdus); offset += 2; /* * set something interesting in the display now that we have info */ if (check_col(pinfo->cinfo, COL_INFO)) { if (ver == 9) { col_add_fstr(pinfo->cinfo, COL_INFO, "total: %u (v%u) FlowSets", pdus, ver); } else { col_add_fstr(pinfo->cinfo, COL_INFO, "total: %u (v%u) flows", pdus, ver); } } /* * the rest is only interesting if we're displaying/searching the * packet */ if (!tree) return; proto_tree_add_item(netflow_tree, hf_cflow_sysuptime, tvb, offset, 4, FALSE); offset += 4; ts.secs = tvb_get_ntohl(tvb, offset); ts.nsecs = tvb_get_ntohl(tvb, offset + 4); timeitem = proto_tree_add_time(netflow_tree, hf_cflow_timestamp, tvb, offset, 8, &ts); timetree = proto_item_add_subtree(timeitem, ett_unixtime); proto_tree_add_item(timetree, hf_cflow_unix_secs, tvb, offset, 4, FALSE); offset += 4; if (ver != 9) { proto_tree_add_item(timetree, hf_cflow_unix_nsecs, tvb, offset, 4, FALSE); offset += 4; } /* * version specific header */ if (ver == 5 || ver == 7 || ver == 8 || ver == 9) { proto_tree_add_item(netflow_tree, hf_cflow_sequence, tvb, offset, 4, FALSE); offset += 4; } if (ver == 5 || ver == 8) { proto_tree_add_item(netflow_tree, hf_cflow_engine_type, tvb, offset++, 1, FALSE); proto_tree_add_item(netflow_tree, hf_cflow_engine_id, tvb, offset++, 1, FALSE); } else if (ver == 9) { proto_tree_add_item(netflow_tree, hf_cflow_source_id, tvb, offset, 4, FALSE); offset += 4; } if (ver == 8) { vspec = tvb_get_guint8(tvb, offset); switch (vspec) { case V8PDU_AS_METHOD: pdusize = V8PDU_AS_SIZE; break; case V8PDU_PROTO_METHOD: pdusize = V8PDU_PROTO_SIZE; break; case V8PDU_SPREFIX_METHOD: pdusize = V8PDU_SPREFIX_SIZE; break; case V8PDU_DPREFIX_METHOD: pdusize = V8PDU_DPREFIX_SIZE; break; case V8PDU_MATRIX_METHOD: pdusize = V8PDU_MATRIX_SIZE; break; case V8PDU_DESTONLY_METHOD: pdusize = V8PDU_DESTONLY_SIZE; pduptr = &dissect_v8_flowpdu; break; case V8PDU_SRCDEST_METHOD: pdusize = V8PDU_SRCDEST_SIZE; pduptr = &dissect_v8_flowpdu; break; case V8PDU_FULL_METHOD: pdusize = V8PDU_FULL_SIZE; pduptr = &dissect_v8_flowpdu; break; case V8PDU_TOSAS_METHOD: pdusize = V8PDU_TOSAS_SIZE; break; case V8PDU_TOSPROTOPORT_METHOD: pdusize = V8PDU_TOSPROTOPORT_SIZE; break; case V8PDU_TOSSRCPREFIX_METHOD: pdusize = V8PDU_TOSSRCPREFIX_SIZE; break; case V8PDU_TOSDSTPREFIX_METHOD: pdusize = V8PDU_TOSDSTPREFIX_SIZE; break; case V8PDU_TOSMATRIX_METHOD: pdusize = V8PDU_TOSMATRIX_SIZE; break; case V8PDU_PREPORTPROTOCOL_METHOD: pdusize = V8PDU_PREPORTPROTOCOL_SIZE; break; default: pdusize = -1; vspec = 0; break; } proto_tree_add_uint(netflow_tree, hf_cflow_aggmethod, tvb, offset++, 1, vspec); proto_tree_add_item(netflow_tree, hf_cflow_aggversion, tvb, offset++, 1, FALSE); } if (ver == 7 || ver == 8) offset = flow_process_textfield(netflow_tree, tvb, offset, 4, "reserved"); else if (ver == 5) { proto_tree_add_item(netflow_tree, hf_cflow_samplerate, tvb, offset, 2, FALSE); offset += 2; } /* * everything below here should be payload */ for (x = 1; x < pdus + 1; x++) { /* * make sure we have a pdu's worth of data */ available = tvb_length_remaining(tvb, offset); if (ver == 9 && available >= 4) { /* pdusize can be different for each v9 flowset */ pdusize = tvb_get_ntohs(tvb, offset + 2); } if (available < pdusize) break; if (ver == 9) { pduitem = proto_tree_add_text(netflow_tree, tvb, offset, pdusize, "FlowSet %u/%u", x, pdus); } else { pduitem = proto_tree_add_text(netflow_tree, tvb, offset, pdusize, "pdu %u/%u", x, pdus); } pdutree = proto_item_add_subtree(pduitem, ett_flow); pduret = pduptr(pdutree, tvb, offset, vspec); /* * if we came up short, stop processing */ if (pduret == pdusize) offset += pduret; else break; } } /* * flow_process_* == common groups of fields, probably could be inline */ static int flow_process_ints(proto_tree * pdutree, tvbuff_t * tvb, int offset) { proto_tree_add_item(pdutree, hf_cflow_inputint, tvb, offset, 2, FALSE); offset += 2; proto_tree_add_item(pdutree, hf_cflow_outputint, tvb, offset, 2, FALSE); offset += 2; return offset; } static int flow_process_ports(proto_tree * pdutree, tvbuff_t * tvb, int offset) { proto_tree_add_item(pdutree, hf_cflow_srcport, tvb, offset, 2, FALSE); offset += 2; proto_tree_add_item(pdutree, hf_cflow_dstport, tvb, offset, 2, FALSE); offset += 2; return offset; } static int flow_process_timeperiod(proto_tree * pdutree, tvbuff_t * tvb, int offset) { nstime_t ts; ts.secs = tvb_get_ntohl(tvb, offset) / 1000; ts.nsecs = ((tvb_get_ntohl(tvb, offset) % 1000) * 1000000); proto_tree_add_time(pdutree, hf_cflow_timestart, tvb, offset, 4, &ts); offset += 4; ts.secs = tvb_get_ntohl(tvb, offset) / 1000; ts.nsecs = ((tvb_get_ntohl(tvb, offset) % 1000) * 1000000); proto_tree_add_time(pdutree, hf_cflow_timeend, tvb, offset, 4, &ts); offset += 4; return offset; } static int flow_process_aspair(proto_tree * pdutree, tvbuff_t * tvb, int offset) { proto_tree_add_item(pdutree, hf_cflow_srcas, tvb, offset, 2, FALSE); offset += 2; proto_tree_add_item(pdutree, hf_cflow_dstas, tvb, offset, 2, FALSE); offset += 2; return offset; } static int flow_process_sizecount(proto_tree * pdutree, tvbuff_t * tvb, int offset) { proto_tree_add_item(pdutree, hf_cflow_packets, tvb, offset, 4, FALSE); offset += 4; proto_tree_add_item(pdutree, hf_cflow_octets, tvb, offset, 4, FALSE); offset += 4; return offset; } static int flow_process_textfield(proto_tree * pdutree, tvbuff_t * tvb, int offset, int bytes, const char *text) { proto_tree_add_text(pdutree, tvb, offset, bytes, text); offset += bytes; return offset; } static int dissect_v8_flowpdu(proto_tree * pdutree, tvbuff_t * tvb, int offset, int verspec) { int startoffset = offset; proto_tree_add_item(pdutree, hf_cflow_dstaddr, tvb, offset, 4, FALSE); offset += 4; if (verspec != V8PDU_DESTONLY_METHOD) { proto_tree_add_item(pdutree, hf_cflow_srcaddr, tvb, offset, 4, FALSE); offset += 4; } if (verspec == V8PDU_FULL_METHOD) { proto_tree_add_item(pdutree, hf_cflow_dstport, tvb, offset, 2, FALSE); offset += 2; proto_tree_add_item(pdutree, hf_cflow_srcport, tvb, offset, 2, FALSE); offset += 2; } offset = flow_process_sizecount(pdutree, tvb, offset); offset = flow_process_timeperiod(pdutree, tvb, offset); proto_tree_add_item(pdutree, hf_cflow_outputint, tvb, offset, 2, FALSE); offset += 2; if (verspec != V8PDU_DESTONLY_METHOD) { proto_tree_add_item(pdutree, hf_cflow_inputint, tvb, offset, 2, FALSE); offset += 2; } proto_tree_add_item(pdutree, hf_cflow_tos, tvb, offset++, 1, FALSE); if (verspec == V8PDU_FULL_METHOD) proto_tree_add_item(pdutree, hf_cflow_prot, tvb, offset++, 1, FALSE); offset = flow_process_textfield(pdutree, tvb, offset, 1, "marked tos"); if (verspec == V8PDU_SRCDEST_METHOD) offset = flow_process_textfield(pdutree, tvb, offset, 2, "reserved"); else if (verspec == V8PDU_FULL_METHOD) offset = flow_process_textfield(pdutree, tvb, offset, 1, "padding"); offset = flow_process_textfield(pdutree, tvb, offset, 4, "extra packets"); proto_tree_add_item(pdutree, hf_cflow_routersc, tvb, offset, 4, FALSE); offset += 4; return (offset - startoffset); } /* * dissect a version 8 pdu, returning the length of the pdu processed */ static int dissect_v8_aggpdu(proto_tree * pdutree, tvbuff_t * tvb, int offset, int verspec) { int startoffset = offset; proto_tree_add_item(pdutree, hf_cflow_flows, tvb, offset, 4, FALSE); offset += 4; offset = flow_process_sizecount(pdutree, tvb, offset); offset = flow_process_timeperiod(pdutree, tvb, offset); switch (verspec) { case V8PDU_AS_METHOD: case V8PDU_TOSAS_METHOD: offset = flow_process_aspair(pdutree, tvb, offset); if (verspec == V8PDU_TOSAS_METHOD) { proto_tree_add_item(pdutree, hf_cflow_tos, tvb, offset++, 1, FALSE); offset = flow_process_textfield(pdutree, tvb, offset, 1, "padding"); offset = flow_process_textfield(pdutree, tvb, offset, 2, "reserved"); } break; case V8PDU_PROTO_METHOD: case V8PDU_TOSPROTOPORT_METHOD: proto_tree_add_item(pdutree, hf_cflow_prot, tvb, offset++, 1, FALSE); if (verspec == V8PDU_PROTO_METHOD) offset = flow_process_textfield(pdutree, tvb, offset, 1, "padding"); else if (verspec == V8PDU_TOSPROTOPORT_METHOD) proto_tree_add_item(pdutree, hf_cflow_tos, tvb, offset++, 1, FALSE); offset = flow_process_textfield(pdutree, tvb, offset, 2, "reserved"); offset = flow_process_ports(pdutree, tvb, offset); if (verspec == V8PDU_TOSPROTOPORT_METHOD) offset = flow_process_ints(pdutree, tvb, offset); break; case V8PDU_SPREFIX_METHOD: case V8PDU_DPREFIX_METHOD: case V8PDU_TOSSRCPREFIX_METHOD: case V8PDU_TOSDSTPREFIX_METHOD: proto_tree_add_item(pdutree, verspec == V8PDU_SPREFIX_METHOD ? hf_cflow_srcnet : hf_cflow_dstnet, tvb, offset, 4, FALSE); offset += 4; proto_tree_add_item(pdutree, verspec == V8PDU_SPREFIX_METHOD ? hf_cflow_srcmask : hf_cflow_dstmask, tvb, offset++, 1, FALSE); if (verspec == V8PDU_SPREFIX_METHOD || verspec == V8PDU_DPREFIX_METHOD) offset = flow_process_textfield(pdutree, tvb, offset, 1, "padding"); else if (verspec == V8PDU_TOSSRCPREFIX_METHOD || verspec == V8PDU_TOSDSTPREFIX_METHOD) proto_tree_add_item(pdutree, hf_cflow_tos, tvb, offset++, 1, FALSE); proto_tree_add_item(pdutree, verspec == V8PDU_SPREFIX_METHOD ? hf_cflow_srcas : hf_cflow_dstas, tvb, offset, 2, FALSE); offset += 2; proto_tree_add_item(pdutree, verspec == V8PDU_SPREFIX_METHOD ? hf_cflow_inputint : hf_cflow_outputint, tvb, offset, 2, FALSE); offset += 2; offset = flow_process_textfield(pdutree, tvb, offset, 2, "reserved"); break; case V8PDU_MATRIX_METHOD: case V8PDU_TOSMATRIX_METHOD: case V8PDU_PREPORTPROTOCOL_METHOD: proto_tree_add_item(pdutree, hf_cflow_srcnet, tvb, offset, 4, FALSE); offset += 4; proto_tree_add_item(pdutree, hf_cflow_dstnet, tvb, offset, 4, FALSE); offset += 4; proto_tree_add_item(pdutree, hf_cflow_srcmask, tvb, offset++, 1, FALSE); proto_tree_add_item(pdutree, hf_cflow_dstmask, tvb, offset++, 1, FALSE); if (verspec == V8PDU_TOSMATRIX_METHOD || verspec == V8PDU_PREPORTPROTOCOL_METHOD) { proto_tree_add_item(pdutree, hf_cflow_tos, tvb, offset++, 1, FALSE); if (verspec == V8PDU_TOSMATRIX_METHOD) { offset = flow_process_textfield(pdutree, tvb, offset, 1, "padding"); } else if (verspec == V8PDU_PREPORTPROTOCOL_METHOD) { proto_tree_add_item(pdutree, hf_cflow_prot, tvb, offset++, 1, FALSE); } } else { offset = flow_process_textfield(pdutree, tvb, offset, 2, "reserved"); } if (verspec == V8PDU_MATRIX_METHOD || verspec == V8PDU_TOSMATRIX_METHOD) { offset = flow_process_aspair(pdutree, tvb, offset); } else if (verspec == V8PDU_PREPORTPROTOCOL_METHOD) { offset = flow_process_ports(pdutree, tvb, offset); } offset = flow_process_ints(pdutree, tvb, offset); break; } return (offset - startoffset); } /* Dissect a version 9 FlowSet and return the length we processed. */ static int dissect_v9_flowset(proto_tree * pdutree, tvbuff_t * tvb, int offset, int ver) { int length; guint16 flowset_id; if (ver != 9) return (0); flowset_id = tvb_get_ntohs(tvb, offset); if (flowset_id == 0) { /* Template */ proto_tree_add_item(pdutree, hf_cflow_template_flowset_id, tvb, offset, 2, FALSE); offset += 2; length = tvb_get_ntohs(tvb, offset); proto_tree_add_item(pdutree, hf_cflow_flowset_length, tvb, offset, 2, FALSE); offset += 2; dissect_v9_template(pdutree, tvb, offset); } else if (flowset_id == 1) { /* Options */ proto_tree_add_item(pdutree, hf_cflow_options_flowset_id, tvb, offset, 2, FALSE); offset += 2; length = tvb_get_ntohs(tvb, offset); proto_tree_add_item(pdutree, hf_cflow_flowset_length, tvb, offset, 2, FALSE); offset += 2; /* dissect_v9_options(pdutree, tvb, offset); */ } else if (flowset_id >= 2 && flowset_id <= 255) { /* Reserved */ proto_tree_add_item(pdutree, hf_cflow_flowset_id, tvb, offset, 2, FALSE); offset += 2; length = tvb_get_ntohs(tvb, offset); proto_tree_add_item(pdutree, hf_cflow_flowset_length, tvb, offset, 2, FALSE); offset += 2; } else { /* Data */ proto_tree_add_item(pdutree, hf_cflow_data_flowset_id, tvb, offset, 2, FALSE); offset += 2; length = tvb_get_ntohs(tvb, offset); proto_tree_add_item(pdutree, hf_cflow_flowset_length, tvb, offset, 2, FALSE); offset += 2; /* * The length includes the length of the FlowSet ID and * the length field itself. */ length -= 4; if (length > 0) { dissect_v9_data(pdutree, tvb, offset, flowset_id, (guint)length); } } return (length); } static int dissect_v9_data(proto_tree * pdutree, tvbuff_t * tvb, int offset, guint16 id, guint 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 >= template->length) { 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; } if (length != 0) { proto_tree_add_text(pdutree, tvb, offset, length, "Padding (%u byte%s)", length, plurality(length, "", "s")); } } else { proto_tree_add_text(pdutree, tvb, offset, length, "Data (%u byte%s), no template found", length, plurality(length, "", "s")); } 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) { return (0); } #endif 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 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; count = tvb_get_ntohs(tvb, offset); proto_tree_add_item(pdutree, hf_cflow_template_field_count, tvb, 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; type = tvb_get_ntohs(tvb, offset); length = tvb_get_ntohs(tvb, offset + 2); template_item = proto_tree_add_text(pdutree, tvb, offset, 4, "Field (%u/%u)", i, count); template_tree = proto_item_add_subtree(template_item, ett_template); proto_tree_add_item(template_tree, hf_cflow_template_field_type, tvb, offset, 2, FALSE); offset += 2; proto_tree_add_item(template_tree, hf_cflow_template_field_length, tvb, offset, 2, FALSE); offset += 2; } return (0); } static value_string v9_template_types[] = { { 1, "BYTES" }, { 2, "PKTS" }, { 3, "FLOWS" }, { 4, "PROT" }, { 5, "TOS" }, { 6, "TCP_FLAGS" }, { 7, "L4_SRC_PORT" }, { 8, "IP_SRC_ADDR" }, { 9, "SRC_MASK" }, { 10, "INPUT_SNMP" }, { 11, "L4_DST_PORT" }, { 12, "IP_DST_ADDR" }, { 13, "DST_MASK" }, { 14, "OUTPUT_SNMP" }, { 15, "IP_NEXT_HOP" }, { 16, "SRC_AS" }, { 17, "DST_AS" }, { 18, "BGP_NEXT_HOP" }, { 19, "MUL_DPKTS" }, { 20, "MUL_DOCTETS" }, { 21, "LAST_SWITCHED" }, { 22, "FIRST_SWITCHED" }, { 24, "OUT_PKTS" }, { 40, "TOTAL_BYTES_EXP" }, { 41, "TOTAL_PKTS_EXP" }, { 42, "TOTAL_FLOWS_EXP" }, { 0, NULL }, }; static void v9_template_add(struct v9_template *template) { int i; /* 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 = g_ntohs(template->entries[i].type); template->entries[i].length = g_ntohs(template->entries[i].length); template->length += template->entries[i].length; } 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); } /* * dissect a version 1, 5, or 7 pdu and return the length of the pdu we * processed */ static int dissect_pdu(proto_tree * pdutree, tvbuff_t * tvb, int offset, int ver) { int startoffset = offset; guint32 srcaddr, dstaddr; guint8 mask; nstime_t ts; memset(&ts, '\0', sizeof(ts)); /* * memcpy so we can use the values later to calculate a prefix */ tvb_memcpy(tvb, (guint8 *) & srcaddr, offset, 4); proto_tree_add_ipv4(pdutree, hf_cflow_srcaddr, tvb, offset, 4, srcaddr); offset += 4; tvb_memcpy(tvb, (guint8 *) & dstaddr, offset, 4); proto_tree_add_ipv4(pdutree, hf_cflow_dstaddr, tvb, offset, 4, dstaddr); offset += 4; proto_tree_add_item(pdutree, hf_cflow_nexthop, tvb, offset, 4, FALSE); offset += 4; offset = flow_process_ints(pdutree, tvb, offset); offset = flow_process_sizecount(pdutree, tvb, offset); offset = flow_process_timeperiod(pdutree, tvb, offset); offset = flow_process_ports(pdutree, tvb, offset); /* * and the similarities end here */ if (ver == 1) { offset = flow_process_textfield(pdutree, tvb, offset, 2, "padding"); proto_tree_add_item(pdutree, hf_cflow_prot, tvb, offset++, 1, FALSE); proto_tree_add_item(pdutree, hf_cflow_tos, tvb, offset++, 1, FALSE); proto_tree_add_item(pdutree, hf_cflow_tcpflags, tvb, offset++, 1, FALSE); offset = flow_process_textfield(pdutree, tvb, offset, 3, "padding"); offset = flow_process_textfield(pdutree, tvb, offset, 4, "reserved"); } else { if (ver == 5) offset = flow_process_textfield(pdutree, tvb, offset, 1, "padding"); else { proto_tree_add_item(pdutree, hf_cflow_flags, tvb, offset++, 1, FALSE); } proto_tree_add_item(pdutree, hf_cflow_tcpflags, tvb, offset++, 1, FALSE); proto_tree_add_item(pdutree, hf_cflow_prot, tvb, offset++, 1, FALSE); proto_tree_add_item(pdutree, hf_cflow_tos, tvb, offset++, 1, FALSE); offset = flow_process_aspair(pdutree, tvb, offset); mask = tvb_get_guint8(tvb, offset); proto_tree_add_text(pdutree, tvb, offset, 1, "SrcMask: %u (prefix: %s/%u)", mask, getprefix(&srcaddr, mask), mask != 0 ? mask : 32); proto_tree_add_uint_hidden(pdutree, hf_cflow_srcmask, tvb, offset++, 1, mask); mask = tvb_get_guint8(tvb, offset); proto_tree_add_text(pdutree, tvb, offset, 1, "DstMask: %u (prefix: %s/%u)", mask, getprefix(&dstaddr, mask), mask != 0 ? mask : 32); proto_tree_add_uint_hidden(pdutree, hf_cflow_dstmask, tvb, offset++, 1, mask); offset = flow_process_textfield(pdutree, tvb, offset, 2, "padding"); if (ver == 7) { proto_tree_add_item(pdutree, hf_cflow_routersc, tvb, offset, 4, FALSE); offset += 4; } } return (offset - startoffset); } static gchar * getprefix(const guint32 * address, int prefix) { guint32 gprefix; gprefix = *address & g_htonl((0xffffffff << (32 - prefix))); return (ip_to_str((const guint8 *)&gprefix)); } void proto_register_netflow(void) { static hf_register_info hf[] = { /* * flow header */ {&hf_cflow_version, {"Version", "cflow.version", FT_UINT16, BASE_DEC, NULL, 0x0, "NetFlow Version", HFILL} }, {&hf_cflow_count, {"Count", "cflow.count", FT_UINT16, BASE_DEC, NULL, 0x0, "Count of PDUs", HFILL} }, {&hf_cflow_sysuptime, {"SysUptime", "cflow.sysuptime", FT_UINT32, BASE_DEC, NULL, 0x0, "Time since router booted (in milliseconds)", HFILL} }, {&hf_cflow_timestamp, {"Timestamp", "cflow.timestamp", FT_ABSOLUTE_TIME, BASE_NONE, NULL, 0x0, "Current seconds since epoch", HFILL} }, {&hf_cflow_unix_secs, {"CurrentSecs", "cflow.unix_secs", FT_UINT32, BASE_DEC, NULL, 0x0, "Current seconds since epoch", HFILL} }, {&hf_cflow_unix_nsecs, {"CurrentNSecs", "cflow.unix_nsecs", FT_UINT32, BASE_DEC, NULL, 0x0, "Residual nanoseconds since epoch", HFILL} }, {&hf_cflow_samplerate, {"SampleRate", "cflow.samplerate", FT_UINT16, BASE_DEC, NULL, 0x0, "Sample Frequency of exporter", HFILL} }, /* * end version-agnostic header * version-specific flow header */ {&hf_cflow_sequence, {"FlowSequence", "cflow.sequence", FT_UINT32, BASE_DEC, NULL, 0x0, "Sequence number of flows seen", HFILL} }, {&hf_cflow_engine_type, {"EngineType", "cflow.engine_type", FT_UINT8, BASE_DEC, NULL, 0x0, "Flow switching engine type", HFILL} }, {&hf_cflow_engine_id, {"EngineId", "cflow.engine_id", FT_UINT8, BASE_DEC, NULL, 0x0, "Slot number of switching engine", HFILL} }, {&hf_cflow_source_id, {"SourceId", "cflow.source_id", FT_UINT32, BASE_DEC, NULL, 0x0, "Identifier for export device", HFILL} }, {&hf_cflow_aggmethod, {"AggMethod", "cflow.aggmethod", FT_UINT8, BASE_DEC, VALS(v8_agg), 0x0, "CFlow V8 Aggregation Method", HFILL} }, {&hf_cflow_aggversion, {"AggVersion", "cflow.aggversion", FT_UINT8, BASE_DEC, NULL, 0x0, "CFlow V8 Aggregation Version", HFILL} }, /* * end version specific header storage */ /* * Version 9 */ {&hf_cflow_flowset_id, {"FlowSet Id", "cflow.flowset_id", FT_UINT16, BASE_DEC, NULL, 0x0, "FlowSet Id", HFILL} }, {&hf_cflow_data_flowset_id, {"Data FlowSet (Template Id)", "cflow.data_flowset_id", FT_UINT16, BASE_DEC, NULL, 0x0, "Data FlowSet with corresponding to a template Id", HFILL} }, {&hf_cflow_options_flowset_id, {"Options FlowSet", "cflow.options_flowset_id", FT_UINT16, BASE_DEC, NULL, 0x0, "Options FlowSet", HFILL} }, {&hf_cflow_template_flowset_id, {"Template FlowSet", "cflow.template_flowset_id", FT_UINT16, BASE_DEC, NULL, 0x0, "Template FlowSet", HFILL} }, {&hf_cflow_flowset_length, {"FlowSet Length", "cflow.flowset_length", FT_UINT16, BASE_DEC, NULL, 0x0, "FlowSet length", HFILL} }, {&hf_cflow_template_id, {"Template Id", "cflow.template_id", FT_UINT16, BASE_DEC, NULL, 0x0, "Template Id", HFILL} }, {&hf_cflow_template_field_count, {"Field Count", "cflow.template_field_count", FT_UINT16, BASE_DEC, NULL, 0x0, "Template field count", HFILL} }, {&hf_cflow_template_field_type, {"Type", "cflow.template_field_type", FT_UINT16, BASE_DEC, VALS(v9_template_types), 0x0, "Template field type", HFILL} }, {&hf_cflow_template_field_length, {"Length", "cflow.template_field_length", FT_UINT16, BASE_DEC, NULL, 0x0, "Template field length", HFILL} }, /* * begin pdu content storage */ {&hf_cflow_srcaddr, {"SrcAddr", "cflow.srcaddr", 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, "Flow Source Network", HFILL} }, {&hf_cflow_dstaddr, {"DstAddr", "cflow.dstaddr", 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, "Flow Destination Network", HFILL} }, {&hf_cflow_nexthop, {"NextHop", "cflow.nexthop", 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, "Flow Input Interface", HFILL} }, {&hf_cflow_outputint, {"OutputInt", "cflow.outputint", FT_UINT16, BASE_DEC, NULL, 0x0, "Flow Output Interface", HFILL} }, {&hf_cflow_flows, {"Flows", "cflow.flows", FT_UINT32, BASE_DEC, NULL, 0x0, "Flows Aggregated in PDU", HFILL} }, {&hf_cflow_packets, {"Packets", "cflow.packets", 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, "Uptime at start of flow", HFILL} }, {&hf_cflow_timeend, {"EndTime", "cflow.timeend", FT_RELATIVE_TIME, BASE_NONE, NULL, 0x0, "Uptime at end of flow", HFILL} }, {&hf_cflow_srcport, {"SrcPort", "cflow.srcport", FT_UINT16, BASE_DEC, NULL, 0x0, "Flow Source Port", HFILL} }, {&hf_cflow_dstport, {"DstPort", "cflow.dstport", FT_UINT16, BASE_DEC, NULL, 0x0, "Flow Destination Port", HFILL} }, {&hf_cflow_prot, {"Protocol", "cflow.protocol", FT_UINT8, BASE_DEC, NULL, 0x0, "IP Protocol", HFILL} }, {&hf_cflow_tos, {"IP ToS", "cflow.tos", FT_UINT8, BASE_HEX, NULL, 0x0, "IP Type of Service", HFILL} }, {&hf_cflow_flags, {"Export Flags", "cflow.flags", FT_UINT8, BASE_HEX, NULL, 0x0, "CFlow Flags", HFILL} }, {&hf_cflow_tcpflags, {"TCP Flags", "cflow.tcpflags", FT_UINT8, BASE_HEX, NULL, 0x0, "TCP Flags", HFILL} }, {&hf_cflow_srcas, {"SrcAS", "cflow.srcas", FT_UINT16, BASE_DEC, NULL, 0x0, "Source AS", HFILL} }, {&hf_cflow_dstas, {"DstAS", "cflow.dstas", FT_UINT16, BASE_DEC, NULL, 0x0, "Destination AS", HFILL} }, {&hf_cflow_srcmask, {"SrcMask", "cflow.srcmask", FT_UINT8, BASE_DEC, NULL, 0x0, "Source Prefix Mask", HFILL} }, {&hf_cflow_dstmask, {"DstMask", "cflow.dstmask", FT_UINT8, BASE_DEC, NULL, 0x0, "Destination Prefix Mask", HFILL} }, {&hf_cflow_routersc, {"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 */ }; static gint *ett[] = { &ett_netflow, &ett_unixtime, &ett_flow, &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); } /* * protocol/port association */ void proto_reg_handoff_netflow(void) { 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; dissector_add("udp.port", netflow_udp_port, netflow_handle); }