Revert "TODO epan/dissectors/asn1/kerberos/packet-kerberos-template.c new GSS flags"
[wireshark-sm.git] / epan / dissectors / packet-iperf3.c
bloba99fe3b611d4f67744e99a4560d6ae353c335519
1 /* packet-iperf3.c
3 * Routines for iPerf3 dissection
4 * by Daniel Mendes <dmendes@redhat.com>,
5 * Jaap Keuter <jaap.keuter@xs4all.nl>
7 * loosely based off iPerf2 dissector
8 * by Anish Bhatt <anish@gatech.edu>
9 * and the iperf3 source code at
10 * https://github.com/esnet/iperf
12 * Wireshark - Network traffic analyzer
13 * By Gerald Combs <gerald@wireshark.org>
14 * Copyright 1998 Gerald Combs
16 * SPDX-License-Identifier: GPL-2.0-or-later
19 #include "config.h"
20 #include <wireshark.h>
21 #include <epan/packet.h>
22 #include <epan/proto_data.h>
23 #include <epan/wmem_scopes.h>
24 #include <epan/expert.h>
25 #include <epan/prefs.h>
26 #include <epan/conversation.h>
27 #include <epan/dissectors/packet-tcp.h>
29 /* From iperf3 source code src/iperf_api.h */
30 #define TEST_START 1
31 #define TEST_RUNNING 2
32 #define RESULT_REQUEST 3 /* not used */
33 #define TEST_END 4
34 #define STREAM_BEGIN 5 /* not used */
35 #define STREAM_RUNNING 6 /* not used */
36 #define STREAM_END 7 /* not used */
37 #define ALL_STREAMS_END 8 /* not used */
38 #define PARAM_EXCHANGE 9
39 #define CREATE_STREAMS 10
40 #define SERVER_TERMINATE 11
41 #define CLIENT_TERMINATE 12
42 #define EXCHANGE_RESULTS 13
43 #define DISPLAY_RESULTS 14
44 #define IPERF_START 15
45 #define IPERF_DONE 16
46 #define ACCESS_DENIED (-1)
47 #define SERVER_ERROR (-2)
49 typedef enum
51 INIT,
52 GENERIC_STATE,
53 PARAM_EXCHANGE_LENGTH,
54 PARAM_EXCHANGE_JSON,
55 EXCHANGE_RESULTS_LENGTH_1,
56 EXCHANGE_RESULTS_JSON_1,
57 EXCHANGE_RESULTS_LENGTH_2,
58 EXCHANGE_RESULTS_JSON_2,
59 DATA
60 } pdu_sequence;
62 void proto_register_iperf3(void);
63 void proto_reg_handoff_iperf3(void);
65 static int proto_iperf3;
67 static int hf_iperf3_sequence;
68 static int hf_iperf3_sec;
69 static int hf_iperf3_usec;
70 static int hf_iperf3_udp_init_msg;
71 static int hf_iperf3_state;
72 static int hf_iperf3_prejson;
73 static int hf_iperf3_cookie;
75 static int ett_iperf3;
76 static int ett_time;
78 static dissector_handle_t iperf3_handle_tcp;
79 static dissector_handle_t iperf3_handle_udp;
80 static dissector_handle_t json_handle;
82 static const value_string iperf3_state_vals[] = {
83 { TEST_START, "TEST_START" },
84 { TEST_RUNNING, "TEST_RUNNING" },
85 { RESULT_REQUEST, "RESULT_REQUEST" },
86 { TEST_END, "TEST_END" },
87 { STREAM_BEGIN, "STREAM_BEGIN" },
88 { STREAM_RUNNING, "STREAM_RUNNING" },
89 { STREAM_END, "STREAM_END" },
90 { ALL_STREAMS_END, "ALL_STREAMS_END" },
91 { PARAM_EXCHANGE, "PARAM_EXCHANGE" },
92 { CREATE_STREAMS, "CREATE_STREAMS" },
93 { SERVER_TERMINATE, "SERVER_TERMINATE" },
94 { CLIENT_TERMINATE, "CLIENT_TERMINATE" },
95 { EXCHANGE_RESULTS, "EXCHANGE_RESULTS" },
96 { DISPLAY_RESULTS, "DISPLAY_RESULTS" },
97 { IPERF_START, "IPERF_START" },
98 { IPERF_DONE, "IPERF_DONE" },
99 { ACCESS_DENIED, "ACCESS_DENIED" },
100 { SERVER_ERROR, "SERVER_ERROR" },
101 { 0, NULL }
104 /* TCP conversation */
105 typedef struct
107 bool control_connection;
108 // Ephemeral packet data
109 unsigned pdu_size;
110 pdu_sequence sequence;
111 } iperf3_tcp_conversation_data;
113 typedef struct
115 unsigned pdu_size;
116 pdu_sequence sequence;
117 } iperf3_tcp_packet_data;
119 /* UDP out-of-order tracking */
120 typedef struct {
121 uint64_t prev_seq_no;
122 wmem_map_t *out_of_order;
123 } udp_conversation_data;
125 static void udp_detect_and_report_out_of_order(packet_info *, proto_item *, uint64_t);
126 static udp_conversation_data *udp_set_conversation_data(packet_info *);
129 /* protocol preferences */
130 static bool iperf3_pref_64bit_seq_no;
131 static bool iperf3_pref_detect_udp_order = true;
132 /* expert info */
133 static expert_field ei_udp_out_of_order;
135 #define IPERF3_UDP_HDR_SIZE 12
136 #define COOKIE_SIZE 37
138 #define IPERF3_INIT_UDP_MSG_SIZE 4
140 #define UDP_CONNECT_MSG 0x36373839 // iperf3 doesn't htonl() convert either
141 #define UDP_CONNECT_REPLY 0x39383736 // the MSG or REPLY so we must accept
142 #define LEGACY_UDP_CONNECT_MSG 0x075bcd15 // accept either endian representation.
143 #define LEGACY_MSG_OPPOSITE_ENDIAN 0x15cd5b07 // luckily current msg and reply are
144 #define LEGACY_UDP_CONNECT_REPLY 0x3ade68b1 // already opposites. this is also why
145 #define LEGACY_REPLY_OPPOSITE_ENDIAN 0xb168de3a // we can't distinguish msg from reply
148 #define DEFINE_CONTROL_PREFACE(protocol) \
149 static void col_info_preface_##protocol(packet_info *pinfo) \
151 col_set_str(pinfo->cinfo, COL_PROTOCOL, "iPerf3"); \
152 col_clear(pinfo->cinfo, COL_INFO); \
153 col_append_ports(pinfo->cinfo, COL_INFO, PT_##protocol, \
154 pinfo->srcport, pinfo->destport); \
157 DEFINE_CONTROL_PREFACE(TCP) /* invoke as col_info_preface_TCP(pinfo) */
158 DEFINE_CONTROL_PREFACE(UDP) /* invoke as col_info_preface_UDP(pinfo) */
160 // Collection of cookies used to differentiate between control and data connections.
161 // See dissect_iperf3_tcp() for details.
162 static wmem_map_t *cookiejar;
164 static int
165 dissect_iperf3_control_pdu(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void *data)
167 iperf3_tcp_conversation_data *conversation_data = (iperf3_tcp_conversation_data *)data;
169 proto_item *ti = proto_tree_add_item(tree, proto_iperf3, tvb, 0, -1, ENC_NA);
170 proto_tree *iperf3_tree = proto_item_add_subtree(ti, ett_iperf3);
172 switch (conversation_data->sequence)
174 // ------
175 case INIT:
177 const uint8_t *cookie;
178 proto_tree_add_item_ret_string(iperf3_tree, hf_iperf3_cookie, tvb, 0, COOKIE_SIZE,
179 ENC_ASCII, pinfo->pool, &cookie);
180 col_append_fstr(pinfo->cinfo, COL_INFO, " Cookie: \"%s\"", cookie);
182 conversation_data->pdu_size = 1;
183 conversation_data->sequence = GENERIC_STATE;
184 break;
186 case GENERIC_STATE:
188 int8_t state_code = tvb_get_int8(tvb, 0);
189 const char *msg = val_to_str(state_code, iperf3_state_vals, "Unknown %d");
190 col_append_fstr(pinfo->cinfo, COL_INFO, " %s(%" PRIi8 ")", msg, state_code);
191 col_set_fence(pinfo->cinfo, COL_INFO);
192 proto_tree_add_item(iperf3_tree, hf_iperf3_state, tvb, 0, 1, ENC_BIG_ENDIAN);
194 switch (state_code)
196 case PARAM_EXCHANGE:
197 conversation_data->pdu_size = 4;
198 conversation_data->sequence = PARAM_EXCHANGE_LENGTH;
199 break;
200 case EXCHANGE_RESULTS:
201 conversation_data->pdu_size = 4;
202 conversation_data->sequence = EXCHANGE_RESULTS_LENGTH_1;
203 break;
204 default:
205 break;
207 break;
209 // ------
210 case PARAM_EXCHANGE_LENGTH:
212 uint32_t json_size = tvb_get_uint32(tvb, 0, ENC_BIG_ENDIAN);
213 col_append_fstr(pinfo->cinfo, COL_INFO,
214 " Next message is JSON of this length: %" PRIu32, json_size);
215 proto_tree_add_item(iperf3_tree, hf_iperf3_prejson, tvb, 0, 4, ENC_BIG_ENDIAN);
217 conversation_data->pdu_size = json_size;
218 conversation_data->sequence = PARAM_EXCHANGE_JSON;
219 break;
221 case PARAM_EXCHANGE_JSON:
223 uint32_t nbytes = tvb_reported_length(tvb);
224 uint8_t *buffer = tvb_get_string_enc(pinfo->pool, tvb, 0, (int)nbytes, ENC_UTF_8);
225 col_append_fstr(pinfo->cinfo, COL_INFO, " %s", buffer);
226 call_dissector(json_handle, tvb, pinfo, iperf3_tree);
228 conversation_data->pdu_size = 1;
229 conversation_data->sequence = GENERIC_STATE;
230 break;
232 // ------
233 case EXCHANGE_RESULTS_LENGTH_1:
235 uint32_t json_size = tvb_get_uint32(tvb, 0, ENC_BIG_ENDIAN);
236 col_append_fstr(pinfo->cinfo, COL_INFO,
237 " Next message is JSON of this length: %" PRIu32, json_size);
238 proto_tree_add_item(iperf3_tree, hf_iperf3_prejson, tvb, 0, 4, ENC_BIG_ENDIAN);
240 conversation_data->pdu_size = json_size;
241 conversation_data->sequence = EXCHANGE_RESULTS_JSON_1;
242 break;
244 case EXCHANGE_RESULTS_JSON_1:
246 uint32_t nbytes = tvb_reported_length(tvb);
247 uint8_t *buffer = tvb_get_string_enc(pinfo->pool, tvb, 0, (int)nbytes, ENC_UTF_8);
248 col_append_fstr(pinfo->cinfo, COL_INFO, " %s", buffer);
249 call_dissector(json_handle, tvb, pinfo, iperf3_tree);
251 conversation_data->pdu_size = 4;
252 conversation_data->sequence = EXCHANGE_RESULTS_LENGTH_2;
253 break;
255 case EXCHANGE_RESULTS_LENGTH_2:
257 uint32_t json_size = tvb_get_uint32(tvb, 0, ENC_BIG_ENDIAN);
258 col_append_fstr(pinfo->cinfo, COL_INFO,
259 " Next message is JSON of this length: %" PRIu32, json_size);
260 proto_tree_add_item(iperf3_tree, hf_iperf3_prejson, tvb, 0, 4, ENC_BIG_ENDIAN);
262 conversation_data->pdu_size = json_size;
263 conversation_data->sequence = EXCHANGE_RESULTS_JSON_2;
264 break;
266 case EXCHANGE_RESULTS_JSON_2:
268 uint32_t nbytes = tvb_reported_length(tvb);
269 uint8_t *buffer = tvb_get_string_enc(pinfo->pool, tvb, 0, (int)nbytes, ENC_UTF_8);
270 col_append_fstr(pinfo->cinfo, COL_INFO, " %s", buffer);
271 call_dissector(json_handle, tvb, pinfo, iperf3_tree);
273 conversation_data->pdu_size = 1;
274 conversation_data->sequence = GENERIC_STATE;
275 break;
277 // ------
278 default:
279 DISSECTOR_ASSERT_NOT_REACHED();
282 return tvb_reported_length(tvb);
285 static int
286 dissect_iperf3_data_pdu(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void *data)
288 iperf3_tcp_conversation_data *conversation_data = (iperf3_tcp_conversation_data *)data;
290 proto_item *ti = proto_tree_add_item(tree, proto_iperf3, tvb, 0, -1, ENC_NA);
291 proto_tree *iperf3_tree = proto_item_add_subtree(ti, ett_iperf3);
293 switch (conversation_data->sequence)
295 case INIT:
297 const uint8_t *cookie;
298 proto_tree_add_item_ret_string(iperf3_tree, hf_iperf3_cookie, tvb, 0, COOKIE_SIZE,
299 ENC_ASCII, pinfo->pool, &cookie);
300 col_info_preface_TCP(pinfo);
301 col_append_fstr(pinfo->cinfo, COL_INFO, " Cookie: \"%s\"", cookie);
303 conversation_data->pdu_size = 0; // The whole TVBuff
304 conversation_data->sequence = DATA;
305 break;
307 case DATA:
309 call_data_dissector(tvb, pinfo, tree);
310 break;
312 default:
313 DISSECTOR_ASSERT_NOT_REACHED();
316 return tvb_reported_length(tvb);
319 static unsigned
320 get_iperf3_pdu_len(packet_info *pinfo _U_, tvbuff_t *tvb _U_, int offset _U_, void *data)
322 iperf3_tcp_conversation_data *conversation_data = (iperf3_tcp_conversation_data *)data;
324 return conversation_data->pdu_size ? conversation_data->pdu_size : tvb_reported_length(tvb);
327 static int
328 dissect_iperf3_tcp(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void *data _U_)
331 * Determine control connection for *any* packet
333 * - Get the conversation data
334 * - If present, retrieve control connection status
335 * - If missing, determine as follows:
336 * - Take cookie from the packet (which is assumed to be first in the data packet)
337 * - Lookup cookie in cookiejar
338 * - If cookie present in jar, this is not a control connection
339 * - If cookie abssent from jar, this is a control connection
340 * - Add cookie to jar
341 * - Create conversation data
344 uint8_t cookie[COOKIE_SIZE];
346 iperf3_tcp_conversation_data *conversation_data = conversation_get_proto_data(find_conversation_pinfo(pinfo, 0), proto_iperf3);
347 iperf3_tcp_packet_data *packet_data = p_get_proto_data(wmem_file_scope(), pinfo, proto_iperf3, 0);
349 if (!conversation_data) {
350 conversation_data = wmem_new0(wmem_file_scope(), iperf3_tcp_conversation_data);
351 conversation_data->control_connection = false;
352 // Data required for PDU determination
353 conversation_data->pdu_size = COOKIE_SIZE;
354 conversation_data->sequence = INIT;
356 if (tvb_get_raw_bytes_as_stringz(tvb, 0, COOKIE_SIZE, cookie) == COOKIE_SIZE-1) {
357 if (!wmem_map_contains(cookiejar, cookie)) {
358 // This is a new control connection (or a data connection for which the control connection has not been seen, fail)
359 char *cookie_save = wmem_strndup(wmem_file_scope(), cookie, COOKIE_SIZE);
360 if (wmem_map_insert(cookiejar, cookie_save, NULL)) { // No value to store, only the cookie
361 // We insert a new cookie in the jar, which now says there's already one.
362 DISSECTOR_ASSERT_NOT_REACHED();
364 conversation_data->control_connection = true;
368 conversation_add_proto_data(find_conversation_pinfo(pinfo, 0), proto_iperf3, conversation_data);
372 * Determine the first PDU for *any* packet
374 * - If the packet has not been seen before, take PDU size and sequence from the conversation.
375 * (This either comes from a newly initialized conversation, or from
376 * the completion of the dissection of the payload of the previously dissected TCP packet.)
377 * - Store the PDU size and sequence with the packet.
378 * - If the packet was seen before, take the PDU size and sequence from the stored packet data
379 * and store it with the conversation.
380 * (This guarantees that the PDU size and sequence for this particular packet is available,
381 * even if dissected out of order.)
382 * - Call the dissection function with the conversation data, which is relevant for the
383 * current TCP payload.
386 if (!PINFO_FD_VISITED(pinfo)) {
387 packet_data = wmem_new0(wmem_file_scope(), iperf3_tcp_packet_data);
388 packet_data->pdu_size = conversation_data->pdu_size;
389 packet_data->sequence = conversation_data->sequence;
390 p_add_proto_data(wmem_file_scope(), pinfo, proto_iperf3, 0, packet_data);
391 } else {
392 DISSECTOR_ASSERT(packet_data);
394 conversation_data->pdu_size = packet_data->pdu_size;
395 conversation_data->sequence = packet_data->sequence;
398 if (conversation_data->control_connection) {
399 col_info_preface_TCP(pinfo);
400 tcp_dissect_pdus(tvb, pinfo, tree, false, 1, get_iperf3_pdu_len, dissect_iperf3_control_pdu, conversation_data);
401 } else {
402 dissect_iperf3_data_pdu(tvb, pinfo, tree, conversation_data);
405 return tvb_reported_length(tvb);
408 static int
409 dissect_iperf3_udp(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void *data _U_)
411 proto_item *ti;
412 proto_tree *iperf3_tree, *time_tree;
413 uint32_t offset = 0;
414 uint32_t nbytes = tvb_reported_length(tvb);
416 uint64_t maybe_sequence_num;
418 /************** UDP CONTROL *****************/
419 if (nbytes == IPERF3_INIT_UDP_MSG_SIZE)
421 /* Due to the fact that UDP_CONNECT_MSG and UDP_CONNECT_REPLY are each others
422 reverse it does not matter which endianness is used. */
423 uint32_t init_cxn_msg = tvb_get_uint32(tvb, offset, ENC_HOST_ENDIAN);
424 if (init_cxn_msg != UDP_CONNECT_MSG &&
425 init_cxn_msg != UDP_CONNECT_REPLY &&
426 init_cxn_msg != LEGACY_UDP_CONNECT_MSG &&
427 init_cxn_msg != LEGACY_MSG_OPPOSITE_ENDIAN &&
428 init_cxn_msg != LEGACY_UDP_CONNECT_REPLY &&
429 init_cxn_msg != LEGACY_REPLY_OPPOSITE_ENDIAN)
430 return 0;
431 col_info_preface_UDP(pinfo);
432 col_append_str(pinfo->cinfo, COL_INFO, " Establishing UDP connection...");
434 ti = proto_tree_add_item(tree, proto_iperf3, tvb, offset, -1, ENC_NA);
435 iperf3_tree = proto_item_add_subtree(ti, ett_iperf3);
436 proto_tree_add_item(iperf3_tree, hf_iperf3_udp_init_msg, tvb, offset, 4, ENC_BIG_ENDIAN);
438 return IPERF3_INIT_UDP_MSG_SIZE;
441 /************** UDP DATA ********************/
443 if (tvb_reported_length(tvb) < IPERF3_UDP_HDR_SIZE)
444 return 0;
446 col_info_preface_UDP(pinfo);
447 ti = proto_tree_add_item(tree, proto_iperf3, tvb, offset, -1, ENC_NA);
448 iperf3_tree = proto_item_add_subtree(ti, ett_iperf3);
450 time_tree = proto_tree_add_subtree(iperf3_tree, tvb, offset, 8, ett_time, &ti, "");
452 int32_t seconds;
453 uint32_t useconds;
455 proto_tree_add_item_ret_int(time_tree, hf_iperf3_sec, tvb, offset, 4, ENC_BIG_ENDIAN, &seconds);
456 offset += 4;
457 proto_tree_add_item_ret_uint(time_tree, hf_iperf3_usec, tvb, offset, 4, ENC_BIG_ENDIAN, &useconds);
458 offset += 4;
460 proto_item_set_text(ti, "Time Sent: %.7f seconds", seconds + (useconds / 1000000.0));
461 /* empirically: no precision below 7 digits */
463 /* let users choose if they're using 64 bit sequence numbers, but still want sensible
464 default... so we detect if the top 32 bits are all zero. if so then they must
465 be using 64 bit numbers regardless */
466 maybe_sequence_num = tvb_get_uint32(tvb, offset, ENC_BIG_ENDIAN);
467 if (iperf3_pref_64bit_seq_no || maybe_sequence_num == 0) {
468 proto_tree_add_item_ret_uint64(iperf3_tree, hf_iperf3_sequence, tvb, offset, 8, ENC_BIG_ENDIAN, &maybe_sequence_num);
469 offset += 8;
470 } else {
471 proto_tree_add_item(iperf3_tree, hf_iperf3_sequence, tvb, offset, 4, ENC_BIG_ENDIAN);
472 offset += 4;
475 col_append_fstr(pinfo->cinfo, COL_INFO, " [%" PRIu64 "] Time sent=%.7f length=%" PRIu32 " bytes",
476 maybe_sequence_num, seconds + (useconds / 1000000.0), nbytes);
478 if (iperf3_pref_detect_udp_order)
479 udp_detect_and_report_out_of_order(pinfo, ti, maybe_sequence_num);
481 tvbuff_t *next_tvb = tvb_new_subset_remaining(tvb, offset);
482 call_data_dissector(next_tvb, pinfo, tree); /* deals with payload size = 0
483 which happens with -l=16 and --udp-64bit-sequence number */
485 return nbytes;
488 static void
489 udp_detect_and_report_out_of_order(packet_info *pinfo, proto_item *ti, uint64_t sequence_num)
491 /* Record out of order packets by keeping a per-conversation set of
492 lost packets. The first time the packets are dissected we add them to the set
493 based on the comparison to the per-conversation "prev_seq_no" variable that gets
494 initialized to 0 when the connection is first seen
496 per-conversation state works well with bidirectional and parallel connections
497 and fast even with ~500k packets out-of-order */
499 udp_conversation_data *conversation;
500 conversation = conversation_get_proto_data(find_conversation_pinfo(pinfo, 0), proto_iperf3);
501 if (!conversation)
502 conversation = udp_set_conversation_data(pinfo);
504 /* detect */
505 if (!PINFO_FD_VISITED(pinfo)) {
506 bool is_out_of_order = (sequence_num != conversation->prev_seq_no + 1);
507 conversation->prev_seq_no = sequence_num;
509 if (is_out_of_order) {
510 wmem_map_insert(conversation->out_of_order, GUINT_TO_POINTER(pinfo->num), NULL);
513 /* report */
514 if (wmem_map_contains(conversation->out_of_order, GUINT_TO_POINTER(pinfo->num))) {
515 col_prepend_fstr(pinfo->cinfo, COL_INFO, "(Loss or out-of-order delivery) ");
516 expert_add_info(pinfo, ti, &ei_udp_out_of_order);
520 static udp_conversation_data *
521 udp_set_conversation_data(packet_info *pinfo)
523 udp_conversation_data *conversation;
524 conversation = wmem_new0(wmem_file_scope(), udp_conversation_data);
525 conversation->out_of_order = wmem_map_new(wmem_file_scope(), g_direct_hash, g_direct_equal);
526 conversation->prev_seq_no = 0;
527 conversation_add_proto_data(find_conversation_pinfo(pinfo, 0), proto_iperf3, conversation);
528 return conversation;
529 /* could (very improbably) run iperf3 test multiple times in same capture
530 and get the same random port.. would be seen as the same conversation and so you'd
531 over count out of order packets... they can just turn off out-of-order detection */
534 void proto_register_iperf3(void)
536 /* Setup list of header fields */
537 static hf_register_info hf[] = {
538 // TCP
539 {&hf_iperf3_state,
540 {"State ID", "iperf3.state", FT_INT8, BASE_DEC, VALS(iperf3_state_vals),
541 0, NULL, HFILL}},
542 {&hf_iperf3_prejson,
543 {"Pre-JSON length identifier", "iperf3.prejson", FT_UINT32, BASE_DEC,
544 NULL, 0, NULL, HFILL}},
545 {&hf_iperf3_cookie,
546 {"Cookie", "iperf3.cookie", FT_STRINGZ, BASE_NONE,
547 NULL, 0, NULL, HFILL}},
548 // UDP
549 {&hf_iperf3_sec,
550 {"iPerf3 sec", "iperf3.sec", FT_INT32, BASE_DEC,
551 NULL, 0, NULL, HFILL}},
552 {&hf_iperf3_usec,
553 {"iPerf3 usec", "iperf3.usec", FT_UINT32, BASE_DEC,
554 NULL, 0, NULL, HFILL}},
555 {&hf_iperf3_sequence,
556 {"iPerf3 sequence", "iperf3.sequence", FT_UINT64, BASE_DEC,
557 NULL, 0, NULL, HFILL}},
558 {&hf_iperf3_udp_init_msg,
559 {"UDP initialization message", "iperf3.init_msg", FT_UINT32, BASE_HEX,
560 NULL, 0, NULL, HFILL}},
563 /* Setup protocol subtree array */
564 static int *ett[] = {
565 &ett_iperf3,
566 &ett_time
569 /* Register the protocol name and description */
570 proto_iperf3 = proto_register_protocol("iPerf3 Speed Test", "iPerf3", "iperf3");
572 /* Register configuration preferences */
573 module_t *iperf3_module = prefs_register_protocol(proto_iperf3, NULL);
574 prefs_register_bool_preference(iperf3_module, "udp_sequence_64bit",
575 "Use 64 bit sequence numbers for UDP data",
576 "Whether iPerf3 was run with --udp-counters-64bit flag set",
577 &iperf3_pref_64bit_seq_no);
578 prefs_register_bool_preference(iperf3_module, "detect_udp_errors",
579 "Detect packet loss and out of order delivery for UDP data",
580 "Attempt to detect when a packets sequence number does not match the previous ones +1",
581 &iperf3_pref_detect_udp_order);
583 /* Setup list of expert warnings */
584 static ei_register_info ei[] = {
585 {&ei_udp_out_of_order,
586 {"iperf3.outoforder", PI_SEQUENCE, PI_NOTE,
587 "UDP packet loss or out of order delivery", EXPFILL}},
589 /* Register expert mode warnings */
590 expert_module_t* expert_iperf3 = expert_register_protocol(proto_iperf3);
591 expert_register_field_array(expert_iperf3, ei, array_length(ei));
593 proto_register_field_array(proto_iperf3, hf, array_length(hf));
594 proto_register_subtree_array(ett, array_length(ett));
596 iperf3_handle_tcp = register_dissector("iperf3_tcp", dissect_iperf3_tcp, proto_iperf3);
597 iperf3_handle_udp = register_dissector("iperf3_udp", dissect_iperf3_udp, proto_iperf3);
599 cookiejar = wmem_map_new_autoreset(wmem_epan_scope(), wmem_file_scope(), wmem_str_hash, g_str_equal);
602 void proto_reg_handoff_iperf3(void)
604 json_handle = find_dissector("json");
606 dissector_add_uint_range_with_preference("tcp.port", "5201", iperf3_handle_tcp);
607 dissector_add_uint_range_with_preference("udp.port", "5201", iperf3_handle_udp);
611 * Editor modelines - https://www.wireshark.org/tools/modelines.html
613 * Local variables:
614 * c-basic-offset: 4
615 * tab-width: 8
616 * indent-tabs-mode: nil
617 * End:
619 * vi: set shiftwidth=4 tabstop=8 expandtab:
620 * :indentSize=4:tabSize=8:noTabs=true: