Bug Summary

File:builds/wireshark/wireshark/epan/dissectors/packet-udx.c
Warning:line 1043, column 9
Value stored to 'offset' is never read

Annotated Source Code

Press '?' to see keyboard shortcuts

clang -cc1 -cc1 -triple x86_64-pc-linux-gnu -analyze -disable-free -clear-ast-before-backend -disable-llvm-verifier -discard-value-names -main-file-name packet-udx.c -analyzer-checker=core -analyzer-checker=apiModeling -analyzer-checker=unix -analyzer-checker=deadcode -analyzer-checker=security.insecureAPI.UncheckedReturn -analyzer-checker=security.insecureAPI.getpw -analyzer-checker=security.insecureAPI.gets -analyzer-checker=security.insecureAPI.mktemp -analyzer-checker=security.insecureAPI.mkstemp -analyzer-checker=security.insecureAPI.vfork -analyzer-checker=nullability.NullPassedToNonnull -analyzer-checker=nullability.NullReturnedFromNonnull -analyzer-output plist -w -setup-static-analyzer -mrelocation-model pic -pic-level 2 -fhalf-no-semantic-interposition -fno-delete-null-pointer-checks -mframe-pointer=all -relaxed-aliasing -fmath-errno -ffp-contract=on -fno-rounding-math -ffloat16-excess-precision=fast -fbfloat16-excess-precision=fast -mconstructor-aliases -funwind-tables=2 -target-cpu x86-64 -tune-cpu generic -debugger-tuning=gdb -fdebug-compilation-dir=/builds/wireshark/wireshark/build -fcoverage-compilation-dir=/builds/wireshark/wireshark/build -resource-dir /usr/lib/llvm-22/lib/clang/22 -isystem /usr/include/glib-2.0 -isystem /usr/lib/x86_64-linux-gnu/glib-2.0/include -isystem /builds/wireshark/wireshark/epan/dissectors -isystem /builds/wireshark/wireshark/build/epan/dissectors -isystem /usr/include/mit-krb5 -isystem /usr/include/libxml2 -isystem /builds/wireshark/wireshark/epan -D CARES_NO_DEPRECATED -D G_DISABLE_DEPRECATED -D G_DISABLE_SINGLE_INCLUDES -D WS_BUILD_DLL -D WS_DEBUG -D WS_DEBUG_UTF_8 -I /builds/wireshark/wireshark/build -I /builds/wireshark/wireshark -I /builds/wireshark/wireshark/include -D _GLIBCXX_ASSERTIONS -internal-isystem /usr/lib/llvm-22/lib/clang/22/include -internal-isystem /usr/local/include -internal-isystem /usr/lib/gcc/x86_64-linux-gnu/16/../../../../x86_64-linux-gnu/include -internal-externc-isystem /usr/include/x86_64-linux-gnu -internal-externc-isystem /include -internal-externc-isystem /usr/include -fmacro-prefix-map=/builds/wireshark/wireshark/= -fmacro-prefix-map=/builds/wireshark/wireshark/build/= -fmacro-prefix-map=../= -Wno-format-nonliteral -std=gnu17 -ferror-limit 19 -fvisibility=hidden -fwrapv -fwrapv-pointer -fstrict-flex-arrays=3 -stack-protector 2 -fstack-clash-protection -fcf-protection=full -fgnuc-version=4.2.1 -fskip-odr-check-in-gmf -fexceptions -fcolor-diagnostics -analyzer-output=html -faddrsig -fdwarf2-cfi-asm -o /builds/wireshark/wireshark/sbout/2026-08-14-100430-3660-1 -x c /builds/wireshark/wireshark/epan/dissectors/packet-udx.c
1/* packet-udx.c
2 * Routines for UDX dissection
3 * Copyright 2026, Frank <[email protected]>
4 *
5 * UDX is a reliable, multiplexed, UDP-based transport protocol used by the
6 * Holepunch peer-to-peer stack. Reference implementation:
7 * https://github.com/holepunchto/libudx
8 *
9 * Wireshark - Network traffic analyzer
10 * By Gerald Combs <[email protected]>
11 * Copyright 1998 Gerald Combs
12 *
13 * SPDX-License-Identifier: GPL-2.0-or-later
14 */
15
16#include "config.h"
17
18#include <math.h>
19
20#include <epan/packet.h>
21#include <epan/conversation.h>
22#include <epan/expert.h>
23#include <epan/prefs.h>
24#include <epan/proto_data.h>
25#include <epan/follow.h>
26#include <epan/addr_resolv.h>
27#include <epan/tap.h>
28#include "packet-udp.h"
29#include <wsutil/wmem/wmem_map.h>
30#include <wsutil/wmem/wmem_tree.h>
31
32/*
33 * UDX wire format (all multi-byte fields little-endian):
34 *
35 * offset size field
36 * 0 1 magic (0xff)
37 * 1 1 version (1)
38 * 2 1 type flags
39 * 3 1 data offset: bytes between the fixed header and the payload,
40 * occupied by SACK blocks, or by padding on MTU probes
41 * 4 4 id - the *receiver's* stream id
42 * 8 4 window - sender's receive window in bytes
43 * 12 4 seq - per-PACKET sequence counter (not bytes)
44 * 16 4 ack - next seq expected from the peer
45 * 20 8*n SACK blocks: pairs of uint32 (start, end) seq ranges
46 * ... payload
47 */
48
49#define UDX_HEADER_SIZE20 20
50#define UDX_MAGIC_BYTE0xff 0xff
51#define UDX_VERSION1 1
52
53#define UDX_FLAG_DATA0x01 0x01
54#define UDX_FLAG_END0x02 0x02
55#define UDX_FLAG_SACK0x04 0x04
56#define UDX_FLAG_MESSAGE0x08 0x08
57#define UDX_FLAG_DESTROY0x10 0x10
58#define UDX_FLAG_HEARTBEAT0x20 0x20
59#define UDX_FLAG_MASK0x3f 0x3f
60
61/*
62 * Sequence numbers count packets and wrap at 2^32, so all comparisons are
63 * made in circular arithmetic.
64 */
65#define UDX_SEQ_LT(a, b)((int32_t)((a) - (b)) < 0) ((int32_t)((a) - (b)) < 0)
66#define UDX_SEQ_GT(a, b)((int32_t)((a) - (b)) > 0) ((int32_t)((a) - (b)) > 0)
67#define UDX_SEQ_GEQ(a, b)((int32_t)((a) - (b)) >= 0) ((int32_t)((a) - (b)) >= 0)
68
69/* Bounds the SACK blocks examined per packet; data_offset caps the area at
70 * 255 bytes, i.e. 31 blocks. */
71#define UDX_MAX_SACK_BLOCKS32 32
72
73/* Floor for the derived retransmission timeout, in seconds. Below this a
74 * repeat is attributed to loss recovery rather than to a timer firing. */
75#define UDX_MIN_RTO0.2 0.2
76
77/* A repeat arriving within this window of the original is a duplicate
78 * datagram rather than anything the sender chose to send again. */
79#define UDX_DUP_WINDOW0.0005 0.0005
80
81/* Shortest pause credited to a tail loss probe timer when no round-trip
82 * time has been measured yet. */
83#define UDX_MIN_PROBE_DELAY0.010 0.010
84
85void proto_register_udx(void);
86void proto_reg_handoff_udx(void);
87
88static dissector_handle_t udx_handle;
89
90static int proto_udx;
91
92static bool_Bool udx_analyze_sequence_numbers = true1;
93
94static int udx_follow_tap;
95
96/* Stream numbers are handed out across the whole capture so that a filter
97 * such as "udx.stream eq 3" identifies exactly one stream. */
98static uint32_t udx_stream_count;
99
100/* Queued for the follow taps: one packet's payload, where it belongs in the
101 * stream and which side sent it. */
102typedef struct udx_follow_tap_data {
103 tvbuff_t *tvb;
104 uint32_t stream;
105 uint32_t offset; /* position of this packet within its direction */
106 bool_Bool from_server;
107} udx_follow_tap_data_t;
108
109/* One transmitted packet, remembered so that a later acknowledgement can be
110 * linked back to it. */
111typedef struct udx_seg {
112 uint32_t frame;
113 nstime_t ts;
114 uint32_t len;
115 uint32_t acked_in_frame;
116 nstime_t ack_ts;
117 unsigned retrans;
118 bool_Bool sacked;
119} udx_seg_t;
120
121/* One direction of one stream: the packets one endpoint sends bearing the
122 * peer's stream id. */
123typedef struct udx_flow {
124 uint32_t id;
125 unsigned dir;
126 unsigned order; /* creation order within this direction */
127 nstime_t first_ts;
128 wmem_tree_t *segs; /* seq -> udx_seg_t */
129 uint32_t base_seq; /* first sequence number seen on this flow */
130 uint32_t max_seq; /* highest sequence number sent */
131 bool_Bool have_seq;
132 uint32_t outstanding_bytes;
133 uint32_t outstanding_pkts;
134 uint32_t max_ack; /* highest acknowledgement this flow emitted */
135 bool_Bool have_ack;
136 uint32_t max_sacked; /* highest sequence the peer selectively acked */
137 bool_Bool have_sacked;
138 uint32_t last_rwnd;
139 bool_Bool rwnd_zero;
140 double srtt;
141 double rttvar;
142 bool_Bool have_rtt;
143 uint32_t stream_num;
144 struct udx_flow *paired;
145 struct udx_flow *cand; /* pairing candidate under consideration */
146 unsigned cand_hits;
147} udx_flow_t;
148
149typedef struct udx_conv {
150 wmem_map_t *flows; /* (dir << 32 | id) -> udx_flow_t */
151 unsigned n_flows[2];
152 unsigned client_dir; /* the side that sent first is the client */
153 bool_Bool have_client_dir;
154} udx_conv_t;
155
156/* Verdicts reached on the first pass and replayed on every later visit. */
157#define UDX_A_RETRANS0x0001 0x0001
158#define UDX_A_FAST_RETRANS0x0002 0x0002
159#define UDX_A_RTO_RETRANS0x0004 0x0004
160#define UDX_A_TLP0x0008 0x0008
161#define UDX_A_SPURIOUS0x0010 0x0010
162#define UDX_A_OUT_OF_ORDER0x0020 0x0020
163#define UDX_A_LOST_SEGMENT0x0040 0x0040
164#define UDX_A_KEEPALIVE0x0080 0x0080
165#define UDX_A_ZERO_WIN_PROBE0x0100 0x0100
166#define UDX_A_ZERO_WIN0x0200 0x0200
167#define UDX_A_WINDOW_UPDATE0x0400 0x0400
168#define UDX_A_MTU_PROBE0x0800 0x0800
169#define UDX_A_END0x1000 0x1000
170#define UDX_A_DESTROY0x2000 0x2000
171#define UDX_A_DUPLICATE0x4000 0x4000
172
173typedef struct udx_ppd {
174 uint32_t flags;
175 uint32_t acks_frame; /* frame this packet acknowledges */
176 nstime_t ack_rtt;
177 bool_Bool have_ack_rtt;
178 uint32_t bytes_in_flight;
179 uint32_t packets_in_flight;
180 uint32_t seq; /* sender sequence, to find our own segment */
181 bool_Bool tracked; /* this packet consumed a sequence number */
182 uint32_t stream; /* stream number at analysis time */
183 uint32_t follow_offset; /* position within this direction */
184 bool_Bool from_server;
185 bool_Bool follow_ok; /* payload belongs in the reassembled stream */
186 udx_flow_t *flow;
187} udx_ppd_t;
188
189static int hf_udx_magic;
190static int hf_udx_version;
191static int hf_udx_flags;
192static int hf_udx_flags_data;
193static int hf_udx_flags_end;
194static int hf_udx_flags_sack;
195static int hf_udx_flags_message;
196static int hf_udx_flags_destroy;
197static int hf_udx_flags_heartbeat;
198static int hf_udx_data_offset;
199static int hf_udx_id;
200static int hf_udx_window;
201static int hf_udx_seq;
202static int hf_udx_ack;
203static int hf_udx_sacks;
204static int hf_udx_sack_block;
205static int hf_udx_sack_start;
206static int hf_udx_sack_end;
207static int hf_udx_padding;
208static int hf_udx_payload;
209static int hf_udx_payload_len;
210static int hf_udx_stream;
211static int hf_udx_analysis;
212static int hf_udx_analysis_acks_frame;
213static int hf_udx_analysis_acked_in;
214static int hf_udx_analysis_ack_rtt;
215static int hf_udx_analysis_bytes_in_flight;
216static int hf_udx_analysis_pkts_in_flight;
217static int hf_udx_analysis_no_reverse;
218
219static int ett_udx;
220static int ett_udx_flags;
221static int ett_udx_sacks;
222static int ett_udx_sack_block;
223static int ett_udx_analysis;
224
225static expert_field ei_udx_retrans;
226static expert_field ei_udx_fast_retrans;
227static expert_field ei_udx_rto_retrans;
228static expert_field ei_udx_tlp;
229static expert_field ei_udx_spurious_retrans;
230static expert_field ei_udx_duplicate;
231static expert_field ei_udx_out_of_order;
232static expert_field ei_udx_lost_segment;
233static expert_field ei_udx_keepalive;
234static expert_field ei_udx_zero_window_probe;
235static expert_field ei_udx_zero_window;
236static expert_field ei_udx_window_update;
237static expert_field ei_udx_mtu_probe;
238static expert_field ei_udx_end;
239static expert_field ei_udx_destroy;
240
241static int * const udx_flag_fields[] = {
242 &hf_udx_flags_data,
243 &hf_udx_flags_end,
244 &hf_udx_flags_sack,
245 &hf_udx_flags_message,
246 &hf_udx_flags_destroy,
247 &hf_udx_flags_heartbeat,
248 NULL((void*)0)
249};
250
251/* Build a "DATA,SACK"-style summary of the flags byte; bare 0 is an ACK. */
252static void
253udx_flags_to_str(uint8_t flags, char *buf, size_t buf_len)
254{
255 static const struct {
256 uint8_t bit;
257 const char *name;
258 } bits[] = {
259 { UDX_FLAG_DATA0x01, "DATA" },
260 { UDX_FLAG_END0x02, "END" },
261 { UDX_FLAG_SACK0x04, "SACK" },
262 { UDX_FLAG_MESSAGE0x08, "MESSAGE" },
263 { UDX_FLAG_DESTROY0x10, "DESTROY" },
264 { UDX_FLAG_HEARTBEAT0x20, "HEARTBEAT" },
265 };
266 size_t pos = 0;
267
268 if (flags == 0) {
269 (void) g_strlcpy(buf, "ACK", buf_len);
270 return;
271 }
272 buf[0] = '\0';
273 for (size_t i = 0; i < array_length(bits)(sizeof (bits) / sizeof (bits)[0]); i++) {
274 if (flags & bits[i].bit) {
275 if (pos > 0)
276 pos += g_strlcpy(buf + pos, ",", buf_len - pos);
277 pos += g_strlcpy(buf + pos, bits[i].name, buf_len - pos);
278 }
279 }
280}
281
282/*
283 * A stable label for the two endpoints of the enclosing UDP conversation.
284 * Which endpoint gets 0 does not matter; only that a given endpoint keeps
285 * the same label for the whole capture.
286 */
287static unsigned
288udx_direction(const packet_info *pinfo)
289{
290 int c = cmp_address(&pinfo->src, &pinfo->dst);
291
292 if (c != 0)
293 return (c < 0) ? 0 : 1;
294 return (pinfo->srcport < pinfo->destport) ? 0 : 1;
295}
296
297static udx_flow_t *
298udx_get_flow(udx_conv_t *conv, unsigned dir, uint32_t id, const nstime_t *ts)
299{
300 uint64_t key = ((uint64_t) dir << 32) | id;
301 udx_flow_t *flow = (udx_flow_t *) wmem_map_lookup(conv->flows, &key);
302 uint64_t *key_copy;
303
304 if (flow != NULL((void*)0))
305 return flow;
306
307 flow = wmem_new0(wmem_file_scope(), udx_flow_t)((udx_flow_t*)wmem_alloc0((wmem_file_scope()), sizeof(udx_flow_t
)))
;
308 flow->id = id;
309 flow->dir = dir;
310 flow->order = conv->n_flows[dir]++;
311 flow->first_ts = *ts;
312 flow->segs = wmem_tree_new(wmem_file_scope());
313 flow->stream_num = udx_stream_count++;
314
315 key_copy = wmem_new(wmem_file_scope(), uint64_t)((uint64_t*)wmem_alloc((wmem_file_scope()), sizeof(uint64_t))
)
;
316 *key_copy = key;
317 wmem_map_insert(conv->flows, key_copy, flow);
318
319 return flow;
320}
321
322/*
323 * Pairing a flow with its reverse.
324 *
325 * A UDX conversation is a six tuple, but a packet carries only the receiver's
326 * stream id: the ids are exchanged in an encrypted handshake that is not
327 * visible here. The reverse flow therefore has to be inferred.
328 *
329 * Candidates are the flows travelling the other way. Each is scored on
330 * - plausibility: the acknowledgement carried by this packet must fall
331 * within the sequence numbers the candidate has actually sent;
332 * - creation order: streams are set up in pairs, so the n'th flow in one
333 * direction usually answers the n'th flow in the other;
334 * - proximity: the two flows should have appeared at about the same time.
335 *
336 * A candidate is adopted once it has been the sole best choice twice, which
337 * keeps a single ambiguous packet from binding the wrong pair. Once bound,
338 * a pairing is never revised.
339 */
340typedef struct udx_pair_scan {
341 udx_flow_t *self;
342 uint32_t ack;
343 udx_flow_t *best;
344 int best_score;
345 bool_Bool tie;
346} udx_pair_scan_t;
347
348static void
349udx_score_candidate(void *key _U___attribute__((unused)), void *value, void *userdata)
350{
351 udx_flow_t *cand = (udx_flow_t *) value;
352 udx_pair_scan_t *scan = (udx_pair_scan_t *) userdata;
353 double dt;
354 int score = 0;
355
356 if (cand->dir == scan->self->dir || cand->paired != NULL((void*)0))
357 return;
358
359 /* The acknowledgement must not reach past what the candidate has sent. */
360 if (cand->have_seq) {
361 if (UDX_SEQ_GT(scan->ack, cand->max_seq + 1)((int32_t)((scan->ack) - (cand->max_seq + 1)) > 0))
362 return;
363 score += 4;
364 }
365
366 if (cand->order == scan->self->order)
367 score += 2;
368
369 dt = nstime_to_sec(&cand->first_ts) - nstime_to_sec(&scan->self->first_ts);
370 if (dt < 0)
371 dt = -dt;
372 if (dt < 0.050)
373 score += 1;
374
375 if (score > scan->best_score) {
376 scan->best_score = score;
377 scan->best = cand;
378 scan->tie = false0;
379 } else if (score == scan->best_score && scan->best != NULL((void*)0)) {
380 scan->tie = true1;
381 }
382}
383
384static void
385udx_try_pair(udx_conv_t *conv, udx_flow_t *flow, uint32_t ack)
386{
387 udx_pair_scan_t scan;
388
389 scan.self = flow;
390 scan.ack = ack;
391 scan.best = NULL((void*)0);
392 scan.best_score = 0;
393 scan.tie = false0;
394
395 wmem_map_foreach(conv->flows, udx_score_candidate, &scan);
396
397 if (scan.best == NULL((void*)0) || scan.tie) {
398 flow->cand = NULL((void*)0);
399 flow->cand_hits = 0;
400 return;
401 }
402
403 /*
404 * One unambiguous winner is adopted at once, so that a stream is paired
405 * from its first packet on. An ambiguous scan binds nothing and is
406 * retried on the next packet, by which time the acknowledgements have
407 * usually separated the candidates.
408 */
409 flow->cand = scan.best;
410 flow->cand_hits++;
411
412 flow->paired = scan.best;
413 scan.best->paired = flow;
414
415 /* Both halves report the lower of the two numbers. */
416 if (scan.best->stream_num < flow->stream_num)
417 flow->stream_num = scan.best->stream_num;
418 else
419 scan.best->stream_num = flow->stream_num;
420}
421
422/* RFC 6298 smoothing, fed only by segments that were never retransmitted. */
423static void
424udx_update_rtt(udx_flow_t *flow, double sample)
425{
426 if (!flow->have_rtt) {
427 flow->srtt = sample;
428 flow->rttvar = sample / 2;
429 flow->have_rtt = true1;
430 return;
431 }
432 flow->rttvar = 0.75 * flow->rttvar + 0.25 * fabs(flow->srtt - sample);
433 flow->srtt = 0.875 * flow->srtt + 0.125 * sample;
434}
435
436static double
437udx_rto(const udx_flow_t *flow)
438{
439 double rto;
440
441 if (!flow->have_rtt)
442 return UDX_MIN_RTO0.2;
443 rto = flow->srtt + 4 * flow->rttvar;
444 return (rto < UDX_MIN_RTO0.2) ? UDX_MIN_RTO0.2 : rto;
445}
446
447/*
448 * Retire every segment of the acknowledged flow below "ack", link the last
449 * of them to the acknowledging packet, and take an RTT sample from it.
450 */
451static void
452udx_process_ack(packet_info *pinfo, udx_flow_t *acked_flow, udx_flow_t *acking_flow,
453 uint32_t ack, udx_ppd_t *ppd)
454{
455 udx_seg_t *newest = NULL((void*)0);
456 uint32_t seq = ack - 1;
457 unsigned guard;
458
459 if (!acked_flow->have_seq)
460 return;
461
462 /* Walk back from the acknowledgement over the segments it covers. The
463 * walk stops at the first segment already retired by an earlier
464 * acknowledgement; the counter only bounds pathological captures. */
465 for (guard = 0; guard < 1024; guard++, seq--) {
466 udx_seg_t *seg = (udx_seg_t *) wmem_tree_lookup32(acked_flow->segs, seq);
467
468 if (seg == NULL((void*)0) || seg->acked_in_frame != 0)
469 break;
470
471 seg->acked_in_frame = pinfo->num;
472 seg->ack_ts = pinfo->abs_ts;
473
474 if (acked_flow->outstanding_pkts > 0) {
475 acked_flow->outstanding_pkts--;
476 acked_flow->outstanding_bytes -= seg->len;
477 }
478 if (newest == NULL((void*)0))
479 newest = seg;
480 }
481
482 if (newest != NULL((void*)0)) {
483 nstime_t rtt;
484
485 nstime_delta(&rtt, &pinfo->abs_ts, &newest->ts);
486 ppd->acks_frame = newest->frame;
487 ppd->ack_rtt = rtt;
488 ppd->have_ack_rtt = true1;
489
490 /* Karn's algorithm: a retransmitted segment yields no usable sample. */
491 if (newest->retrans == 0)
492 udx_update_rtt(acking_flow, nstime_to_sec(&rtt));
493 }
494}
495
496static void
497udx_analyze(packet_info *pinfo, udx_conv_t *conv, uint8_t flags, uint8_t data_offset,
498 uint32_t id, uint32_t window, uint32_t seq, uint32_t ack,
499 uint32_t payload_len, const uint32_t *sack_start, const uint32_t *sack_end,
500 unsigned n_sacks, udx_ppd_t *ppd)
501{
502 unsigned dir = udx_direction(pinfo);
503 udx_flow_t *flow = udx_get_flow(conv, dir, id, &pinfo->abs_ts);
504 udx_flow_t *rflow;
505 udx_seg_t *seg;
506 bool_Bool consumes_seq;
507
508 if (!conv->have_client_dir) {
509 conv->client_dir = dir;
510 conv->have_client_dir = true1;
511 }
512
513 ppd->flow = flow;
514 ppd->seq = seq;
515 ppd->from_server = (dir != conv->client_dir);
516
517 if (flow->paired == NULL((void*)0))
518 udx_try_pair(conv, flow, ack);
519 rflow = flow->paired;
520
521 /* DATA and END occupy a sequence number; MESSAGE is an unordered
522 * datagram outside the stream and a bare ACK only reports one. */
523 consumes_seq = (flags & (UDX_FLAG_DATA0x01 | UDX_FLAG_END0x02)) != 0;
524
525 if (consumes_seq) {
526 seg = (udx_seg_t *) wmem_tree_lookup32(flow->segs, seq);
527
528 if (seg == NULL((void*)0)) {
529 if (flow->have_seq && UDX_SEQ_GT(seq, flow->max_seq + 1)((int32_t)((seq) - (flow->max_seq + 1)) > 0))
530 ppd->flags |= UDX_A_LOST_SEGMENT0x0040;
531 else if (flow->have_seq && UDX_SEQ_LT(seq, flow->max_seq)((int32_t)((seq) - (flow->max_seq)) < 0))
532 ppd->flags |= UDX_A_OUT_OF_ORDER0x0020;
533
534 seg = wmem_new0(wmem_file_scope(), udx_seg_t)((udx_seg_t*)wmem_alloc0((wmem_file_scope()), sizeof(udx_seg_t
)))
;
535 seg->frame = pinfo->num;
536 seg->ts = pinfo->abs_ts;
537 seg->len = payload_len;
538 wmem_tree_insert32(flow->segs, seq, seg);
539
540 if (!flow->have_seq) {
541 flow->base_seq = seq;
542 flow->max_seq = seq;
543 flow->have_seq = true1;
544 } else if (UDX_SEQ_GT(seq, flow->max_seq)((int32_t)((seq) - (flow->max_seq)) > 0)) {
545 flow->max_seq = seq;
546 }
547 flow->outstanding_pkts++;
548 flow->outstanding_bytes += payload_len;
549 } else {
550 double dt = nstime_to_sec(&pinfo->abs_ts) - nstime_to_sec(&seg->ts);
551
552 seg->retrans++;
553 ppd->flags |= UDX_A_RETRANS0x0001;
554
555 if (seg->acked_in_frame != 0) {
556 ppd->flags |= UDX_A_SPURIOUS0x0010;
557 } else if (flow->have_sacked && UDX_SEQ_GT(flow->max_sacked, seq)((int32_t)((flow->max_sacked) - (seq)) > 0)) {
558 /* The peer has selectively acknowledged later packets, so
559 * this one was resent because it was reported missing rather
560 * than because a timer expired. */
561 ppd->flags |= UDX_A_FAST_RETRANS0x0002;
562 } else if (dt >= udx_rto(flow)) {
563 ppd->flags |= UDX_A_RTO_RETRANS0x0004;
564 } else if (dt < UDX_DUP_WINDOW0.0005) {
565 /* Too soon to be any sender timer: the datagram was
566 * delivered, or captured, twice. */
567 ppd->flags |= UDX_A_DUPLICATE0x4000;
568 } else if (seq == flow->max_seq &&
569 dt >= (flow->have_rtt ? 2 * flow->srtt : UDX_MIN_PROBE_DELAY0.010)) {
570 /* A repeat of the tail after a probe-sized pause, with
571 * nothing newer sent, is how a tail loss probe looks here. */
572 ppd->flags |= UDX_A_TLP0x0008;
573 }
574 }
575
576 ppd->tracked = true1;
577 ppd->bytes_in_flight = flow->outstanding_bytes;
578 ppd->packets_in_flight = flow->outstanding_pkts;
579
580 /*
581 * Position within the stream, counted from the first packet seen on
582 * this flow. Anything before that point arrived out of order at the
583 * very start of the capture and cannot be placed.
584 */
585 if (payload_len > 0 && UDX_SEQ_GEQ(seq, flow->base_seq)((int32_t)((seq) - (flow->base_seq)) >= 0)) {
586 ppd->follow_offset = seq - flow->base_seq;
587 ppd->follow_ok = true1;
588 }
589 }
590
591 if (flags & UDX_FLAG_END0x02)
592 ppd->flags |= UDX_A_END0x1000;
593 if (flags & UDX_FLAG_DESTROY0x10)
594 ppd->flags |= UDX_A_DESTROY0x2000;
595
596 /* An MTU probe pads between the header and the payload; the same byte
597 * delimits SACK blocks when they are present. */
598 if (data_offset > 0 && !(flags & UDX_FLAG_SACK0x04))
599 ppd->flags |= UDX_A_MTU_PROBE0x0800;
600
601 /* Acknowledgement side: retire the peer's segments, then record the
602 * selective ranges so a later repeat can be recognised as recovery. */
603 if (rflow != NULL((void*)0)) {
604 if (!flow->have_ack || UDX_SEQ_GT(ack, flow->max_ack)((int32_t)((ack) - (flow->max_ack)) > 0))
605 udx_process_ack(pinfo, rflow, flow, ack, ppd);
606
607 for (unsigned i = 0; i < n_sacks; i++) {
608 uint32_t s;
609 unsigned guard = 0;
610
611 for (s = sack_start[i]; UDX_SEQ_LT(s, sack_end[i])((int32_t)((s) - (sack_end[i])) < 0) && guard < 1024;
612 s++, guard++) {
613 udx_seg_t *ss = (udx_seg_t *) wmem_tree_lookup32(rflow->segs, s);
614
615 if (ss != NULL((void*)0))
616 ss->sacked = true1;
617 }
618
619 /* Remember how far the selective acknowledgements reach: a
620 * retransmission below this point is loss recovery. */
621 if (!rflow->have_sacked || UDX_SEQ_GT(sack_end[i] - 1, rflow->max_sacked)((int32_t)((sack_end[i] - 1) - (rflow->max_sacked)) > 0
)
) {
622 rflow->max_sacked = sack_end[i] - 1;
623 rflow->have_sacked = true1;
624 }
625 }
626 }
627
628 if (!flow->have_ack || UDX_SEQ_GT(ack, flow->max_ack)((int32_t)((ack) - (flow->max_ack)) > 0)) {
629 flow->max_ack = ack;
630 flow->have_ack = true1;
631 }
632
633 /* Receive window transitions. */
634 if (window == 0) {
635 ppd->flags |= UDX_A_ZERO_WIN0x0200;
636 flow->rwnd_zero = true1;
637 } else if (flow->rwnd_zero) {
638 ppd->flags |= UDX_A_WINDOW_UPDATE0x0400;
639 flow->rwnd_zero = false0;
640 }
641 flow->last_rwnd = window;
642
643 /*
644 * Keepalives and zero-window probes are the same bytes on the wire: a
645 * bare heartbeat. Only the peer's advertised window tells them apart.
646 */
647 if ((flags & UDX_FLAG_HEARTBEAT0x20) && payload_len == 0) {
648 if (rflow != NULL((void*)0) && rflow->rwnd_zero)
649 ppd->flags |= UDX_A_ZERO_WIN_PROBE0x0100;
650 else
651 ppd->flags |= UDX_A_KEEPALIVE0x0080;
652 }
653
654 ppd->stream = (flow->paired != NULL((void*)0) && flow->paired->stream_num < flow->stream_num)
655 ? flow->paired->stream_num
656 : flow->stream_num;
657}
658
659/*
660 * Render the verdicts reached on the first pass. Nothing here computes: on a
661 * revisit the stored results are simply replayed, so what is shown never
662 * depends on how the packet was reached.
663 */
664static void
665udx_show_analysis(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, udx_ppd_t *ppd)
666{
667 proto_item *ti;
668 proto_tree *an_tree;
669 udx_seg_t *seg;
670
671 if (ppd->flow == NULL((void*)0))
672 return;
673
674 ti = proto_tree_add_uint(tree, hf_udx_stream, tvb, 0, 0,
675 (ppd->flow->paired != NULL((void*)0) &&
676 ppd->flow->paired->stream_num < ppd->flow->stream_num)
677 ? ppd->flow->paired->stream_num
678 : ppd->flow->stream_num);
679 proto_item_set_generated(ti);
680
681 /* Nothing to report on a packet that neither carries data nor advances
682 * an acknowledgement, so leave the subtree out entirely rather than
683 * showing an empty one. */
684 if (ppd->flags == 0 && ppd->acks_frame == 0 && !ppd->tracked &&
685 ppd->flow->paired != NULL((void*)0))
686 return;
687
688 ti = proto_tree_add_item(tree, hf_udx_analysis, tvb, 0, 0, ENC_NA0x00000000);
689 proto_item_set_generated(ti);
690 an_tree = proto_item_add_subtree(ti, ett_udx_analysis);
691
692 if (ppd->flow->paired == NULL((void*)0)) {
693 proto_item *rev_ti = proto_tree_add_item(an_tree, hf_udx_analysis_no_reverse,
694 tvb, 0, 0, ENC_NA0x00000000);
695 proto_item_set_generated(rev_ti);
696 }
697
698 if (ppd->acks_frame != 0) {
699 ti = proto_tree_add_uint(an_tree, hf_udx_analysis_acks_frame, tvb, 0, 0,
700 ppd->acks_frame);
701 proto_item_set_generated(ti);
702
703 if (ppd->have_ack_rtt) {
704 ti = proto_tree_add_time(an_tree, hf_udx_analysis_ack_rtt, tvb, 0, 0,
705 &ppd->ack_rtt);
706 proto_item_set_generated(ti);
707 }
708 }
709
710 /* A packet that carried data learns only later which packet acked it. */
711 if (ppd->tracked) {
712 seg = (udx_seg_t *) wmem_tree_lookup32(ppd->flow->segs, ppd->seq);
713 if (seg != NULL((void*)0) && seg->frame == pinfo->num && seg->acked_in_frame != 0) {
714 nstime_t rtt;
715
716 ti = proto_tree_add_uint(an_tree, hf_udx_analysis_acked_in, tvb, 0, 0,
717 seg->acked_in_frame);
718 proto_item_set_generated(ti);
719
720 nstime_delta(&rtt, &seg->ack_ts, &seg->ts);
721 ti = proto_tree_add_time(an_tree, hf_udx_analysis_ack_rtt, tvb, 0, 0, &rtt);
722 proto_item_set_generated(ti);
723 }
724
725 ti = proto_tree_add_uint(an_tree, hf_udx_analysis_bytes_in_flight, tvb, 0, 0,
726 ppd->bytes_in_flight);
727 proto_item_set_generated(ti);
728 ti = proto_tree_add_uint(an_tree, hf_udx_analysis_pkts_in_flight, tvb, 0, 0,
729 ppd->packets_in_flight);
730 proto_item_set_generated(ti);
731 }
732
733 /* Expert notes, most specific classification first. */
734 if (ppd->flags & UDX_A_LOST_SEGMENT0x0040)
735 expert_add_info(pinfo, ti, &ei_udx_lost_segment);
736 if (ppd->flags & UDX_A_OUT_OF_ORDER0x0020)
737 expert_add_info(pinfo, ti, &ei_udx_out_of_order);
738
739 if (ppd->flags & UDX_A_SPURIOUS0x0010)
740 expert_add_info(pinfo, ti, &ei_udx_spurious_retrans);
741 else if (ppd->flags & UDX_A_FAST_RETRANS0x0002)
742 expert_add_info(pinfo, ti, &ei_udx_fast_retrans);
743 else if (ppd->flags & UDX_A_RTO_RETRANS0x0004)
744 expert_add_info(pinfo, ti, &ei_udx_rto_retrans);
745 else if (ppd->flags & UDX_A_TLP0x0008)
746 expert_add_info(pinfo, ti, &ei_udx_tlp);
747 else if (ppd->flags & UDX_A_DUPLICATE0x4000)
748 expert_add_info(pinfo, ti, &ei_udx_duplicate);
749 else if (ppd->flags & UDX_A_RETRANS0x0001)
750 expert_add_info(pinfo, ti, &ei_udx_retrans);
751
752 if (ppd->flags & UDX_A_ZERO_WIN_PROBE0x0100)
753 expert_add_info(pinfo, ti, &ei_udx_zero_window_probe);
754 else if (ppd->flags & UDX_A_KEEPALIVE0x0080)
755 expert_add_info(pinfo, ti, &ei_udx_keepalive);
756
757 if (ppd->flags & UDX_A_ZERO_WIN0x0200)
758 expert_add_info(pinfo, ti, &ei_udx_zero_window);
759 if (ppd->flags & UDX_A_WINDOW_UPDATE0x0400)
760 expert_add_info(pinfo, ti, &ei_udx_window_update);
761 if (ppd->flags & UDX_A_MTU_PROBE0x0800)
762 expert_add_info(pinfo, ti, &ei_udx_mtu_probe);
763 if (ppd->flags & UDX_A_END0x1000)
764 expert_add_info(pinfo, ti, &ei_udx_end);
765 if (ppd->flags & UDX_A_DESTROY0x2000)
766 expert_add_info(pinfo, ti, &ei_udx_destroy);
767}
768
769
770/*
771 * Follow stream.
772 *
773 * Payload is delivered in sequence order per direction. A packet that
774 * arrives early is held until the gap before it is filled, and a payload
775 * already delivered - a retransmission - is dropped, so the reassembled
776 * conversation reads the way the application saw it rather than the way the
777 * network happened to deliver it.
778 */
779
780/* Stream numbers restart with every capture file, as they do for TCP. */
781static void
782udx_init(void)
783{
784 udx_stream_count = 0;
785}
786
787static char *
788udx_follow_conv_filter(epan_dissect_t *edt _U___attribute__((unused)), packet_info *pinfo,
789 unsigned *stream, unsigned *sub_stream _U___attribute__((unused)))
790{
791 udx_ppd_t *ppd = (udx_ppd_t *) p_get_proto_data(wmem_file_scope(), pinfo, proto_udx, 0);
792
793 if (ppd == NULL((void*)0) || ppd->flow == NULL((void*)0))
794 return NULL((void*)0);
795
796 *stream = ppd->stream;
797 return ws_strdup_printf("udx.stream eq %u", ppd->stream)wmem_strdup_printf(((void*)0), "udx.stream eq %u", ppd->stream
)
;
798}
799
800static char *
801udx_follow_index_filter(unsigned stream, unsigned sub_stream _U___attribute__((unused)))
802{
803 return ws_strdup_printf("udx.stream eq %u", stream)wmem_strdup_printf(((void*)0), "udx.stream eq %u", stream);
804}
805
806static unsigned
807udx_get_stream_count(void)
808{
809 return udx_stream_count;
810}
811
812static void
813udx_follow_append(follow_info_t *follow_info, follow_record_t *record)
814{
815 follow_info->payload = g_list_prepend(follow_info->payload, record);
816 follow_info->bytes_written[record->is_server ? 1 : 0] += record->data->len;
817}
818
819static int
820udx_follow_seq_cmp(const void *a, const void *b)
821{
822 const follow_record_t *ra = (const follow_record_t *) a;
823 const follow_record_t *rb = (const follow_record_t *) b;
824
825 if (ra->seq == rb->seq)
826 return 0;
827 return (ra->seq < rb->seq) ? -1 : 1;
828}
829
830/*
831 * Release held payload that now continues the stream. The pending list is
832 * kept in sequence order, so this only ever walks its front.
833 */
834static void
835udx_follow_drain(follow_info_t *follow_info, int dir)
836{
837 while (follow_info->fragments[dir] != NULL((void*)0)) {
838 follow_record_t *held = (follow_record_t *) follow_info->fragments[dir]->data;
839
840 if (held->seq != follow_info->seq[dir])
841 break;
842
843 follow_info->seq[dir]++;
844 follow_info->fragments[dir] = g_list_delete_link(follow_info->fragments[dir],
845 follow_info->fragments[dir]);
846 udx_follow_append(follow_info, held);
847 }
848}
849
850static tap_packet_status
851udx_follow_tap_listener(void *tapdata, packet_info *pinfo, epan_dissect_t *edt _U___attribute__((unused)),
852 const void *data, tap_flags_t flags _U___attribute__((unused)))
853{
854 follow_info_t *follow_info = (follow_info_t *) tapdata;
855 const udx_follow_tap_data_t *follow_data = (const udx_follow_tap_data_t *) data;
856 follow_record_t *record;
857 unsigned length = tvb_captured_length(follow_data->tvb);
858 int dir = follow_data->from_server ? 1 : 0;
859
860 if (follow_info->stream_id != follow_data->stream)
861 return TAP_PACKET_DONT_REDRAW;
862
863 /* Already delivered: a retransmission or a duplicate. */
864 if (follow_data->offset < follow_info->seq[dir])
865 return TAP_PACKET_DONT_REDRAW;
866
867 record = g_new0(follow_record_t, 1)((follow_record_t *) g_malloc0_n ((1), sizeof (follow_record_t
)))
;
868 record->is_server = follow_data->from_server;
869 record->packet_num = pinfo->fd->num;
870 record->abs_ts = pinfo->fd->abs_ts;
871 record->seq = follow_data->offset;
872 record->data = g_byte_array_sized_new(length);
873 record->data = g_byte_array_append(record->data,
874 tvb_get_ptr(follow_data->tvb, 0, length), length);
875
876 if (follow_data->from_server) {
877 if (follow_info->server_port == 0) {
878 follow_info->server_port = pinfo->srcport;
879 copy_address(&follow_info->server_ip, &pinfo->src);
880 follow_info->client_port = pinfo->destport;
881 copy_address(&follow_info->client_ip, &pinfo->dst);
882 }
883 } else {
884 if (follow_info->client_port == 0) {
885 follow_info->client_port = pinfo->srcport;
886 copy_address(&follow_info->client_ip, &pinfo->src);
887 follow_info->server_port = pinfo->destport;
888 copy_address(&follow_info->server_ip, &pinfo->dst);
889 }
890 }
891
892 if (follow_data->offset == follow_info->seq[dir]) {
893 follow_info->seq[dir]++;
894 udx_follow_append(follow_info, record);
895 udx_follow_drain(follow_info, dir);
896 } else {
897 /* Arrived early: hold it, in order, until the gap ahead is filled.
898 * The framework frees whatever is still pending when the stream is
899 * reset, so an unfilled gap leaks nothing. */
900 follow_info->fragments[dir] = g_list_insert_sorted(follow_info->fragments[dir],
901 record, udx_follow_seq_cmp);
902 }
903
904 return TAP_PACKET_DONT_REDRAW;
905}
906
907static int
908dissect_udx(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void *data _U___attribute__((unused)))
909{
910 proto_item *ti;
911 proto_tree *udx_tree;
912 uint8_t flags, data_offset;
913 uint32_t id, window, seq, ack;
914 int offset = 0;
915 int sack_end_offset;
916 unsigned payload_len;
917 char flags_str[64];
918 uint32_t sack_start[UDX_MAX_SACK_BLOCKS32];
919 uint32_t sack_end[UDX_MAX_SACK_BLOCKS32];
920 unsigned n_sacks = 0;
921 udx_ppd_t *ppd = NULL((void*)0);
922
923 /*
924 * Reached either from the heuristic, which has already validated the
925 * header, or directly once a conversation has been claimed or through
926 * "Decode As". The latter routes make this check load bearing.
927 */
928 if (tvb_reported_length(tvb) < UDX_HEADER_SIZE20)
929 return 0;
930
931 col_set_str(pinfo->cinfo, COL_PROTOCOL, "UDX");
932 col_clear(pinfo->cinfo, COL_INFO);
933
934 flags = tvb_get_uint8(tvb, 2);
935 data_offset = tvb_get_uint8(tvb, 3);
936 id = tvb_get_letohl(tvb, 4);
937 window = tvb_get_letohl(tvb, 8);
938 seq = tvb_get_letohl(tvb, 12);
939 ack = tvb_get_letohl(tvb, 16);
940
941 udx_flags_to_str(flags, flags_str, sizeof(flags_str));
942
943 /* Collect the selective acknowledgement ranges before anything is added
944 * to the tree: the analysis below needs them, and the display needs the
945 * analysis. */
946 if (flags & UDX_FLAG_SACK0x04) {
947 /* Blocks fill the area delimited by data_offset; a packet with no
948 * payload may leave that byte zero and run to the end instead. */
949 sack_end_offset = (data_offset > 0)
950 ? UDX_HEADER_SIZE20 + data_offset
951 : (int) tvb_reported_length(tvb);
952 } else {
953 /* Anything reserved without SACK blocks is MTU probe padding. */
954 sack_end_offset = UDX_HEADER_SIZE20 + data_offset;
955 }
956
957 /* data_offset is not trustworthy on a packet this dissector did not
958 * validate, so never let it point past the datagram. */
959 sack_end_offset = MIN(sack_end_offset, (int) tvb_reported_length(tvb))(((sack_end_offset) < ((int) tvb_reported_length(tvb))) ? (
sack_end_offset) : ((int) tvb_reported_length(tvb)))
;
960
961 if (flags & UDX_FLAG_SACK0x04) {
962 int pos = UDX_HEADER_SIZE20;
963
964 while (pos + 8 <= sack_end_offset && n_sacks < UDX_MAX_SACK_BLOCKS32) {
965 sack_start[n_sacks] = tvb_get_letohl(tvb, pos);
966 sack_end[n_sacks] = tvb_get_letohl(tvb, pos + 4);
967 n_sacks++;
968 pos += 8;
969 }
970 }
971
972 payload_len = (unsigned) MAX(0, (int) tvb_reported_length(tvb) - sack_end_offset)(((0) > ((int) tvb_reported_length(tvb) - sack_end_offset)
) ? (0) : ((int) tvb_reported_length(tvb) - sack_end_offset))
;
973
974 if (udx_analyze_sequence_numbers) {
975 if (!PINFO_FD_VISITED(pinfo)((pinfo)->fd->visited)) {
976 conversation_t *conversation = find_or_create_conversation(pinfo);
977 udx_conv_t *conv;
978
979 conv = (udx_conv_t *) conversation_get_proto_data(conversation, proto_udx);
980 if (conv == NULL((void*)0)) {
981 conv = wmem_new0(wmem_file_scope(), udx_conv_t)((udx_conv_t*)wmem_alloc0((wmem_file_scope()), sizeof(udx_conv_t
)))
;
982 conv->flows = wmem_map_new(wmem_file_scope(), g_int64_hash, g_int64_equal);
983 conversation_add_proto_data(conversation, proto_udx, conv);
984 }
985
986 ppd = wmem_new0(wmem_file_scope(), udx_ppd_t)((udx_ppd_t*)wmem_alloc0((wmem_file_scope()), sizeof(udx_ppd_t
)))
;
987 udx_analyze(pinfo, conv, flags, data_offset, id, window, seq, ack,
988 payload_len, sack_start, sack_end, n_sacks, ppd);
989 p_add_proto_data(wmem_file_scope(), pinfo, proto_udx, 0, ppd);
990 } else {
991 ppd = (udx_ppd_t *) p_get_proto_data(wmem_file_scope(), pinfo, proto_udx, 0);
992 }
993 }
994
995 ti = proto_tree_add_item(tree, proto_udx, tvb, 0, -1, ENC_NA0x00000000);
996 proto_item_append_text(ti, ", %s, Id: %u, Seq: %u, Ack: %u", flags_str, id, seq, ack);
997 udx_tree = proto_item_add_subtree(ti, ett_udx);
998
999 proto_tree_add_item(udx_tree, hf_udx_magic, tvb, offset, 1, ENC_NA0x00000000);
1000 offset += 1;
1001 proto_tree_add_item(udx_tree, hf_udx_version, tvb, offset, 1, ENC_NA0x00000000);
1002 offset += 1;
1003 proto_tree_add_bitmask(udx_tree, tvb, offset, hf_udx_flags, ett_udx_flags,
1004 udx_flag_fields, ENC_NA0x00000000);
1005 offset += 1;
1006 proto_tree_add_item(udx_tree, hf_udx_data_offset, tvb, offset, 1, ENC_NA0x00000000);
1007 offset += 1;
1008 proto_tree_add_item(udx_tree, hf_udx_id, tvb, offset, 4, ENC_LITTLE_ENDIAN0x80000000);
1009 offset += 4;
1010 proto_tree_add_item(udx_tree, hf_udx_window, tvb, offset, 4, ENC_LITTLE_ENDIAN0x80000000);
1011 offset += 4;
1012 proto_tree_add_item(udx_tree, hf_udx_seq, tvb, offset, 4, ENC_LITTLE_ENDIAN0x80000000);
1013 offset += 4;
1014 proto_tree_add_item(udx_tree, hf_udx_ack, tvb, offset, 4, ENC_LITTLE_ENDIAN0x80000000);
1015 offset += 4;
1016
1017 if (n_sacks > 0) {
1018 proto_item *sacks_ti;
1019 proto_tree *sacks_tree, *block_tree;
1020
1021 sacks_ti = proto_tree_add_item(udx_tree, hf_udx_sacks, tvb, offset,
1022 sack_end_offset - offset, ENC_NA0x00000000);
1023 proto_item_append_text(sacks_ti, " (%u)", n_sacks);
1024 sacks_tree = proto_item_add_subtree(sacks_ti, ett_udx_sacks);
1025
1026 for (unsigned i = 0; i < n_sacks; i++) {
1027 block_tree = proto_tree_add_subtree_format(sacks_tree, tvb, offset, 8,
1028 ett_udx_sack_block, NULL((void*)0),
1029 "SACK: %u-%u",
1030 sack_start[i], sack_end[i]);
1031 proto_tree_add_item(block_tree, hf_udx_sack_start, tvb, offset, 4,
1032 ENC_LITTLE_ENDIAN0x80000000);
1033 proto_tree_add_item(block_tree, hf_udx_sack_end, tvb, offset + 4, 4,
1034 ENC_LITTLE_ENDIAN0x80000000);
1035 offset += 8;
1036 }
1037 } else if (!(flags & UDX_FLAG_SACK0x04) && data_offset > 0) {
1038 /*
1039 * Padding between header and payload with no SACK blocks: inserted by
1040 * mtu_probeify_packet() in libudx - this datagram is an MTU probe.
1041 */
1042 proto_tree_add_item(udx_tree, hf_udx_padding, tvb, offset, data_offset, ENC_NA0x00000000);
1043 offset += data_offset;
Value stored to 'offset' is never read
1044 }
1045
1046 if (payload_len > 0) {
1047 ti = proto_tree_add_uint(udx_tree, hf_udx_payload_len, tvb, 0, 0, payload_len);
1048 proto_item_set_generated(ti);
1049 proto_tree_add_item(udx_tree, hf_udx_payload, tvb, sack_end_offset,
1050 (int) payload_len, ENC_NA0x00000000);
1051 }
1052
1053 if (ppd != NULL((void*)0)) {
1054 udx_show_analysis(tvb, pinfo, udx_tree, ppd);
1055
1056 /* MESSAGE payloads travel outside the ordered stream, so they are
1057 * shown per packet but left out of the reassembled conversation. */
1058 if (ppd->follow_ok && !(flags & UDX_FLAG_MESSAGE0x08) &&
1059 have_tap_listener(udx_follow_tap)) {
1060 udx_follow_tap_data_t *follow_data = wmem_new0(pinfo->pool, udx_follow_tap_data_t)((udx_follow_tap_data_t*)wmem_alloc0((pinfo->pool), sizeof
(udx_follow_tap_data_t)))
;
1061
1062 follow_data->tvb = tvb_new_subset_length(tvb, sack_end_offset, (int) payload_len);
1063 follow_data->stream = ppd->stream;
1064 follow_data->offset = ppd->follow_offset;
1065 follow_data->from_server = ppd->from_server;
1066 tap_queue_packet(udx_follow_tap, pinfo, follow_data);
1067 }
1068 }
1069
1070 col_add_fstr(pinfo->cinfo, COL_INFO, "%s Id=%u Seq=%u Ack=%u Rwnd=%u",
1071 flags_str, id, seq, ack, window);
1072 if (payload_len > 0)
1073 col_append_fstr(pinfo->cinfo, COL_INFO, " Len=%u", payload_len);
1074 if (ppd != NULL((void*)0) && (ppd->flags & UDX_A_RETRANS0x0001))
1075 col_append_str(pinfo->cinfo, COL_INFO, " [retransmission]");
1076
1077 return tvb_reported_length(tvb);
1078}
1079
1080static bool_Bool
1081test_udx(tvbuff_t *tvb)
1082{
1083 uint8_t flags, data_offset;
1084
1085 if (tvb_captured_length(tvb) < UDX_HEADER_SIZE20)
1086 return false0;
1087 if (tvb_get_uint8(tvb, 0) != UDX_MAGIC_BYTE0xff)
1088 return false0;
1089 if (tvb_get_uint8(tvb, 1) != UDX_VERSION1)
1090 return false0;
1091
1092 flags = tvb_get_uint8(tvb, 2);
1093 if (flags & ~UDX_FLAG_MASK0x3f)
1094 return false0;
1095
1096 data_offset = tvb_get_uint8(tvb, 3);
1097 if (UDX_HEADER_SIZE20 + (unsigned) data_offset > tvb_reported_length(tvb))
1098 return false0;
1099 /* The area delimited by data_offset holds SACK blocks (uint32 pairs) when
1100 * the SACK flag is set - anything not a multiple of 8 is not UDX. */
1101 if ((flags & UDX_FLAG_SACK0x04) && data_offset > 0 && (data_offset % 8) != 0)
1102 return false0;
1103
1104 /*
1105 * Only DATA and MESSAGE packets carry a payload. Everything else is the
1106 * fixed header followed at most by selective acknowledgement blocks, so
1107 * its length is known exactly and anything else is not UDX.
1108 */
1109 if (!(flags & (UDX_FLAG_DATA0x01 | UDX_FLAG_MESSAGE0x08))) {
1110 unsigned trailing = tvb_reported_length(tvb) - UDX_HEADER_SIZE20;
1111
1112 if (flags & UDX_FLAG_SACK0x04) {
1113 if ((trailing % 8) != 0)
1114 return false0;
1115 } else if (trailing != 0) {
1116 return false0;
1117 }
1118 }
1119
1120 return true1;
1121}
1122
1123static bool_Bool
1124dissect_udx_heur(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void *data)
1125{
1126 conversation_t *conversation;
1127
1128 if (!test_udx(tvb))
1129 return false0;
1130
1131 /* Claim the whole UDP conversation so weaker frames (e.g. bare 20-byte
1132 * heartbeats) and future packets skip the heuristic. */
1133 conversation = find_or_create_conversation(pinfo);
1134 conversation_set_dissector(conversation, udx_handle);
1135
1136 dissect_udx(tvb, pinfo, tree, data);
1137 return true1;
1138}
1139
1140void
1141proto_register_udx(void)
1142{
1143 static hf_register_info hf[] = {
1144 { &hf_udx_magic,
1145 { "Magic Byte", "udx.magic_byte", FT_UINT8, BASE_HEX, NULL((void*)0), 0x0,
1146 "Always 0xff", HFILL-1, 0, HF_REF_TYPE_NONE, -1, ((void*)0) }
1147 },
1148 { &hf_udx_version,
1149 { "Version", "udx.version", FT_UINT8, BASE_DEC, NULL((void*)0), 0x0,
1150 NULL((void*)0), HFILL-1, 0, HF_REF_TYPE_NONE, -1, ((void*)0) }
1151 },
1152 { &hf_udx_flags,
1153 { "Type", "udx.type", FT_UINT8, BASE_HEX, NULL((void*)0), 0x0,
1154 "Packet type flags", HFILL-1, 0, HF_REF_TYPE_NONE, -1, ((void*)0) }
1155 },
1156 { &hf_udx_flags_data,
1157 { "Data", "udx.type.data", FT_BOOLEAN, 8, NULL((void*)0), UDX_FLAG_DATA0x01,
1158 "Carries stream payload", HFILL-1, 0, HF_REF_TYPE_NONE, -1, ((void*)0) }
1159 },
1160 { &hf_udx_flags_end,
1161 { "End", "udx.type.end", FT_BOOLEAN, 8, NULL((void*)0), UDX_FLAG_END0x02,
1162 "Graceful end of stream (consumes a sequence number)", HFILL-1, 0, HF_REF_TYPE_NONE, -1, ((void*)0) }
1163 },
1164 { &hf_udx_flags_sack,
1165 { "SACK", "udx.type.sack", FT_BOOLEAN, 8, NULL((void*)0), UDX_FLAG_SACK0x04,
1166 "Carries selective acknowledgement blocks", HFILL-1, 0, HF_REF_TYPE_NONE, -1, ((void*)0) }
1167 },
1168 { &hf_udx_flags_message,
1169 { "Message", "udx.type.message", FT_BOOLEAN, 8, NULL((void*)0), UDX_FLAG_MESSAGE0x08,
1170 "Unordered datagram outside the byte stream", HFILL-1, 0, HF_REF_TYPE_NONE, -1, ((void*)0) }
1171 },
1172 { &hf_udx_flags_destroy,
1173 { "Destroy", "udx.type.destroy", FT_BOOLEAN, 8, NULL((void*)0), UDX_FLAG_DESTROY0x10,
1174 "Abrupt stream termination", HFILL-1, 0, HF_REF_TYPE_NONE, -1, ((void*)0) }
1175 },
1176 { &hf_udx_flags_heartbeat,
1177 { "Heartbeat", "udx.type.heartbeat", FT_BOOLEAN, 8, NULL((void*)0), UDX_FLAG_HEARTBEAT0x20,
1178 "Keepalive or zero-window probe", HFILL-1, 0, HF_REF_TYPE_NONE, -1, ((void*)0) }
1179 },
1180 { &hf_udx_data_offset,
1181 { "Data Offset", "udx.data_offset", FT_UINT8, BASE_DEC, NULL((void*)0), 0x0,
1182 "Bytes between the fixed header and the payload", HFILL-1, 0, HF_REF_TYPE_NONE, -1, ((void*)0) }
1183 },
1184 { &hf_udx_id,
1185 { "Id", "udx.id", FT_UINT32, BASE_DEC, NULL((void*)0), 0x0,
1186 "Receiver's stream id", HFILL-1, 0, HF_REF_TYPE_NONE, -1, ((void*)0) }
1187 },
1188 { &hf_udx_window,
1189 { "Window", "udx.rwnd", FT_UINT32, BASE_DEC, NULL((void*)0), 0x0,
1190 "Sender's receive window in bytes", HFILL-1, 0, HF_REF_TYPE_NONE, -1, ((void*)0) }
1191 },
1192 { &hf_udx_seq,
1193 { "Seq", "udx.seq", FT_UINT32, BASE_DEC, NULL((void*)0), 0x0,
1194 "Packet sequence number (counts packets, not bytes)", HFILL-1, 0, HF_REF_TYPE_NONE, -1, ((void*)0) }
1195 },
1196 { &hf_udx_ack,
1197 { "Ack", "udx.ack", FT_UINT32, BASE_DEC, NULL((void*)0), 0x0,
1198 "Next sequence number expected from the peer", HFILL-1, 0, HF_REF_TYPE_NONE, -1, ((void*)0) }
1199 },
1200 { &hf_udx_sacks,
1201 { "SACK Blocks", "udx.sacks", FT_NONE, BASE_NONE, NULL((void*)0), 0x0,
1202 "Selective acknowledgement ranges", HFILL-1, 0, HF_REF_TYPE_NONE, -1, ((void*)0) }
1203 },
1204 { &hf_udx_sack_block,
1205 { "SACK Block", "udx.sack", FT_NONE, BASE_NONE, NULL((void*)0), 0x0,
1206 NULL((void*)0), HFILL-1, 0, HF_REF_TYPE_NONE, -1, ((void*)0) }
1207 },
1208 { &hf_udx_sack_start,
1209 { "Start", "udx.sack.start", FT_UINT32, BASE_DEC, NULL((void*)0), 0x0,
1210 "First sequence number in the acknowledged range", HFILL-1, 0, HF_REF_TYPE_NONE, -1, ((void*)0) }
1211 },
1212 { &hf_udx_sack_end,
1213 { "End", "udx.sack.end", FT_UINT32, BASE_DEC, NULL((void*)0), 0x0,
1214 "One past the last acknowledged sequence number", HFILL-1, 0, HF_REF_TYPE_NONE, -1, ((void*)0) }
1215 },
1216 { &hf_udx_padding,
1217 { "Padding", "udx.padding", FT_BYTES, BASE_NONE, NULL((void*)0), 0x0,
1218 "MTU probe padding", HFILL-1, 0, HF_REF_TYPE_NONE, -1, ((void*)0) }
1219 },
1220 { &hf_udx_payload,
1221 { "Payload", "udx.payload", FT_BYTES, BASE_NONE, NULL((void*)0), 0x0,
1222 NULL((void*)0), HFILL-1, 0, HF_REF_TYPE_NONE, -1, ((void*)0) }
1223 },
1224 { &hf_udx_payload_len,
1225 { "Payload Length", "udx.length", FT_UINT32, BASE_DEC, NULL((void*)0), 0x0,
1226 NULL((void*)0), HFILL-1, 0, HF_REF_TYPE_NONE, -1, ((void*)0) }
1227 },
1228 { &hf_udx_stream,
1229 { "Stream index", "udx.stream", FT_UINT32, BASE_DEC, NULL((void*)0), 0x0,
1230 "Index of the paired flows carrying this stream", HFILL-1, 0, HF_REF_TYPE_NONE, -1, ((void*)0) }
1231 },
1232 { &hf_udx_analysis,
1233 { "SEQ/ACK analysis", "udx.analysis", FT_NONE, BASE_NONE, NULL((void*)0), 0x0,
1234 "Results of the sequence number analysis", HFILL-1, 0, HF_REF_TYPE_NONE, -1, ((void*)0) }
1235 },
1236 { &hf_udx_analysis_acks_frame,
1237 { "This is an ACK to the packet in frame", "udx.analysis.acks_frame",
1238 FT_FRAMENUM, BASE_NONE, FRAMENUM_TYPE(FT_FRAMENUM_ACK)((gpointer) (glong) (FT_FRAMENUM_ACK)), 0x0,
1239 NULL((void*)0), HFILL-1, 0, HF_REF_TYPE_NONE, -1, ((void*)0) }
1240 },
1241 { &hf_udx_analysis_acked_in,
1242 { "ACKed in frame", "udx.analysis.acked_in", FT_FRAMENUM, BASE_NONE,
1243 FRAMENUM_TYPE(FT_FRAMENUM_NONE)((gpointer) (glong) (FT_FRAMENUM_NONE)), 0x0,
1244 "The frame that acknowledged this packet", HFILL-1, 0, HF_REF_TYPE_NONE, -1, ((void*)0) }
1245 },
1246 { &hf_udx_analysis_ack_rtt,
1247 { "Time to ACK", "udx.analysis.ack_rtt", FT_RELATIVE_TIME, BASE_NONE, NULL((void*)0), 0x0,
1248 "Time between the packet and its acknowledgement", HFILL-1, 0, HF_REF_TYPE_NONE, -1, ((void*)0) }
1249 },
1250 { &hf_udx_analysis_bytes_in_flight,
1251 { "Bytes in flight", "udx.analysis.bytes_in_flight", FT_UINT32, BASE_DEC,
1252 NULL((void*)0), 0x0, "Unacknowledged payload bytes on this flow", HFILL-1, 0, HF_REF_TYPE_NONE, -1, ((void*)0) }
1253 },
1254 { &hf_udx_analysis_pkts_in_flight,
1255 { "Packets in flight", "udx.analysis.packets_in_flight", FT_UINT32, BASE_DEC,
1256 NULL((void*)0), 0x0, "Unacknowledged packets on this flow", HFILL-1, 0, HF_REF_TYPE_NONE, -1, ((void*)0) }
1257 },
1258 { &hf_udx_analysis_no_reverse,
1259 { "Reverse flow not identified", "udx.analysis.no_reverse", FT_NONE, BASE_NONE,
1260 NULL((void*)0), 0x0, "The stream carrying the other direction has not been paired",
1261 HFILL-1, 0, HF_REF_TYPE_NONE, -1, ((void*)0) }
1262 },
1263 };
1264
1265 static int *ett[] = {
1266 &ett_udx,
1267 &ett_udx_flags,
1268 &ett_udx_sacks,
1269 &ett_udx_sack_block,
1270 &ett_udx_analysis,
1271 };
1272
1273 static ei_register_info ei[] = {
1274 { &ei_udx_retrans,
1275 { "udx.analysis.retransmission", PI_SEQUENCE0x02000000, PI_NOTE0x00400000,
1276 "This packet was retransmitted", EXPFILL0, ((void*)0), 0, ((void*)0), {0, {((void*)0), ((void*)0), FT_NONE
, BASE_NONE, ((void*)0), 0, ((void*)0), -1, 0, HF_REF_TYPE_NONE
, -1, ((void*)0)}}
}
1277 },
1278 { &ei_udx_fast_retrans,
1279 { "udx.analysis.fast_retransmission", PI_SEQUENCE0x02000000, PI_NOTE0x00400000,
1280 "Fast retransmission: resent while later packets were selectively"
1281 " acknowledged", EXPFILL0, ((void*)0), 0, ((void*)0), {0, {((void*)0), ((void*)0), FT_NONE
, BASE_NONE, ((void*)0), 0, ((void*)0), -1, 0, HF_REF_TYPE_NONE
, -1, ((void*)0)}}
}
1282 },
1283 { &ei_udx_rto_retrans,
1284 { "udx.analysis.rto_retransmission", PI_SEQUENCE0x02000000, PI_NOTE0x00400000,
1285 "Retransmission timeout: resent after more than the estimated RTO",
1286 EXPFILL0, ((void*)0), 0, ((void*)0), {0, {((void*)0), ((void*)0), FT_NONE
, BASE_NONE, ((void*)0), 0, ((void*)0), -1, 0, HF_REF_TYPE_NONE
, -1, ((void*)0)}}
}
1287 },
1288 { &ei_udx_tlp,
1289 { "udx.analysis.tail_loss_probe", PI_SEQUENCE0x02000000, PI_NOTE0x00400000,
1290 "Tail loss probe: the last packet of a burst was resent to elicit an"
1291 " acknowledgement", EXPFILL0, ((void*)0), 0, ((void*)0), {0, {((void*)0), ((void*)0), FT_NONE
, BASE_NONE, ((void*)0), 0, ((void*)0), -1, 0, HF_REF_TYPE_NONE
, -1, ((void*)0)}}
}
1292 },
1293 { &ei_udx_spurious_retrans,
1294 { "udx.analysis.spurious_retransmission", PI_SEQUENCE0x02000000, PI_NOTE0x00400000,
1295 "Spurious retransmission: this packet was already acknowledged", EXPFILL0, ((void*)0), 0, ((void*)0), {0, {((void*)0), ((void*)0), FT_NONE
, BASE_NONE, ((void*)0), 0, ((void*)0), -1, 0, HF_REF_TYPE_NONE
, -1, ((void*)0)}}
}
1296 },
1297 { &ei_udx_duplicate,
1298 { "udx.analysis.duplicate", PI_SEQUENCE0x02000000, PI_NOTE0x00400000,
1299 "Duplicate packet: the same packet was seen twice in quick succession",
1300 EXPFILL0, ((void*)0), 0, ((void*)0), {0, {((void*)0), ((void*)0), FT_NONE
, BASE_NONE, ((void*)0), 0, ((void*)0), -1, 0, HF_REF_TYPE_NONE
, -1, ((void*)0)}}
}
1301 },
1302 { &ei_udx_out_of_order,
1303 { "udx.analysis.out_of_order", PI_SEQUENCE0x02000000, PI_NOTE0x00400000,
1304 "Out-of-order packet", EXPFILL0, ((void*)0), 0, ((void*)0), {0, {((void*)0), ((void*)0), FT_NONE
, BASE_NONE, ((void*)0), 0, ((void*)0), -1, 0, HF_REF_TYPE_NONE
, -1, ((void*)0)}}
}
1305 },
1306 { &ei_udx_lost_segment,
1307 { "udx.analysis.lost_segment", PI_SEQUENCE0x02000000, PI_WARN0x00600000,
1308 "Previous packet not captured: a sequence number was skipped", EXPFILL0, ((void*)0), 0, ((void*)0), {0, {((void*)0), ((void*)0), FT_NONE
, BASE_NONE, ((void*)0), 0, ((void*)0), -1, 0, HF_REF_TYPE_NONE
, -1, ((void*)0)}}
}
1309 },
1310 { &ei_udx_keepalive,
1311 { "udx.analysis.keepalive", PI_SEQUENCE0x02000000, PI_NOTE0x00400000,
1312 "Keepalive", EXPFILL0, ((void*)0), 0, ((void*)0), {0, {((void*)0), ((void*)0), FT_NONE
, BASE_NONE, ((void*)0), 0, ((void*)0), -1, 0, HF_REF_TYPE_NONE
, -1, ((void*)0)}}
}
1313 },
1314 { &ei_udx_zero_window_probe,
1315 { "udx.analysis.zero_window_probe", PI_SEQUENCE0x02000000, PI_NOTE0x00400000,
1316 "Zero window probe: sent while the peer advertised no receive window",
1317 EXPFILL0, ((void*)0), 0, ((void*)0), {0, {((void*)0), ((void*)0), FT_NONE
, BASE_NONE, ((void*)0), 0, ((void*)0), -1, 0, HF_REF_TYPE_NONE
, -1, ((void*)0)}}
}
1318 },
1319 { &ei_udx_zero_window,
1320 { "udx.analysis.zero_window", PI_SEQUENCE0x02000000, PI_WARN0x00600000,
1321 "Zero window: the sender cannot accept more data", EXPFILL0, ((void*)0), 0, ((void*)0), {0, {((void*)0), ((void*)0), FT_NONE
, BASE_NONE, ((void*)0), 0, ((void*)0), -1, 0, HF_REF_TYPE_NONE
, -1, ((void*)0)}}
}
1322 },
1323 { &ei_udx_window_update,
1324 { "udx.analysis.window_update", PI_SEQUENCE0x02000000, PI_CHAT0x00200000,
1325 "Window update: the receive window reopened", EXPFILL0, ((void*)0), 0, ((void*)0), {0, {((void*)0), ((void*)0), FT_NONE
, BASE_NONE, ((void*)0), 0, ((void*)0), -1, 0, HF_REF_TYPE_NONE
, -1, ((void*)0)}}
}
1326 },
1327 { &ei_udx_mtu_probe,
1328 { "udx.analysis.mtu_probe", PI_SEQUENCE0x02000000, PI_CHAT0x00200000,
1329 "MTU probe: padded to test a larger path MTU", EXPFILL0, ((void*)0), 0, ((void*)0), {0, {((void*)0), ((void*)0), FT_NONE
, BASE_NONE, ((void*)0), 0, ((void*)0), -1, 0, HF_REF_TYPE_NONE
, -1, ((void*)0)}}
}
1330 },
1331 { &ei_udx_end,
1332 { "udx.analysis.end", PI_SEQUENCE0x02000000, PI_CHAT0x00200000,
1333 "End of stream", EXPFILL0, ((void*)0), 0, ((void*)0), {0, {((void*)0), ((void*)0), FT_NONE
, BASE_NONE, ((void*)0), 0, ((void*)0), -1, 0, HF_REF_TYPE_NONE
, -1, ((void*)0)}}
}
1334 },
1335 { &ei_udx_destroy,
1336 { "udx.analysis.destroy", PI_SEQUENCE0x02000000, PI_WARN0x00600000,
1337 "Stream destroyed: abrupt termination", EXPFILL0, ((void*)0), 0, ((void*)0), {0, {((void*)0), ((void*)0), FT_NONE
, BASE_NONE, ((void*)0), 0, ((void*)0), -1, 0, HF_REF_TYPE_NONE
, -1, ((void*)0)}}
}
1338 },
1339 };
1340
1341 expert_module_t *expert_udx;
1342 module_t *udx_module;
1343
1344 proto_udx = proto_register_protocol("UDX Protocol", "UDX", "udx");
1345 proto_register_field_array(proto_udx, hf, array_length(hf)(sizeof (hf) / sizeof (hf)[0]));
1346 proto_register_subtree_array(ett, array_length(ett)(sizeof (ett) / sizeof (ett)[0]));
1347
1348 expert_udx = expert_register_protocol(proto_udx);
1349 expert_register_field_array(expert_udx, ei, array_length(ei)(sizeof (ei) / sizeof (ei)[0]));
1350
1351 udx_handle = register_dissector("udx", dissect_udx, proto_udx);
1352
1353 register_init_routine(udx_init);
1354
1355 udx_follow_tap = register_tap("udx_follow");
1356 register_follow_stream(proto_udx, "udx_follow",
1357 udx_follow_conv_filter, udx_follow_index_filter,
1358 udp_follow_address_filter, udp_port_to_display,
1359 udx_follow_tap_listener, udx_get_stream_count, NULL((void*)0));
1360
1361 udx_module = prefs_register_protocol(proto_udx, NULL((void*)0));
1362 prefs_register_bool_preference(udx_module, "analyze_sequence_numbers",
1363 "Analyze UDX sequence numbers",
1364 "Track sequence and acknowledgement numbers to pair flows, measure "
1365 "round-trip times and flag retransmissions",
1366 &udx_analyze_sequence_numbers);
1367}
1368
1369void
1370proto_reg_handoff_udx(void)
1371{
1372 heur_dissector_add("udp", dissect_udx_heur, "UDX over UDP", "udx_udp",
1373 proto_udx, HEURISTIC_DISABLE);
1374 dissector_add_for_decode_as_with_preference("udp.port", udx_handle);
1375}
1376
1377/*
1378 * Editor modelines - https://www.wireshark.org/tools/modelines.html
1379 *
1380 * Local variables:
1381 * c-basic-offset: 4
1382 * tab-width: 8
1383 * indent-tabs-mode: nil
1384 * End:
1385 *
1386 * vi: set shiftwidth=4 tabstop=8 expandtab:
1387 * :indentSize=4:tabSize=8:noTabs=true:
1388 */