/* packet-iperf.c * Routines for iPerf dissection * By Anish Bhatt * * Updates for iperf 2.1.9 * By Andrii Vladyka * By Robert McMahon * TODO: server-side packets (pending clarifications form Bob) * * Wireshark - Network traffic analyzer * By Gerald Combs * Copyright 1998 Gerald Combs * * SPDX-License-Identifier: GPL-2.0-or-later */ #include "config.h" #include #include #define IPERF2_UDP_HDR_SIZE 160 void proto_register_iperf2(void); void proto_reg_handoff_iperf2(void); static int proto_iperf2 = -1; static int hf_iperf2_sequence = -1; static int hf_iperf2_sec = -1; static int hf_iperf2_usec = -1; static int hf_iperf2_timestamp = -1; static int hf_iperf2_sequence_upper = -1; static int hf_iperf2_flags = -1; static int hf_iperf2_num_threads = -1; static int hf_iperf2_mport = -1; static int hf_iperf2_bufferlen = -1; static int hf_iperf2_mwinband = -1; static int hf_iperf2_mamount = -1; static int hf_iperf2_type = -1; static int hf_iperf2_length = -1; static int hf_iperf2_up_flags = -1; static int hf_iperf2_low_flags = -1; static int hf_iperf2_version_major = -1; static int hf_iperf2_version_minor = -1; static int hf_iperf2_version = -1; static int hf_iperf2_reserved = -1; static int hf_iperf2_tos = -1; static int hf_iperf2_rate = -1; static int hf_iperf2_rate_units = -1; static int hf_iperf2_realtime = -1; static int hf_iperf2_permit_key_len = -1; static int hf_iperf2_permit_key = -1; static int hf_iperf2_isoch_burst_period = -1; static int hf_iperf2_isoch_start_ts_s = -1; static int hf_iperf2_isoch_start_ts_us = -1; static int hf_iperf2_isoch_start_ts = -1; static int hf_iperf2_isoch_prev_frameid = -1; static int hf_iperf2_isoch_frameid = -1; static int hf_iperf2_isoch_burstsize = -1; static int hf_iperf2_isoch_bytes_remaining = -1; static int hf_iperf2_isoch_reserved = -1; static int hf_iperf2_reserved2 = -1; static int hf_iperf2_start_tv_sec = -1; static int hf_iperf2_start_tv_usec = -1; static int hf_iperf2_start_tv = -1; static int hf_iperf2_fq_ratel = -1; static int hf_iperf2_fq_rateu = -1; static int hf_iperf2_fpsl = -1; static int hf_iperf2_fpsu = -1; static int hf_iperf2_meanl = -1; static int hf_iperf2_meanu = -1; static int hf_iperf2_variancel = -1; static int hf_iperf2_varianceu = -1; static int hf_iperf2_burstipgl = -1; static int hf_iperf2_burstipg = -1; static int hf_iperf2_cca_len = -1; static int hf_iperf2_cca_value = -1; static int hf_iperf2_bb_size = -1; static int hf_iperf2_bb_id = -1; static int hf_iperf2_bb_flags = -1; static int hf_iperf2_bb_tos = -1; static int hf_iperf2_bb_run_time = -1; static int hf_iperf2_bb_clienttx_ts_sec = -1; static int hf_iperf2_bb_clienttx_ts_usec = -1; static int hf_iperf2_bb_clienttx_ts = -1; static int hf_iperf2_bb_serverrx_ts_sec = -1; static int hf_iperf2_bb_serverrx_ts_usec = -1; static int hf_iperf2_bb_serverrx_ts = -1; static int hf_iperf2_bb_servertx_ts_sec = -1; static int hf_iperf2_bb_servertx_ts_usec = -1; static int hf_iperf2_bb_servertx_ts = -1; static int hf_iperf2_bb_hold = -1; static int hf_iperf2_bb_rtt = -1; static int hf_iperf2_bb_read_ts_sec = -1; static int hf_iperf2_bb_read_ts_usec = -1; static int hf_iperf2_bb_read_ts = -1; static int hf_iperf2_bb_reply_size = -1; // Flags definition for hf_iperf2_flags. See include/Listener.hpp in iperf2 source code #define HEADER_VERSION1 0x80000000 #define HEADER_EXTEND 0x40000000 #define HEADER_UDPTESTS 0x20000000 #define HEADER_SEQNO64B 0x08000000 #define HEADER_VERSION2 0x04000000 #define HEADER_V2PEERDETECT 0x02000000 #define HEADER_UDPAVOID2 0x02000000 #define HEADER_UDPAVOID1 0x01000000 #define HEADER_BOUNCEBACK 0x00800000 #define HEADER32_SMALL_TRIPTIMES 0x00020000 #define HEADER_LEN_BIT 0x00010000 #define HEADER_LEN_MASK 0x000001FE #define RUN_NOW 0x00000001 #define HEADER16_SMALL_TRIPTIMES 0x00020000 #define HEADER_ISOCH 0x0001 #define HEADER_L2ETHPIPV6 0x0002 #define HEADER_L2LENCHECK 0x0004 #define HEADER_NOUDPFIN 0x0008 #define HEADER_TRIPTIME 0x0010 #define HEADER_UNUSED2 0x0020 #define HEADER_ISOCH_SETTINGS 0x0040 #define HEADER_UNITS_PPS 0x0080 #define HEADER_BWSET 0x0100 #define HEADER_FQRATESET 0x0200 #define HEADER_REVERSE 0x0400 #define HEADER_FULLDUPLEX 0x0800 #define HEADER_EPOCH_START 0x1000 #define HEADER_PERIODICBURST 0x2000 #define HEADER_WRITEPREFETCH 0x4000 #define HEADER_TCPQUICKACK 0x8000 // Bounceback flags #define HEADER_BBQUICKACK 0x8000 #define HEADER_BBCLOCKSYNCED 0x4000 #define HEADER_BBTOS 0x2000 #define HEADER_BBSTOP 0x1000 #define HEADER_BBREPLYSIZE 0x0800 // lower flags (16 bit) #define HEADER_CCA 0x8000 // Flags fields declarations for hf_iperf2_flags static int hf_iperf2_flag_header_version1 = -1; static int hf_iperf2_flag_header_extend = -1; static int hf_iperf2_header_udptests = -1; static int hf_iperf2_header_seqno64b = -1; static int hf_iperf2_header_version2 = -1; static int hf_iperf2_header_v2peerdetect = -1; static int hf_iperf2_header_udpavoid = -1; static int hf_iperf2_header_bounceback = -1; static int hf_iperf2_header_len_bit = -1; static int hf_iperf2_header_len_mask = -1; static int hf_iperf2_run_now = -1; static int hf_iperf2_header16_small_triptimes = -1; static int hf_iperf2_payload = -1; static int * const iperf2_flags[] = { &hf_iperf2_flag_header_version1, &hf_iperf2_flag_header_extend, &hf_iperf2_header_udptests, &hf_iperf2_header_seqno64b, &hf_iperf2_header_version2, &hf_iperf2_header_v2peerdetect, &hf_iperf2_header_udpavoid, &hf_iperf2_header_bounceback, &hf_iperf2_header_len_mask, &hf_iperf2_header16_small_triptimes, &hf_iperf2_header_len_bit, &hf_iperf2_run_now, NULL }; // Flags fields declarations for iperf2_upper_flags static int hf_iperf2_upper_header_isoch = -1; static int hf_iperf2_upper_header_l2ethpipv6 = -1; static int hf_iperf2_upper_header_l2lencheck = -1; static int hf_iperf2_upper_header_noudpfin = -1; static int hf_iperf2_upper_header_triptime = -1; static int hf_iperf2_upper_header_unused2 = -1; static int hf_iperf2_upper_header_isoch_settings = -1; static int hf_iperf2_upper_header_units_pps = -1; static int hf_iperf2_upper_header_bwset = -1; static int hf_iperf2_upper_header_fqrateset = -1; static int hf_iperf2_upper_header_reverse = -1; static int hf_iperf2_upper_header_fullduplex = -1; static int hf_iperf2_upper_header_epoch_start = -1; static int hf_iperf2_upper_header_periodicburst = -1; static int hf_iperf2_upper_header_writeprefetch = -1; static int hf_iperf2_upper_header_tcpquickack = -1; static int * const iperf2_upper_flags[] = { &hf_iperf2_upper_header_tcpquickack, &hf_iperf2_upper_header_writeprefetch, &hf_iperf2_upper_header_periodicburst, &hf_iperf2_upper_header_epoch_start, &hf_iperf2_upper_header_fullduplex, &hf_iperf2_upper_header_reverse, &hf_iperf2_upper_header_fqrateset, &hf_iperf2_upper_header_bwset, &hf_iperf2_upper_header_units_pps, &hf_iperf2_upper_header_isoch_settings, &hf_iperf2_upper_header_unused2, &hf_iperf2_upper_header_triptime, &hf_iperf2_upper_header_noudpfin, &hf_iperf2_upper_header_l2lencheck, &hf_iperf2_upper_header_l2ethpipv6, &hf_iperf2_upper_header_isoch, NULL }; // Flags fields declarations for iperf2_lower_flags static int hf_iperf2_lower_header_cca = -1; static int * const iperf2_lower_flags[] = { &hf_iperf2_lower_header_cca, NULL }; // Flags fields declarations for iperf2_bb_flags static int hf_iperf2_header_bbquickack = -1; static int hf_iperf2_header_bbclocksynced = -1; static int hf_iperf2_header_bbtos = -1; static int hf_iperf2_header_bbstop = -1; static int hf_iperf2_header_bbreplysize = -1; static int * const iperf2_bb_flags[] = { &hf_iperf2_header_bbquickack, &hf_iperf2_header_bbclocksynced, &hf_iperf2_header_bbtos, &hf_iperf2_header_bbstop, &hf_iperf2_header_bbreplysize, NULL }; static gint ett_iperf2_udp = -1; static gint ett_iperf2_tcp = -1; static gint ett_udphdr = -1; static gint ett_clienthdr = -1; static gint ett_bbhdr = -1; static gint ett_extendedhdr = -1; static gint ett_permit_key = -1; static gint ett_client_upper_flags = -1; static gint ett_client_lower_flags = -1; static gint ett_isochhdr = -1; static gint ett_fqhdr = -1; static gint ett_ext_isochhdr = -1; static gint ett_client_hdr = -1; static gint ett_client_hdr_flags = -1; static gint ett_cca_hdr = -1; static gint ett_bb_hdr_flags = -1; static gint ett_bbclienttx_ts = -1; static gint ett_bbserverrx_ts = -1; static gint ett_bbservertx_ts = -1; static gint ett_bbread_ts = -1; static gint ett_data = -1; /* parser definitions for iperf2 payload */ static tvbparse_wanted_t *want; static tvbparse_wanted_t *want_trailing; static dissector_handle_t iperf2_handle_tcp = NULL; static dissector_handle_t iperf2_handle_udp = NULL; typedef struct { bool first_packet_processed; bool second_packet_processed; } iperf2_conversation_t; static void format_version(gchar *buf, guint32 value) { snprintf(buf, ITEM_LABEL_LENGTH, "%d.%d", (value & 0xFFFF0000) >> 16, (value & 0xFFFF)); } static void format_version_long(gchar *buf, guint64 value) { snprintf(buf, ITEM_LABEL_LENGTH, "%d.%d.%d.%d", (guint32)((value & 0xFFFF000000000000) >> 48), (guint32)((value & 0xFFFF00000000) >> 32), (guint32)((value & 0xFFFF0000) >> 16), (guint32)(value & 0xFFFF)); } static int dissect_iperf2_payload(tvbuff_t *tvb, proto_tree *tree, guint32 offset) { proto_tree *data_tree; data_tree = proto_tree_add_subtree(tree, tvb, offset, tvb_reported_length(tvb) - offset, ett_data, NULL, "iPerf2 Payload"); proto_tree_add_item(data_tree, hf_iperf2_payload, tvb, offset, tvb_reported_length(tvb) - offset, ENC_NA); offset = tvb_reported_length(tvb); return offset; } static int dissect_iperf2_client_header(tvbuff_t *tvb, proto_tree *tree, guint32 offset, bool is_udp) { guint32 small_packets = 0; guint32 initial_offset = offset; gint client_header_size = 24; proto_tree *client_tree; if (is_udp) { small_packets = tvb_get_guint32(tvb, offset, ENC_BIG_ENDIAN); if (small_packets & HEADER32_SMALL_TRIPTIMES) { client_header_size = 4; } } client_tree = proto_tree_add_subtree(tree, tvb, offset, client_header_size, ett_clienthdr, NULL, "iPerf2 Client Header"); proto_tree_add_bitmask(client_tree, tvb, offset, hf_iperf2_flags, ett_client_hdr_flags, iperf2_flags, ENC_BIG_ENDIAN); offset += 4; if (is_udp) { if (small_packets & HEADER32_SMALL_TRIPTIMES) { if ((tvb_reported_length(tvb) - offset) > 0) { return dissect_iperf2_payload(tvb, tree, offset); } else { return offset - initial_offset; } } } proto_tree_add_item(client_tree, hf_iperf2_num_threads, tvb, offset, 4, ENC_BIG_ENDIAN); offset += 4; proto_tree_add_item(client_tree, hf_iperf2_mport, tvb, offset, 4, ENC_BIG_ENDIAN); offset += 4; proto_tree_add_item(client_tree, hf_iperf2_bufferlen, tvb, offset, 4, ENC_BIG_ENDIAN); offset += 4; proto_tree_add_item(client_tree, hf_iperf2_mwinband, tvb, offset, 4, ENC_BIG_ENDIAN); offset += 4; proto_tree_add_item(client_tree, hf_iperf2_mamount, tvb, offset, 4, ENC_BIG_ENDIAN); offset += 4; return offset - initial_offset; } static int dissect_iperf2_extended_header(tvbuff_t *tvb, proto_tree *tree, guint32 offset) { guint32 initial_offset = offset, permit_key_len = 0; proto_tree *extended_tree, *permit_key_tree; proto_item *ti; extended_tree = proto_tree_add_subtree(tree, tvb, offset, 36, ett_extendedhdr, NULL, "iPerf2 Extended Header"); proto_tree_add_item(extended_tree, hf_iperf2_type, tvb, offset, 4, ENC_BIG_ENDIAN); offset += 4; proto_tree_add_item(extended_tree, hf_iperf2_length, tvb, offset, 4, ENC_BIG_ENDIAN); offset += 4; proto_tree_add_bitmask(extended_tree, tvb, offset, hf_iperf2_up_flags, ett_client_upper_flags, iperf2_upper_flags, ENC_BIG_ENDIAN); offset += 2; proto_tree_add_bitmask(extended_tree, tvb, offset, hf_iperf2_low_flags, ett_client_lower_flags, iperf2_lower_flags, ENC_BIG_ENDIAN); offset += 2; proto_tree_add_item(extended_tree, hf_iperf2_version_major, tvb, offset, 4, ENC_BIG_ENDIAN); offset += 4; proto_tree_add_item(extended_tree, hf_iperf2_version_minor, tvb, offset, 4, ENC_BIG_ENDIAN); offset += 4; ti = proto_tree_add_item(extended_tree, hf_iperf2_version, tvb, offset - 8, 8, ENC_BIG_ENDIAN); proto_item_set_generated(ti); proto_tree_add_item(extended_tree, hf_iperf2_reserved, tvb, offset, 2, ENC_BIG_ENDIAN); offset += 2; proto_tree_add_item(extended_tree, hf_iperf2_tos, tvb, offset, 2, ENC_BIG_ENDIAN); offset += 2; proto_tree_add_item(extended_tree, hf_iperf2_rate, tvb, offset, 4, ENC_BIG_ENDIAN); offset += 4; proto_tree_add_item(extended_tree, hf_iperf2_rate_units, tvb, offset, 4, ENC_BIG_ENDIAN); offset += 4; proto_tree_add_item(extended_tree, hf_iperf2_realtime, tvb, offset, 4, ENC_BIG_ENDIAN); offset += 4; // There may be an optional permit key at the end of this header. Flags are not reliable - do some heuristics here instead. if (tvb_reported_length(tvb) - offset >= 2) { permit_key_len = tvb_get_guint16(tvb, offset, ENC_BIG_ENDIAN); if ((permit_key_len != 0) && (permit_key_len <= (tvb_reported_length(tvb) - offset - 2))) { permit_key_tree = proto_tree_add_subtree(tree, tvb, offset, permit_key_len + 2, ett_permit_key, NULL, "iPerf2 Permit Key"); proto_tree_add_item(permit_key_tree, hf_iperf2_permit_key_len, tvb, offset, 2, ENC_BIG_ENDIAN); offset += 2; proto_tree_add_item(permit_key_tree, hf_iperf2_permit_key, tvb, offset, permit_key_len, ENC_ASCII); offset += permit_key_len; } } return offset - initial_offset; } static int dissect_iperf2_isoch_header(tvbuff_t *tvb, proto_tree *tree, guint32 offset) { guint32 initial_offset = offset; proto_tree *ext_isoch_tree; ext_isoch_tree = proto_tree_add_subtree(tree, tvb, offset, 32, ett_ext_isochhdr, NULL, "iPerf2 Extended Isochronous Header"); proto_tree_add_item(ext_isoch_tree, hf_iperf2_fpsl, tvb, offset, 4, ENC_BIG_ENDIAN); offset += 4; proto_tree_add_item(ext_isoch_tree, hf_iperf2_fpsu, tvb, offset, 4, ENC_BIG_ENDIAN); offset += 4; proto_tree_add_item(ext_isoch_tree, hf_iperf2_meanl, tvb, offset, 4, ENC_BIG_ENDIAN); offset += 4; proto_tree_add_item(ext_isoch_tree, hf_iperf2_meanu, tvb, offset, 4, ENC_BIG_ENDIAN); offset += 4; proto_tree_add_item(ext_isoch_tree, hf_iperf2_variancel, tvb, offset, 4, ENC_BIG_ENDIAN); offset += 4; proto_tree_add_item(ext_isoch_tree, hf_iperf2_varianceu, tvb, offset, 4, ENC_BIG_ENDIAN); offset += 4; proto_tree_add_item(ext_isoch_tree, hf_iperf2_burstipgl, tvb, offset, 4, ENC_BIG_ENDIAN); offset += 4; proto_tree_add_item(ext_isoch_tree, hf_iperf2_burstipg, tvb, offset, 4, ENC_BIG_ENDIAN); offset += 4; return offset - initial_offset; } static int dissect_iperf2_isoch_payload_header(tvbuff_t *tvb, proto_tree *tree, guint32 offset) { proto_item *ti; guint32 initial_offset = offset; proto_tree *isoch_tree; nstime_t isoch_timestamp; guint isoch_ts_sec = 0; guint isoch_ts_usec = 0; isoch_tree = proto_tree_add_subtree(tree, tvb, offset, 32, ett_isochhdr, NULL, "iPerf2 Isochronous Header"); proto_tree_add_item(isoch_tree, hf_iperf2_isoch_burst_period, tvb, offset, 4, ENC_BIG_ENDIAN); offset += 4; proto_tree_add_item_ret_uint(isoch_tree, hf_iperf2_isoch_start_ts_s, tvb, offset, 4, ENC_BIG_ENDIAN, &isoch_ts_sec); offset += 4; proto_tree_add_item_ret_uint(isoch_tree, hf_iperf2_isoch_start_ts_us, tvb, offset, 4, ENC_BIG_ENDIAN, &isoch_ts_usec); offset += 4; isoch_timestamp.secs = (time_t)isoch_ts_sec; isoch_timestamp.nsecs = (int)isoch_ts_usec * 1000; ti = proto_tree_add_time(isoch_tree, hf_iperf2_isoch_start_ts, tvb, offset - 8, 8, &isoch_timestamp); proto_item_set_generated(ti); proto_tree_add_item(isoch_tree, hf_iperf2_isoch_prev_frameid, tvb, offset, 4, ENC_BIG_ENDIAN); offset += 4; proto_tree_add_item(isoch_tree, hf_iperf2_isoch_frameid, tvb, offset, 4, ENC_BIG_ENDIAN); offset += 4; proto_tree_add_item(isoch_tree, hf_iperf2_isoch_burstsize, tvb, offset, 4, ENC_BIG_ENDIAN); offset += 4; proto_tree_add_item(isoch_tree, hf_iperf2_isoch_bytes_remaining, tvb, offset, 4, ENC_BIG_ENDIAN); offset += 4; proto_tree_add_item(isoch_tree, hf_iperf2_isoch_reserved, tvb, offset, 4, ENC_BIG_ENDIAN); offset += 4; return offset - initial_offset; } static int dissect_iperf2_fq_start_time_header(tvbuff_t *tvb, proto_tree *tree, guint32 offset) { proto_item *ti; guint32 initial_offset = offset; proto_tree *fq_tree; guint fq_ts_sec = 0; guint fq_ts_usec = 0; nstime_t fq_timestamp; fq_tree = proto_tree_add_subtree(tree, tvb, offset, 20, ett_fqhdr, NULL, "iPerf2 Fair Queue Start Time Header"); proto_tree_add_item(fq_tree, hf_iperf2_reserved2, tvb, offset, 4, ENC_BIG_ENDIAN); offset += 4; proto_tree_add_item_ret_uint(fq_tree, hf_iperf2_start_tv_sec, tvb, offset, 4, ENC_BIG_ENDIAN, &fq_ts_sec); offset += 4; proto_tree_add_item_ret_uint(fq_tree, hf_iperf2_start_tv_usec, tvb, offset, 4, ENC_BIG_ENDIAN, &fq_ts_usec); offset += 4; fq_timestamp.secs = (time_t)fq_ts_sec; fq_timestamp.nsecs = (int)fq_ts_usec * 1000; ti = proto_tree_add_time(fq_tree, hf_iperf2_start_tv, tvb, offset - 8, 8, &fq_timestamp); proto_item_set_generated(ti); proto_tree_add_item(fq_tree, hf_iperf2_fq_ratel, tvb, offset, 4, ENC_BIG_ENDIAN); offset += 4; proto_tree_add_item(fq_tree, hf_iperf2_fq_rateu, tvb, offset, 4, ENC_BIG_ENDIAN); offset += 4; return offset - initial_offset; } static int dissect_iperf2_cca_header(tvbuff_t *tvb, proto_tree *tree, guint32 offset) { guint32 initial_offset = offset; proto_tree *cca_tree; cca_tree = proto_tree_add_subtree(tree, tvb, offset, 34, ett_cca_hdr, NULL, "iPerf2 CCA Header"); proto_tree_add_item(cca_tree, hf_iperf2_cca_len, tvb, offset, 2, ENC_BIG_ENDIAN); offset += 2; proto_tree_add_item(cca_tree, hf_iperf2_cca_value, tvb, offset, 32, ENC_NA); offset += 32; return offset - initial_offset; } static int dissect_iperf2_bounceback_header(tvbuff_t *tvb, proto_tree *tree, guint32 offset) { proto_tree *bb_tree, *bb_tree_clienttx_ts, *bb_tree_serverrx_ts, *bb_tree_servertx_ts, *bb_tree_read_ts; proto_item *ti; guint ts_sec = 0, ts_usec = 0; nstime_t clienttx_ts, serverrx_ts, servertx_ts, read_ts; bb_tree = proto_tree_add_subtree(tree, tvb, offset, 64, ett_bbhdr, NULL, "iPerf2 Bounceback Header"); proto_tree_add_bitmask(bb_tree, tvb, offset, hf_iperf2_flags, ett_client_hdr_flags, iperf2_flags, ENC_BIG_ENDIAN); offset += 4; proto_tree_add_item(bb_tree, hf_iperf2_bb_size, tvb, offset, 4, ENC_BIG_ENDIAN); offset += 4; proto_tree_add_item(bb_tree, hf_iperf2_bb_id, tvb, offset, 4, ENC_BIG_ENDIAN); offset += 4; proto_tree_add_bitmask(bb_tree, tvb, offset, hf_iperf2_bb_flags, ett_bb_hdr_flags, iperf2_bb_flags, ENC_BIG_ENDIAN); offset += 2; proto_tree_add_item(bb_tree, hf_iperf2_bb_tos, tvb, offset, 2, ENC_BIG_ENDIAN); offset += 2; guint bb_run_time = 0; ti = proto_tree_add_item_ret_uint(bb_tree, hf_iperf2_bb_run_time, tvb, offset, 4, ENC_BIG_ENDIAN, &bb_run_time); proto_item_append_text(ti, "%d ms", bb_run_time * 10); offset += 4; bb_tree_clienttx_ts = proto_tree_add_subtree(bb_tree, tvb, offset, 8, ett_bbclienttx_ts, NULL, "Client Tx Timestamp"); proto_tree_add_item_ret_uint(bb_tree_clienttx_ts, hf_iperf2_bb_clienttx_ts_sec, tvb, offset, 4, ENC_BIG_ENDIAN, &ts_sec); offset += 4; proto_tree_add_item_ret_uint(bb_tree_clienttx_ts, hf_iperf2_bb_clienttx_ts_usec, tvb, offset, 4, ENC_BIG_ENDIAN, &ts_usec); offset += 4; clienttx_ts.secs = (time_t)ts_sec; clienttx_ts.nsecs = (int)ts_usec * 1000; ti = proto_tree_add_time(bb_tree_clienttx_ts, hf_iperf2_bb_clienttx_ts, tvb, offset - 8, 8, &clienttx_ts); proto_item_set_generated(ti); bb_tree_serverrx_ts = proto_tree_add_subtree(bb_tree, tvb, offset, 8, ett_bbserverrx_ts, NULL, "Server Rx Timestamp"); proto_tree_add_item_ret_uint(bb_tree_serverrx_ts, hf_iperf2_bb_serverrx_ts_sec, tvb, offset, 4, ENC_BIG_ENDIAN, &ts_sec); offset += 4; proto_tree_add_item_ret_uint(bb_tree_serverrx_ts, hf_iperf2_bb_serverrx_ts_usec, tvb, offset, 4, ENC_BIG_ENDIAN, &ts_usec); offset += 4; serverrx_ts.secs = (time_t)ts_sec; serverrx_ts.nsecs = (int)ts_usec * 1000; ti = proto_tree_add_time(bb_tree_serverrx_ts, hf_iperf2_bb_serverrx_ts, tvb, offset - 8, 8, &serverrx_ts); proto_item_set_generated(ti); bb_tree_servertx_ts = proto_tree_add_subtree(bb_tree, tvb, offset, 8, ett_bbservertx_ts, NULL, "Server Tx Timestamp"); proto_tree_add_item_ret_uint(bb_tree_servertx_ts, hf_iperf2_bb_servertx_ts_sec, tvb, offset, 4, ENC_BIG_ENDIAN, &ts_sec); offset += 4; proto_tree_add_item_ret_uint(bb_tree_servertx_ts, hf_iperf2_bb_servertx_ts_usec, tvb, offset, 4, ENC_BIG_ENDIAN, &ts_usec); offset += 4; servertx_ts.secs = (time_t)ts_sec; servertx_ts.nsecs = (int)ts_usec * 1000; ti = proto_tree_add_time(bb_tree_servertx_ts, hf_iperf2_bb_servertx_ts, tvb, offset - 8, 8, &servertx_ts); proto_item_set_generated(ti); proto_tree_add_item(bb_tree, hf_iperf2_bb_hold, tvb, offset, 4, ENC_BIG_ENDIAN); offset += 4; proto_tree_add_item(bb_tree, hf_iperf2_bb_rtt, tvb, offset, 4, ENC_BIG_ENDIAN); offset += 4; bb_tree_read_ts = proto_tree_add_subtree(bb_tree, tvb, offset, 8, ett_bbread_ts, NULL, "Read Timestamp"); proto_tree_add_item_ret_uint(bb_tree_read_ts, hf_iperf2_bb_read_ts_sec, tvb, offset, 4, ENC_BIG_ENDIAN, &ts_sec); offset += 4; proto_tree_add_item_ret_uint(bb_tree_read_ts, hf_iperf2_bb_read_ts_usec, tvb, offset, 4, ENC_BIG_ENDIAN, &ts_usec); offset += 4; read_ts.secs = (time_t)ts_sec; read_ts.nsecs = (int)ts_usec * 1000; ti = proto_tree_add_time(bb_tree_read_ts, hf_iperf2_bb_read_ts, tvb, offset - 8, 8, &read_ts); proto_item_set_generated(ti); proto_tree_add_item(bb_tree, hf_iperf2_bb_reply_size, tvb, offset, 4, ENC_BIG_ENDIAN); offset += 4; return offset; } static int dissect_iperf2_tcp(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void * data _U_) { proto_item *ti = NULL; proto_tree *iperf2_tree = NULL; guint32 offset = 0, flags = 0, upper_flags = 0, lower_flags = 0, pdu_len = 24; tvbparse_t *tt; col_set_str(pinfo->cinfo, COL_PROTOCOL, "iPerf2"); col_clear(pinfo->cinfo, COL_INFO); // Is it the TCP first write with test information if ((tvb_reported_length(tvb) - offset) < pdu_len) { // We don't have enough data to decode the header offset += tvb_reported_length(tvb); pinfo->desegment_offset = offset; pinfo->desegment_len = pdu_len - offset; return tvb_reported_length(tvb); } flags = tvb_get_guint32(tvb, offset, ENC_BIG_ENDIAN); ti = proto_tree_add_item(tree, proto_iperf2, tvb, offset, pdu_len, ENC_NA); iperf2_tree = proto_item_add_subtree(ti, ett_iperf2_tcp); // Check if we got iPerf2 payload // First, check if the first 10 bytes look like payload tt = tvbparse_init(pinfo->pool, tvb, offset, 10, NULL, NULL); if (tvbparse_get(tt, want)) { // Okay, the first 10 bytes follow the pattern, continue... tvbparse_reset(tt, offset + 10, tvb_reported_length(tvb) - 10); while(tvbparse_curr_offset(tt) < tvb_reported_length(tvb)) { if(tvbparse_get(tt, want) == NULL) break; } // Special case for the trailing payload bytes less than 10 bytes of length if ((tvb_reported_length(tvb) - tvbparse_curr_offset(tt)) <= 10) { tvbparse_get(tt, want_trailing); } if (tvbparse_curr_offset(tt) == tvb_reported_length(tvb)) { col_set_str(pinfo->cinfo, COL_INFO, "Payload only"); offset += dissect_iperf2_payload(tvb, iperf2_tree, offset); proto_item_set_len(ti, tvb_reported_length(tvb)); return offset; } } /* Here we try to understand what extra headers are present. The options are: - client_hdrext (36 bytes) - may contain permitKey (up to 130 bytes) - client_hdrext_starttime_fq (20 bytes) - client_hdrext_isoch_settings (40 bytes) - cca_field (34 bytes) - Combinations of the above - OR a bounnceback header (64 bytes in total) We do two pass analysis to avoid code duplication: - First, we collect the total headers length - Second, we confirm that TCP buffer provided enough bytes to decode all headers - If needed, stop decoding and ask TCP for more data */ for (int pass = 1; pass <= 2; pass++) { if (flags & HEADER_BOUNCEBACK) { (pass == 1) ? (pdu_len = 64) : (offset += dissect_iperf2_bounceback_header(tvb, iperf2_tree, offset)); col_set_str(pinfo->cinfo, COL_INFO, "Bounceback"); } else { if (pass == 2) offset += dissect_iperf2_client_header(tvb, iperf2_tree, offset, FALSE); if (flags & HEADER_EXTEND) { if (pass == 1) pdu_len += 36; else { upper_flags = tvb_get_guint16(tvb, offset + 8, ENC_BIG_ENDIAN); lower_flags = tvb_get_guint16(tvb, offset + 10, ENC_BIG_ENDIAN); offset += dissect_iperf2_extended_header(tvb, iperf2_tree, offset); } } if (upper_flags & HEADER_TRIPTIME || upper_flags & HEADER_FQRATESET || upper_flags & HEADER_ISOCH_SETTINGS || upper_flags & HEADER_EPOCH_START) { (pass == 1) ? (pdu_len += 20) : (offset += dissect_iperf2_fq_start_time_header(tvb, iperf2_tree, offset)); } if (upper_flags & HEADER_FULLDUPLEX || upper_flags & HEADER_REVERSE || upper_flags & HEADER_PERIODICBURST) { (pass == 1) ? (pdu_len += 40) : (offset += dissect_iperf2_isoch_header(tvb, iperf2_tree, offset)); } if (lower_flags & HEADER_CCA) { (pass == 1) ? (pdu_len += 34) : (offset += dissect_iperf2_cca_header(tvb, iperf2_tree, offset)); } } if ((pass == 1) && (tvb_reported_length(tvb) - offset) < pdu_len) { // We don't have enough data to decode all headers offset += tvb_reported_length(tvb); pinfo->desegment_offset = offset; pinfo->desegment_len = pdu_len - offset; proto_item_set_len(ti, tvb_reported_length(tvb)); return tvb_reported_length(tvb); } } if ((tvb_reported_length(tvb) - offset) > 0) { offset += dissect_iperf2_payload(tvb, iperf2_tree, offset); } proto_item_set_len(ti, offset); return offset; } static int dissect_iperf2_udp(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void * data _U_) { guint32 offset = 0; guint32 ext_header = 0; proto_item *ti; proto_tree *iperf2_tree, *udp_tree; col_set_str(pinfo->cinfo, COL_PROTOCOL, "iPerf2"); /* Clear out stuff in the info column */ col_clear(pinfo->cinfo, COL_INFO); ti = proto_tree_add_item(tree, proto_iperf2, tvb, offset, IPERF2_UDP_HDR_SIZE, ENC_NA); iperf2_tree = proto_item_add_subtree(ti, ett_iperf2_udp); udp_tree = proto_tree_add_subtree(iperf2_tree, tvb, offset, 16, ett_udphdr, NULL, "iPerf2 UDP Header"); proto_tree_add_item(udp_tree, hf_iperf2_sequence, tvb, offset, 4, ENC_BIG_ENDIAN); offset += 4; guint ts_sec = 0; proto_tree_add_item_ret_uint(udp_tree, hf_iperf2_sec, tvb, offset, 4, ENC_BIG_ENDIAN, &ts_sec); offset += 4; guint ts_usec = 0; proto_tree_add_item_ret_uint(udp_tree, hf_iperf2_usec, tvb, offset, 4, ENC_BIG_ENDIAN, &ts_usec); offset += 4; nstime_t timestamp; timestamp.secs = (time_t)ts_sec; timestamp.nsecs = (int)ts_usec * 1000; ti = proto_tree_add_time(udp_tree, hf_iperf2_timestamp, tvb, offset - 8, 8, ×tamp); proto_item_set_generated(ti); proto_tree_add_item(udp_tree, hf_iperf2_sequence_upper, tvb, offset, 4, ENC_BIG_ENDIAN); offset += 4; ext_header = tvb_get_guint32(tvb, offset, ENC_BIG_ENDIAN); offset += dissect_iperf2_client_header(tvb, iperf2_tree, offset, TRUE); if (tvb_reported_length(tvb) == offset) { return offset; } //Check is Extended header flag is set, if it's not - do no add more subheaders if (!(ext_header & HEADER_EXTEND)) { return dissect_iperf2_payload(tvb, iperf2_tree, offset); } offset += dissect_iperf2_extended_header(tvb, iperf2_tree, offset); offset += dissect_iperf2_isoch_payload_header(tvb, iperf2_tree, offset); offset += dissect_iperf2_fq_start_time_header(tvb, iperf2_tree, offset); offset += dissect_iperf2_isoch_header(tvb, iperf2_tree, offset); if ((tvb_reported_length(tvb) - offset) > 0) { return dissect_iperf2_payload(tvb, iperf2_tree, offset); } else { return offset; } } void proto_register_iperf2(void) { /* Setup list of header fields */ static hf_register_info hf[] = { { &hf_iperf2_sequence, { "Sequence Number", "iperf2.udp.sequence", FT_INT32, BASE_DEC, NULL, 0, NULL, HFILL } }, { &hf_iperf2_sec, { "Start Time (sec)", "iperf2.udp.sec", FT_UINT32, BASE_DEC, NULL, 0, NULL, HFILL } }, { &hf_iperf2_usec, { "Start Time (usec)", "iperf2.udp.usec", FT_UINT32, BASE_DEC, NULL, 0, NULL, HFILL } }, { &hf_iperf2_timestamp, { "Start Time", "iperf2.udp.timestamp", FT_ABSOLUTE_TIME, ABSOLUTE_TIME_LOCAL, NULL, 0, NULL, HFILL } }, { &hf_iperf2_sequence_upper, { "Upper Sequence Number", "iperf2.udp.sequence_upper", FT_INT32, BASE_DEC, NULL, 0, NULL, HFILL } }, { &hf_iperf2_flags, { "Flags", "iperf2.client.flags", FT_UINT32, BASE_HEX, NULL, 0, NULL, HFILL } }, { &hf_iperf2_flag_header_version1, { "Header Valid", "iperf2.client.flags_version1", FT_BOOLEAN, 32, NULL, HEADER_VERSION1, NULL, HFILL } }, { &hf_iperf2_flag_header_extend, { "Extended Version", "iperf2.client.flags_extend", FT_BOOLEAN, 32, NULL, HEADER_EXTEND, NULL, HFILL } }, { &hf_iperf2_header_udptests, { "UDP Tests", "iperf2.client.flags_udp_tests", FT_BOOLEAN, 32, NULL, HEADER_UDPTESTS, NULL, HFILL } }, { &hf_iperf2_header_seqno64b, { "64 Bit Seq Num", "iperf2.client.flags_seqno64b", FT_BOOLEAN, 32, NULL, HEADER_SEQNO64B, "64-bits sequence numbers are used", HFILL } }, { &hf_iperf2_header_version2, { "Version 2", "iperf2.client.flags_version2", FT_BOOLEAN, 32, NULL, HEADER_VERSION2, NULL, HFILL } }, { &hf_iperf2_header_v2peerdetect, { "Version 2 Peer Detect", "iperf2.client.flags_version2_peerdetect", FT_BOOLEAN, 32, NULL, HEADER_V2PEERDETECT, NULL, HFILL } }, { &hf_iperf2_header_udpavoid, { "Don't use for UDP", "iperf2.client.flags_udpavoid", FT_BOOLEAN, 32, NULL, HEADER_UDPAVOID1, "Don't use these bits for UDP", HFILL } }, { &hf_iperf2_header_bounceback, { "Bounceback", "iperf2.client.flags_bounceback", FT_BOOLEAN, 32, NULL, HEADER_BOUNCEBACK, NULL, HFILL } }, { &hf_iperf2_header_len_bit, { "Length Bit", "iperf2.client.flags_len_bit", FT_BOOLEAN, 32, NULL, HEADER_LEN_BIT, NULL, HFILL } }, { &hf_iperf2_header_len_mask, { "Length Mask", "iperf2.client.flags_len_mask", FT_UINT32, BASE_HEX, NULL, HEADER_LEN_MASK, NULL, HFILL } }, { &hf_iperf2_run_now, { "Run Now", "iperf2.client.flags_run_now", FT_BOOLEAN, 32, NULL, RUN_NOW, NULL, HFILL } }, { &hf_iperf2_header16_small_triptimes, { "Small Triptimes", "iperf2.client.flags_small_triptimes", FT_BOOLEAN, 32, NULL, HEADER16_SMALL_TRIPTIMES, "Don't decode other fields in this packet", HFILL } }, { &hf_iperf2_num_threads, { "Number of Threads", "iperf2.client.numthreads", FT_UINT32, BASE_DEC, NULL, 0, NULL, HFILL } }, { &hf_iperf2_mport, { "Port", "iperf2.client.port", FT_UINT32, BASE_DEC, NULL, 0, NULL, HFILL } }, { &hf_iperf2_bufferlen, { "Buffer Length", "iperf2.client.bufferlen", FT_UINT32, BASE_DEC, NULL, 0, NULL, HFILL } }, { &hf_iperf2_mwinband, { "Bandwidth", "iperf2.client.bandwidth", FT_UINT32, BASE_DEC, NULL, 0, NULL, HFILL } }, { &hf_iperf2_mamount, { "Amount (Time or Bytes)", "iperf2.client.num_bytes", FT_INT32, BASE_DEC, NULL, 0, NULL, HFILL } }, { &hf_iperf2_type, { "Type", "iperf2.client.type", FT_UINT32, BASE_DEC, NULL, 0, NULL, HFILL } }, { &hf_iperf2_length, { "Length", "iperf2.client.length", FT_UINT32, BASE_DEC, NULL, 0, NULL, HFILL } }, { &hf_iperf2_up_flags, { "Upper Flags", "iperf2.client.up_flags", FT_UINT16, BASE_HEX, NULL, 0, NULL, HFILL } }, { &hf_iperf2_upper_header_isoch, { "Isochronous Header", "iperf2.client.upper_header_isoch", FT_BOOLEAN, 16, NULL, HEADER_ISOCH, NULL, HFILL } }, { &hf_iperf2_upper_header_l2ethpipv6, { "L2 ETH IPv6", "iperf2.client.upper_header_l2ethpipv6", FT_BOOLEAN, 16, NULL, HEADER_L2ETHPIPV6, NULL, HFILL } }, { &hf_iperf2_upper_header_l2lencheck, { "L2 Length Check", "iperf2.client.upper_header_l2lencheck", FT_BOOLEAN, 16, NULL, HEADER_L2LENCHECK, NULL, HFILL } }, { &hf_iperf2_upper_header_noudpfin, { "No UDP Fin", "iperf2.client.up_flags", FT_BOOLEAN, 16, NULL, HEADER_NOUDPFIN, NULL, HFILL } }, { &hf_iperf2_upper_header_triptime, { "Trip Time", "iperf2.client.upper_header_triptime", FT_BOOLEAN, 16, NULL, HEADER_TRIPTIME, NULL, HFILL } }, { &hf_iperf2_upper_header_unused2, { "Unused", "iperf2.client.upper_header_unused2", FT_BOOLEAN, 16, NULL, HEADER_UNUSED2, NULL, HFILL } }, { &hf_iperf2_upper_header_isoch_settings, { "Isochronous Settings", "iperf2.client.upper_header_isoch_settings", FT_BOOLEAN, 16, NULL, HEADER_ISOCH_SETTINGS, NULL, HFILL } }, { &hf_iperf2_upper_header_units_pps, { "Units PPS", "iperf2.client.upper_header_units_pps", FT_BOOLEAN, 16, NULL, HEADER_UNITS_PPS, NULL, HFILL } }, { &hf_iperf2_upper_header_bwset, { "Header BW Set", "iperf2.client.upper_header_bwset", FT_BOOLEAN, 16, NULL, HEADER_BWSET, NULL, HFILL } }, { &hf_iperf2_upper_header_fqrateset, { "Fair Queue Rate Set", "iperf2.client.upper_header_fqrateset", FT_BOOLEAN, 16, NULL, HEADER_FQRATESET, NULL, HFILL } }, { &hf_iperf2_upper_header_reverse, { "Reverse", "iperf2.client.upper_header_reverse", FT_BOOLEAN, 16, NULL, HEADER_REVERSE, NULL, HFILL } }, { &hf_iperf2_upper_header_fullduplex, { "Full Duplex", "iperf2.client.upper_header_fullduplex", FT_BOOLEAN, 16, NULL, HEADER_FULLDUPLEX, NULL, HFILL } }, { &hf_iperf2_upper_header_epoch_start, { "Epoch Start", "iperf2.client.upper_header_epoch_start", FT_BOOLEAN, 16, NULL, HEADER_EPOCH_START, NULL, HFILL } }, { &hf_iperf2_upper_header_periodicburst, { "Periodic Burst", "iperf2.client.upper_header_periodicburst", FT_BOOLEAN, 16, NULL, HEADER_PERIODICBURST, NULL, HFILL } }, { &hf_iperf2_upper_header_writeprefetch, { "Write Prefetch", "iperf2.client.upper_header_writeprefetch", FT_BOOLEAN, 16, NULL, HEADER_WRITEPREFETCH, NULL, HFILL } }, { &hf_iperf2_upper_header_tcpquickack, { "TCP Quick Ack", "iperf2.client.upper_header_tcpquickack", FT_BOOLEAN, 16, NULL, HEADER_TCPQUICKACK, NULL, HFILL } }, { &hf_iperf2_low_flags, { "Lower Flags", "iperf2.client.low_flags", FT_UINT16, BASE_HEX, NULL, 0, NULL, HFILL } }, { &hf_iperf2_lower_header_cca, { "CCA", "iperf2.client.lower_header_cca", FT_BOOLEAN, 16, NULL, HEADER_CCA, NULL, HFILL } }, { &hf_iperf2_version_major, { "Major Version", "iperf2.client.version_major", FT_UINT32, BASE_CUSTOM, CF_FUNC(format_version), 0, NULL, HFILL } }, { &hf_iperf2_version_minor, { "Minor Version", "iperf2.client.version_minor", FT_UINT32, BASE_CUSTOM, CF_FUNC(format_version), 0, NULL, HFILL } }, { &hf_iperf2_version, { "Iperf2 Version", "iperf2.client.version", FT_UINT64, BASE_CUSTOM, CF_FUNC(format_version_long), 0, NULL, HFILL } }, { &hf_iperf2_reserved, { "Reserved", "iperf2.client.reserved", FT_UINT16, BASE_HEX, NULL, 0, NULL, HFILL } }, { &hf_iperf2_tos, { "TOS", "iperf2.client.tos", FT_UINT16, BASE_HEX, NULL, 0, NULL, HFILL } }, { &hf_iperf2_rate, { "Rate", "iperf2.client.rate", FT_UINT32, BASE_DEC, NULL, 0, NULL, HFILL } }, { &hf_iperf2_rate_units, { "Rate Units", "iperf2.client.rate_units", FT_UINT32, BASE_DEC, NULL, 0, NULL, HFILL } }, { &hf_iperf2_realtime, { "TCP Realtime", "iperf2.client.realtime", FT_UINT32, BASE_DEC, NULL, 0, NULL, HFILL } }, { &hf_iperf2_permit_key_len, { "Permit Key Length", "iperf2.client.permit_key_length", FT_UINT16, BASE_DEC, NULL, 0, NULL, HFILL } }, { &hf_iperf2_permit_key, { "Permit Key", "iperf2.client.permit_key", FT_STRING, BASE_NONE, NULL, 0, NULL, HFILL } }, { &hf_iperf2_isoch_burst_period, { "Burst Period", "iperf2.client.isoch_burst_period", FT_UINT32, BASE_DEC, NULL, 0, NULL, HFILL } }, { &hf_iperf2_isoch_start_ts_s, { "Start Timestamp (s)", "iperf2.client.isoch_start_ts_s", FT_UINT32, BASE_DEC, NULL, 0, NULL, HFILL } }, { &hf_iperf2_isoch_start_ts_us, { "Start Timestamp (us)", "iperf2.client.isoch_start_ts_us", FT_UINT32, BASE_DEC, NULL, 0, NULL, HFILL } }, { &hf_iperf2_isoch_start_ts, { "Start Timestamp", "iperf2.client.isoch_start_ts", FT_ABSOLUTE_TIME, ABSOLUTE_TIME_LOCAL, NULL, 0, NULL, HFILL } }, { &hf_iperf2_isoch_prev_frameid, { "Previous Frame ID", "iperf2.client.isoch_prev_frameid", FT_UINT32, BASE_DEC, NULL, 0, NULL, HFILL } }, { &hf_iperf2_isoch_frameid, { "Frame ID", "iperf2.client.isoch_frameid", FT_UINT32, BASE_DEC, NULL, 0, NULL, HFILL } }, { &hf_iperf2_isoch_burstsize, { "Burst Size", "iperf2.client.isoch_burstsize", FT_UINT32, BASE_DEC, NULL, 0, NULL, HFILL } }, { &hf_iperf2_isoch_bytes_remaining, { "Bytes Remaining", "iperf2.client.isoch_bytes_remaining", FT_UINT32, BASE_DEC, NULL, 0, NULL, HFILL } }, { &hf_iperf2_isoch_reserved, { "Reserved", "iperf2.client.isoch_reserved", FT_UINT32, BASE_HEX, NULL, 0, NULL, HFILL } }, { &hf_iperf2_reserved2, { "Reserved", "iperf2.client.reserved2", FT_UINT32, BASE_HEX, NULL, 0, NULL, HFILL } }, { &hf_iperf2_start_tv_sec, { "Start TV (s)", "iperf2.client.start_tv_sec", FT_UINT32, BASE_DEC, NULL, 0, NULL, HFILL } }, { &hf_iperf2_start_tv_usec, { "Start TV (us)", "iperf2.client.start_tv_usec", FT_UINT32, BASE_DEC, NULL, 0, NULL, HFILL } }, { &hf_iperf2_start_tv, { "Start TV", "iperf2.client.start_tv", FT_ABSOLUTE_TIME, ABSOLUTE_TIME_LOCAL, NULL, 0, NULL, HFILL } }, { &hf_iperf2_fq_ratel, { "Fair-Queuing Rate Lower", "iperf2.client.fq_ratel", FT_UINT32, BASE_DEC, NULL, 0, NULL, HFILL } }, { &hf_iperf2_fq_rateu, { "Fair-Queuing Rate Upper", "iperf2.client.fq_rateu", FT_UINT32, BASE_DEC, NULL, 0, NULL, HFILL } }, { &hf_iperf2_fpsl, { "FPS Lower", "iperf2.client.fpsl", FT_UINT32, BASE_DEC, NULL, 0, NULL, HFILL } }, { &hf_iperf2_fpsu, { "FPS Upper", "iperf2.client.fpsu", FT_UINT32, BASE_DEC, NULL, 0, NULL, HFILL } }, { &hf_iperf2_meanl, { "Mean Lower", "iperf2.client.meanl", FT_UINT32, BASE_DEC, NULL, 0, NULL, HFILL } }, { &hf_iperf2_meanu, { "Mean Upper", "iperf2.client.meanu", FT_UINT32, BASE_DEC, NULL, 0, NULL, HFILL } }, { &hf_iperf2_variancel, { "Variance Lower", "iperf2.client.variancel", FT_UINT32, BASE_DEC, NULL, 0, NULL, HFILL } }, { &hf_iperf2_varianceu, { "Variance Upper", "iperf2.client.varianceu", FT_UINT32, BASE_DEC, NULL, 0, NULL, HFILL } }, { &hf_iperf2_burstipgl, { "Burst Inter-packet Gap Lower", "iperf2.client.burstipgl", FT_UINT32, BASE_DEC, NULL, 0, NULL, HFILL } }, { &hf_iperf2_burstipg, { "Burst Inter-packet Gap", "iperf2.client.burstipg", FT_UINT32, BASE_DEC, NULL, 0, NULL, HFILL } }, { &hf_iperf2_cca_len, { "CCA Length", "iperf2.client.cca_len", FT_UINT16, BASE_DEC, NULL, 0, NULL, HFILL } }, { &hf_iperf2_cca_value, { "CCA Value", "iperf2.client.cca_value", FT_BYTES, BASE_NONE, NULL, 0, NULL, HFILL } }, { &hf_iperf2_bb_size, { "Bounceback Size", "iperf2.client.bb_size", FT_UINT32, BASE_DEC, NULL, 0, NULL, HFILL } }, { &hf_iperf2_bb_id, { "Bounceback ID", "iperf2.client.bb_id", FT_UINT32, BASE_DEC, NULL, 0, NULL, HFILL } }, { &hf_iperf2_bb_flags, { "Bounceback Flags", "iperf2.client.bb_flags", FT_UINT16, BASE_HEX, NULL, 0, NULL, HFILL } }, { &hf_iperf2_header_bbquickack, { "Quick Ack", "iperf2.client.bb_flags_quickack", FT_BOOLEAN, 16, NULL, HEADER_BBQUICKACK, NULL, HFILL } }, { &hf_iperf2_header_bbclocksynced, { "Clock Synced", "iperf2.client.bb_flags_clock_synced", FT_BOOLEAN, 16, NULL, HEADER_BBCLOCKSYNCED, NULL, HFILL } }, { &hf_iperf2_header_bbtos, { "ToS", "iperf2.client.bb_flags_tos", FT_BOOLEAN, 16, NULL, HEADER_BBTOS, NULL, HFILL } }, { &hf_iperf2_header_bbstop, { "Stop", "iperf2.client.bb_flags_stop", FT_BOOLEAN, 16, NULL, HEADER_BBSTOP, NULL, HFILL } }, { &hf_iperf2_header_bbreplysize, { "Reply Size", "iperf2.client.bb_flags_reply_size", FT_BOOLEAN, 16, NULL, HEADER_BBREPLYSIZE, NULL, HFILL } }, { &hf_iperf2_bb_tos, { "Bounceback ToS", "iperf2.client.bb_tos", FT_UINT16, BASE_HEX, NULL, 0, NULL, HFILL } }, { &hf_iperf2_bb_run_time, { "Bounceback Run Time", "iperf2.client.bb_run_time", FT_UINT32, BASE_DEC, NULL, 0, NULL, HFILL } }, { &hf_iperf2_bb_clienttx_ts_sec, { "Client TX Timestamp (s)", "iperf2.client.bb_clienttx_ts_sec", FT_UINT32, BASE_DEC, NULL, 0, NULL, HFILL } }, { &hf_iperf2_bb_clienttx_ts_usec, { "Client TX Timestamp (us)", "iperf2.client.bb_clienttx_ts_usec", FT_UINT32, BASE_DEC, NULL, 0, NULL, HFILL } }, { &hf_iperf2_bb_clienttx_ts, { "Client TX Timestamp", "iperf2.client.bb_clienttx_ts", FT_ABSOLUTE_TIME, ABSOLUTE_TIME_LOCAL, NULL, 0, NULL, HFILL } }, { &hf_iperf2_bb_serverrx_ts_sec, { "Server RX Timestamp (s)", "iperf2.client.bb_serverrx_ts_sec", FT_UINT32, BASE_DEC, NULL, 0, NULL, HFILL } }, { &hf_iperf2_bb_serverrx_ts_usec, { "Server RX Timestamp (us)", "iperf2.client.bb_serverrx_ts_usec", FT_UINT32, BASE_DEC, NULL, 0, NULL, HFILL } }, { &hf_iperf2_bb_serverrx_ts, { "Server RX Timestamp", "iperf2.client.bb_serverrx_ts", FT_ABSOLUTE_TIME, ABSOLUTE_TIME_LOCAL, NULL, 0, NULL, HFILL } }, { &hf_iperf2_bb_servertx_ts_sec, { "Server TX Timestamp (s)", "iperf2.client.bb_servertx_ts_sec", FT_UINT32, BASE_DEC, NULL, 0, NULL, HFILL } }, { &hf_iperf2_bb_servertx_ts_usec, { "Server TX Timestamp (us)", "iperf2.client.bb_servertx_ts_usec", FT_UINT32, BASE_DEC, NULL, 0, NULL, HFILL } }, { &hf_iperf2_bb_servertx_ts, { "Server TX Timestamp", "iperf2.client.bb_servertx_ts", FT_ABSOLUTE_TIME, ABSOLUTE_TIME_LOCAL, NULL, 0, NULL, HFILL } }, { &hf_iperf2_bb_hold, { "Bounceback Hold", "iperf2.client.bb_hold", FT_UINT32, BASE_DEC, NULL, 0, NULL, HFILL } }, { &hf_iperf2_bb_rtt, { "Bounceback RTT", "iperf2.client.bb_rtt", FT_UINT32, BASE_DEC, NULL, 0, NULL, HFILL } }, { &hf_iperf2_bb_read_ts_sec, { "Read Timestamp (s)", "iperf2.client.bb_read_ts_sec", FT_UINT32, BASE_DEC, NULL, 0, NULL, HFILL } }, { &hf_iperf2_bb_read_ts_usec, { "Read Timestamp (us)", "iperf2.client.bb_read_ts_usec", FT_UINT32, BASE_DEC, NULL, 0, NULL, HFILL } }, { &hf_iperf2_bb_read_ts, { "Read Timestamp", "iperf2.client.bb_read_ts", FT_ABSOLUTE_TIME, ABSOLUTE_TIME_LOCAL, NULL, 0, NULL, HFILL } }, { &hf_iperf2_bb_reply_size, { "Bounceback Reply Size", "iperf2.client.bb_reply_size", FT_UINT32, BASE_DEC, NULL, 0, NULL, HFILL } }, { &hf_iperf2_payload, { "Data", "iperf2.client.payload", FT_BYTES, BASE_NONE, NULL, 0, NULL, HFILL } }, }; /* Setup protocol subtree array */ static gint *ett[] = { &ett_iperf2_udp, &ett_iperf2_tcp, &ett_udphdr, &ett_clienthdr, &ett_bbhdr, &ett_extendedhdr, &ett_permit_key, &ett_client_upper_flags, &ett_client_lower_flags, &ett_isochhdr, &ett_fqhdr, &ett_ext_isochhdr, &ett_client_hdr, &ett_client_hdr_flags, &ett_cca_hdr, &ett_bb_hdr_flags, &ett_bbclienttx_ts, &ett_bbserverrx_ts, &ett_bbservertx_ts, &ett_bbread_ts, &ett_data }; /* Register the protocol name and description */ proto_iperf2 = proto_register_protocol("iPerf2 Packet Data", "iPerf2", "iperf2"); proto_register_field_array(proto_iperf2, hf, array_length(hf)); proto_register_subtree_array(ett, array_length(ett)); /* Set up string templates for iperf2 payload */ want = tvbparse_set_oneof(0, NULL, NULL, NULL, tvbparse_string(-1,"0123456789",NULL,NULL,NULL), tvbparse_string(-1,"1234567890",NULL,NULL,NULL), tvbparse_string(-1,"2345678901",NULL,NULL,NULL), tvbparse_string(-1,"3456789012",NULL,NULL,NULL), tvbparse_string(-1,"4567890123",NULL,NULL,NULL), tvbparse_string(-1,"5678901234",NULL,NULL,NULL), tvbparse_string(-1,"6789012345",NULL,NULL,NULL), tvbparse_string(-1,"7890123456",NULL,NULL,NULL), tvbparse_string(-1,"8901234567",NULL,NULL,NULL), tvbparse_string(-1,"9012345678",NULL,NULL,NULL), NULL); want_trailing = tvbparse_chars(-1, 1, 0, "0123456789", NULL, NULL, NULL); iperf2_handle_tcp = register_dissector("iperf2_tcp", dissect_iperf2_tcp, proto_iperf2); iperf2_handle_udp = register_dissector("iperf2_udp", dissect_iperf2_udp, proto_iperf2); } void proto_reg_handoff_iperf2(void) { dissector_add_for_decode_as_with_preference("tcp.port", iperf2_handle_tcp); dissector_add_for_decode_as_with_preference("udp.port", iperf2_handle_udp); } /* * Editor modelines - https://www.wireshark.org/tools/modelines.html * * Local variables: * c-basic-offset: 4 * tab-width: 8 * indent-tabs-mode: nil * End: * * vi: set shiftwidth=4 tabstop=8 expandtab: * :indentSize=4:tabSize=8:noTabs=true: */