### Bug Summary

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

### Annotated Source Code

Press '?' to see keyboard shortcuts

Show analyzer invocation
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-28-100404-3660-1 -x c /builds/wireshark/wireshark/epan/dissectors/packet-udx.c

Keyboard shortcuts:

- Use 'j/k' keys for keyboard navigation
- Use 'Shift+S' to show/hide relevant lines
- Use '?' to toggle this window

Close
Show only relevant lines Show control flow arrows

1 | /* packet-udx.c
2 | * Routines for UDX dissection
3 | * Copyright 2026, Frank <frankolien123@gmail.com>
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 <gerald@wireshark.org>
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/conversation_table.h>
23 | #include <epan/expert.h>
24 | #include <epan/prefs.h>
25 | #include <epan/proto_data.h>
26 | #include <epan/follow.h>
27 | #include <epan/addr_resolv.h>
28 | #include <epan/tap.h>
29 | #include "packet-udp.h"
30 | #include "packet-udx.h"
31 | #include <wsutil/wmem/wmem_map.h>
32 | #include <wsutil/wmem/wmem_tree.h>
33 |
34 | /*
35 | * UDX wire format (all multi-byte fields little-endian):
36 | *
37 | * offset size field
38 | * 0 1 magic (0xff)
39 | * 1 1 version (1)
40 | * 2 1 type flags
41 | * 3 1 data offset: bytes between the fixed header and the payload,
42 | * occupied by SACK blocks, or by padding on MTU probes
43 | * 4 4 id - the *receiver's* stream id
44 | * 8 4 window - sender's receive window in bytes
45 | * 12 4 seq - per-PACKET sequence counter (not bytes)
46 | * 16 4 ack - next seq expected from the peer
47 | * 20 8*n SACK blocks: pairs of uint32 (start, end) seq ranges
48 | * ... payload
49 | */
50 |
51 | #define UDX_HEADER_SIZE20 20
52 | #define UDX_MAGIC_BYTE0xff 0xff
53 | #define UDX_VERSION1 1
54 |
55 | #define UDX_FLAG_DATA0x01 0x01
56 | #define UDX_FLAG_END0x02 0x02
57 | #define UDX_FLAG_SACK0x04 0x04
58 | #define UDX_FLAG_MESSAGE0x08 0x08
59 | #define UDX_FLAG_DESTROY0x10 0x10
60 | #define UDX_FLAG_HEARTBEAT0x20 0x20
61 | #define UDX_FLAG_MASK0x3f 0x3f
62 |
63 | /*
64 | * Sequence numbers count packets and wrap at 2^32, so all comparisons are
65 | * made in circular arithmetic.
66 | */
67 | #define UDX_SEQ_LT(a, b)((int32_t)((a) - (b)) < 0) ((int32_t)((a) - (b)) < 0)
68 | #define UDX_SEQ_GT(a, b)((int32_t)((a) - (b)) > 0) ((int32_t)((a) - (b)) > 0)
69 | #define UDX_SEQ_GEQ(a, b)((int32_t)((a) - (b)) >= 0) ((int32_t)((a) - (b)) >= 0)
70 |
71 | /* Floor for the derived retransmission timeout, in seconds, matching the
72 | * floor libudx applies to its own. Below this a repeat is attributed to loss
73 | * recovery rather than to a timer firing. */
74 | #define UDX_MIN_RTO1.0 1.0
75 |
76 | /* A repeat arriving within this window of the original is a duplicate
77 | * datagram rather than anything the sender chose to send again. */
78 | #define UDX_DUP_WINDOW0.0005 0.0005
79 |
80 | void proto_register_udx(void);
81 | void proto_reg_handoff_udx(void);
82 |
83 | static dissector_handle_t udx_handle;
84 |
85 | static int proto_udx;
86 |
87 | static bool_Bool udx_analyze_sequence_numbers = true1;
88 |
89 | static int udx_follow_tap;
90 | static int udx_tap;
91 |
92 | /* Stream numbers are handed out across the whole capture so that a filter
93 | * such as "udx.stream eq 3" identifies exactly one stream. */
94 | static uint32_t udx_stream_count;
95 |
96 | /* Queued for the follow taps: one packet's payload, where it belongs in the
97 | * stream and which side sent it. */
98 | typedef struct udx_follow_tap_data {
99 | tvbuff_t *tvb;
100 | uint32_t stream;
101 | uint32_t offset; /* position of this packet within its direction */
102 | bool_Bool from_server;
103 | } udx_follow_tap_data_t;
104 |
105 | /* One transmitted packet, remembered so that a later acknowledgement can be
106 | * linked back to it. */
107 | typedef struct udx_seg {
108 | uint32_t frame;
109 | nstime_t ts;
110 | uint32_t len;
111 | uint32_t acked_in_frame;
112 | nstime_t ack_ts;
113 | unsigned retrans;
114 | bool_Bool sacked;
115 | } udx_seg_t;
116 |
117 | /* One direction of one stream: the packets one endpoint sends bearing the
118 | * peer's stream id. */
119 | typedef struct udx_flow {
120 | uint32_t id;
121 | unsigned dir;
122 | unsigned order; /* creation order within this direction */
123 | nstime_t first_ts;
124 | wmem_tree_t *segs; /* seq -> udx_seg_t */
125 | uint32_t base_seq; /* first sequence number seen on this flow */
126 | uint32_t low_seq; /* lowest sequence number seen on this flow */
127 | uint32_t max_seq; /* highest sequence number sent */
128 | bool_Bool have_seq;
129 | uint32_t outstanding_bytes;
130 | uint32_t outstanding_pkts;
131 | uint32_t max_ack; /* highest acknowledgement this flow emitted */
132 | bool_Bool have_ack;
133 | uint32_t max_sacked; /* highest sequence the peer selectively acked */
134 | bool_Bool have_sacked;
135 | uint32_t last_rwnd;
136 | bool_Bool rwnd_zero;
137 | nstime_t last_tx_ts; /* when this flow last sent stream data */
138 | bool_Bool have_tx;
139 | double srtt;
140 | double rttvar;
141 | bool_Bool have_rtt;
142 | uint32_t stream_num;
143 | struct udx_flow *paired;
144 | struct udx_flow *cand; /* pairing candidate under consideration */
145 | unsigned cand_hits;
146 | } udx_flow_t;
147 |
148 | typedef struct udx_conv {
149 | wmem_map_t *flows; /* (dir << 32 | id) -> udx_flow_t */
150 | unsigned n_flows[2];
151 | unsigned client_dir; /* the side that sent first is the client */
152 | bool_Bool have_client_dir;
153 | } udx_conv_t;
154 |
155 | /* Verdicts reached on the first pass and replayed on every later visit. */
156 | #define UDX_A_RETRANS0x0001 0x0001
157 | #define UDX_A_FAST_RETRANS0x0002 0x0002
158 | #define UDX_A_RTO_RETRANS0x0004 0x0004
159 | #define UDX_A_TLP0x0008 0x0008
160 | #define UDX_A_SPURIOUS0x0010 0x0010
161 | #define UDX_A_OUT_OF_ORDER0x0020 0x0020
162 | #define UDX_A_LOST_SEGMENT0x0040 0x0040
163 | #define UDX_A_KEEPALIVE0x0080 0x0080
164 | #define UDX_A_ZERO_WIN_PROBE0x0100 0x0100
165 | #define UDX_A_ZERO_WIN0x0200 0x0200
166 | #define UDX_A_WINDOW_UPDATE0x0400 0x0400
167 | #define UDX_A_MTU_PROBE0x0800 0x0800
168 | #define UDX_A_END0x1000 0x1000
169 | #define UDX_A_DESTROY0x2000 0x2000
170 | #define UDX_A_DUPLICATE0x4000 0x4000
171 |
172 | typedef struct udx_ppd {
173 | uint32_t flags;
174 | uint32_t acks_frame; /* frame this packet acknowledges */
175 | nstime_t ack_rtt;
176 | bool_Bool have_ack_rtt;
177 | uint32_t bytes_in_flight;
178 | uint32_t packets_in_flight;
179 | uint32_t seq; /* sender sequence, to find our own segment */
180 | bool_Bool tracked; /* this packet consumed a sequence number */
181 | uint32_t stream; /* stream number at analysis time */
182 | uint32_t follow_offset; /* position within this direction */
183 | bool_Bool from_server;
184 | bool_Bool follow_ok; /* payload belongs in the reassembled stream */
185 | udx_flow_t *flow;
186 | } udx_ppd_t;
187 |
188 | static int hf_udx_magic;
189 | static int hf_udx_version;
190 | static int hf_udx_flags;
191 | static int hf_udx_flags_data;
192 | static int hf_udx_flags_end;
193 | static int hf_udx_flags_sack;
194 | static int hf_udx_flags_message;
195 | static int hf_udx_flags_destroy;
196 | static int hf_udx_flags_heartbeat;
197 | static int hf_udx_data_offset;
198 | static int hf_udx_id;
199 | static int hf_udx_window;
200 | static int hf_udx_seq;
201 | static int hf_udx_ack;
202 | static int hf_udx_sacks;
203 | static int hf_udx_sack_block;
204 | static int hf_udx_sack_start;
205 | static int hf_udx_sack_end;
206 | static int hf_udx_padding;
207 | static int hf_udx_payload;
208 | static int hf_udx_payload_len;
209 | static int hf_udx_stream;
210 | static int hf_udx_analysis;
211 | static int hf_udx_analysis_acks_frame;
212 | static int hf_udx_analysis_acked_in;
213 | static int hf_udx_analysis_ack_rtt;
214 | static int hf_udx_analysis_bytes_in_flight;
215 | static int hf_udx_analysis_pkts_in_flight;
216 | static int hf_udx_analysis_no_reverse;
217 |
218 | static int ett_udx;
219 | static int ett_udx_flags;
220 | static int ett_udx_sacks;
221 | static int ett_udx_sack_block;
222 | static int ett_udx_analysis;
223 |
224 | static expert_field ei_udx_retrans;
225 | static expert_field ei_udx_fast_retrans;
226 | static expert_field ei_udx_rto_retrans;
227 | static expert_field ei_udx_tlp;
228 | static expert_field ei_udx_spurious_retrans;
229 | static expert_field ei_udx_duplicate;
230 | static expert_field ei_udx_out_of_order;
231 | static expert_field ei_udx_lost_segment;
232 | static expert_field ei_udx_keepalive;
233 | static expert_field ei_udx_zero_window_probe;
234 | static expert_field ei_udx_zero_window;
235 | static expert_field ei_udx_window_update;
236 | static expert_field ei_udx_mtu_probe;
237 | static expert_field ei_udx_end;
238 | static expert_field ei_udx_destroy;
239 |
240 | static int * const udx_flag_fields[] = {
241 | &hf_udx_flags_data,
242 | &hf_udx_flags_end,
243 | &hf_udx_flags_sack,
244 | &hf_udx_flags_message,
245 | &hf_udx_flags_destroy,
246 | &hf_udx_flags_heartbeat,
247 | NULL((void*)0)
248 | };
249 |
250 | /* Build a "DATA,SACK"-style summary of the flags byte; bare 0 is an ACK. */
251 | static void
252 | udx_flags_to_str(uint8_t flags, char *buf, size_t buf_len)
253 | {
254 | static const struct {
255 | uint8_t bit;
256 | const char *name;
257 | } bits[] = {
258 | { UDX_FLAG_DATA0x01, "DATA" },
259 | { UDX_FLAG_END0x02, "END" },
260 | { UDX_FLAG_SACK0x04, "SACK" },
261 | { UDX_FLAG_MESSAGE0x08, "MESSAGE" },
262 | { UDX_FLAG_DESTROY0x10, "DESTROY" },
263 | { UDX_FLAG_HEARTBEAT0x20, "HEARTBEAT" },
264 | };
265 | size_t pos = 0;
266 |
267 | if (flags == 0) {
268 | (void) g_strlcpy(buf, "ACK", buf_len);
269 | return;
270 | }
271 | buf[0] = '\0';
272 | for (size_t i = 0; i < array_length(bits)(sizeof (bits) / sizeof (bits)[0]); i++) {
273 | if (flags & bits[i].bit) {
274 | if (pos > 0)
275 | pos += g_strlcpy(buf + pos, ",", buf_len - pos);
276 | pos += g_strlcpy(buf + pos, bits[i].name, buf_len - pos);
277 | }
278 | }
279 | }
280 |
281 | /*
282 | * A stable label for the two endpoints of the enclosing UDP conversation.
283 | * Which endpoint gets 0 does not matter; only that a given endpoint keeps
284 | * the same label for the whole capture.
285 | */
286 | static unsigned
287 | udx_direction(const packet_info *pinfo)
288 | {
289 | int c = cmp_address(&pinfo->src, &pinfo->dst);
290 |
291 | if (c != 0)
292 | return (c < 0) ? 0 : 1;
293 | return (pinfo->srcport < pinfo->destport) ? 0 : 1;
294 | }
295 |
296 | static udx_flow_t *
297 | udx_get_flow(udx_conv_t *conv, unsigned dir, uint32_t id, const nstime_t *ts)
298 | {
299 | uint64_t key = ((uint64_t) dir << 32) | id;
300 | udx_flow_t *flow = (udx_flow_t *) wmem_map_lookup(conv->flows, &key);
301 | uint64_t *key_copy;
302 |
303 | if (flow != NULL((void*)0))
304 | return flow;
305 |
306 | flow = wmem_new0(wmem_file_scope(), udx_flow_t)((udx_flow_t*)wmem_alloc0((wmem_file_scope()), sizeof(udx_flow_t )));
307 | flow->id = id;
308 | flow->dir = dir;
309 | flow->order = conv->n_flows[dir]++;
310 | flow->first_ts = *ts;
311 | flow->segs = wmem_tree_new(wmem_file_scope());
312 | flow->stream_num = udx_stream_count++;
313 |
314 | key_copy = wmem_new(wmem_file_scope(), uint64_t)((uint64_t*)wmem_alloc((wmem_file_scope()), sizeof(uint64_t)) );
315 | *key_copy = key;
316 | wmem_map_insert(conv->flows, key_copy, flow);
317 |
318 | return flow;
319 | }
320 |
321 | /*
322 | * Pairing a flow with its reverse.
323 | *
324 | * A UDX conversation is a six tuple, but a packet carries only the receiver's
325 | * stream id: the ids are exchanged in an encrypted handshake that is not
326 | * visible here. The reverse flow therefore has to be inferred.
327 | *
328 | * Candidates are the flows travelling the other way. Each is scored on
329 | * - plausibility: the acknowledgement carried by this packet must fall
330 | * within the sequence numbers the candidate has actually sent;
331 | * - creation order: streams are set up in pairs, so the n'th flow in one
332 | * direction usually answers the n'th flow in the other;
333 | * - proximity: the two flows should have appeared at about the same time.
334 | *
335 | * A candidate is adopted once it has been the sole best choice twice, which
336 | * keeps a single ambiguous packet from binding the wrong pair. Once bound,
337 | * a pairing is never revised.
338 | */
339 | typedef struct udx_pair_scan {
340 | udx_flow_t *self;
341 | uint32_t ack;
342 | udx_flow_t *best;
343 | int best_score;
344 | bool_Bool tie;
345 | } udx_pair_scan_t;
346 |
347 | static void
348 | udx_score_candidate(void *key _U___attribute__((unused)), void *value, void *userdata)
349 | {
350 | udx_flow_t *cand = (udx_flow_t *) value;
351 | udx_pair_scan_t *scan = (udx_pair_scan_t *) userdata;
352 | double dt;
353 | int score = 0;
354 |
355 | if (cand->dir == scan->self->dir || cand->paired != NULL((void*)0))
356 | return;
357 |
358 | /* The acknowledgement must not reach past what the candidate has sent. */
359 | if (cand->have_seq) {
360 | if (UDX_SEQ_GT(scan->ack, cand->max_seq + 1)((int32_t)((scan->ack) - (cand->max_seq + 1)) > 0))
361 | return;
362 | score += 4;
363 | }
364 |
365 | if (cand->order == scan->self->order)
366 | score += 2;
367 |
368 | dt = nstime_to_sec(&cand->first_ts) - nstime_to_sec(&scan->self->first_ts);
369 | if (dt < 0)
370 | dt = -dt;
371 | if (dt < 0.050)
372 | score += 1;
373 |
374 | if (score > scan->best_score) {
375 | scan->best_score = score;
376 | scan->best = cand;
377 | scan->tie = false0;
378 | } else if (score == scan->best_score && scan->best != NULL((void*)0)) {
379 | scan->tie = true1;
380 | }
381 | }
382 |
383 | static void
384 | udx_try_pair(udx_conv_t *conv, udx_flow_t *flow, uint32_t ack)
385 | {
386 | udx_pair_scan_t scan;
387 |
388 | scan.self = flow;
389 | scan.ack = ack;
390 | scan.best = NULL((void*)0);
391 | scan.best_score = 0;
392 | scan.tie = false0;
393 |
394 | wmem_map_foreach(conv->flows, udx_score_candidate, &scan);
395 |
396 | if (scan.best == NULL((void*)0) || scan.tie) {
397 | flow->cand = NULL((void*)0);
398 | flow->cand_hits = 0;
399 | return;
400 | }
401 |
402 | /*
403 | * One unambiguous winner is adopted at once, so that a stream is paired
404 | * from its first packet on. An ambiguous scan binds nothing and is
405 | * retried on the next packet, by which time the acknowledgements have
406 | * usually separated the candidates.
407 | */
408 | flow->cand = scan.best;
409 | flow->cand_hits++;
410 |
411 | flow->paired = scan.best;
412 | scan.best->paired = flow;
413 |
414 | /* Both halves report the lower of the two numbers. */
415 | if (scan.best->stream_num < flow->stream_num)
416 | flow->stream_num = scan.best->stream_num;
417 | else
418 | scan.best->stream_num = flow->stream_num;
419 | }
420 |
421 | /* RFC 6298 smoothing, fed only by segments that were never retransmitted. */
422 | static void
423 | udx_update_rtt(udx_flow_t *flow, double sample)
424 | {
425 | if (!flow->have_rtt) {
426 | flow->srtt = sample;
427 | flow->rttvar = sample / 2;
428 | flow->have_rtt = true1;
429 | return;
430 | }
431 | flow->rttvar = 0.75 * flow->rttvar + 0.25 * fabs(flow->srtt - sample);
432 | flow->srtt = 0.875 * flow->srtt + 0.125 * sample;
433 | }
434 |
435 | static double
436 | udx_rto(const udx_flow_t *flow)
437 | {
438 | double rto;
439 |
440 | if (!flow->have_rtt)
441 | return UDX_MIN_RTO1.0;
442 | rto = flow->srtt + 4 * flow->rttvar;
443 | return (rto < UDX_MIN_RTO1.0) ? UDX_MIN_RTO1.0 : rto;
444 | }
445 |
446 | /*
447 | * Retire one segment. A packet stops being in flight the moment it is
448 | * acknowledged, whether cumulatively or selectively, so both paths come
449 | * through here. Returns true only for the frame that first acknowledged it,
450 | * which keeps a later cumulative acknowledgement of an already selectively
451 | * acknowledged packet from counting it twice.
452 | */
453 | static bool_Bool
454 | udx_retire_seg(packet_info *pinfo, udx_flow_t *flow, udx_seg_t *seg)
455 | {
456 | if (seg->acked_in_frame != 0)
457 | return false0;
458 |
459 | seg->acked_in_frame = pinfo->num;
460 | seg->ack_ts = pinfo->abs_ts;
461 |
462 | if (flow->outstanding_pkts > 0) {
463 | flow->outstanding_pkts--;
464 | flow->outstanding_bytes -= seg->len;
465 | }
466 | return true1;
467 | }
468 |
469 | /*
470 | * Retire every segment of the acknowledged flow below "ack", link the last
471 | * of them to the acknowledging packet, and take an RTT sample from it.
472 | */
473 | static void
474 | udx_process_ack(packet_info *pinfo, udx_flow_t *acked_flow,
475 | uint32_t ack, uint32_t prev_ack, bool_Bool have_prev, udx_ppd_t *ppd)
476 | {
477 | udx_seg_t *newest = NULL((void*)0);
478 | uint32_t seq = ack - 1;
479 | unsigned guard;
480 |
481 | if (!acked_flow->have_seq)
482 | return;
483 |
484 | /*
485 | * Walk back over the range this acknowledgement newly covers, which
486 | * starts just above the previous cumulative acknowledgement. A segment
487 | * already retired, by an earlier acknowledgement or by a selective one,
488 | * is skipped rather than counted again, and the walk carries on past it
489 | * so that anything older still outstanding is retired too. The counter
490 | * only bounds pathological captures.
491 | */
492 | for (guard = 0; guard < 4096; guard++, seq--) {
493 | udx_seg_t *seg;
494 |
495 | if (have_prev && UDX_SEQ_LT(seq, prev_ack)((int32_t)((seq) - (prev_ack)) < 0))
496 | break;
497 | if (UDX_SEQ_LT(seq, acked_flow->low_seq)((int32_t)((seq) - (acked_flow->low_seq)) < 0))
498 | break;
499 |
500 | seg = (udx_seg_t *) wmem_tree_lookup32(acked_flow->segs, seq);
501 | if (seg == NULL((void*)0))
502 | continue;
503 |
504 | if (udx_retire_seg(pinfo, acked_flow, seg) && newest == NULL((void*)0))
505 | newest = seg;
506 | }
507 |
508 | if (newest != NULL((void*)0)) {
509 | nstime_t rtt;
510 |
511 | nstime_delta(&rtt, &pinfo->abs_ts, &newest->ts);
512 | ppd->acks_frame = newest->frame;
513 | ppd->ack_rtt = rtt;
514 | ppd->have_ack_rtt = true1;
515 |
516 | /*
517 | * Karn's algorithm: a retransmitted segment yields no usable sample.
518 | * The sample times a packet this flow sent, so it belongs to the flow
519 | * that sent it and not to the one reporting the acknowledgement. The
520 | * two are only ever the same on a stream carrying data both ways; on
521 | * a one-way transfer the sending flow would otherwise never obtain a
522 | * round trip time at all, and every timeout test would fall back on
523 | * the floor.
524 | */
525 | if (newest->retrans == 0)
526 | udx_update_rtt(acked_flow, nstime_to_sec(&rtt));
527 | }
528 | }
529 |
530 | static void
531 | udx_analyze(packet_info *pinfo, udx_conv_t *conv, uint8_t flags, uint8_t data_offset,
532 | uint32_t id, uint32_t window, uint32_t seq, uint32_t ack,
533 | uint32_t payload_len, const uint32_t *sack_start, const uint32_t *sack_end,
534 | unsigned n_sacks, udx_ppd_t *ppd)
535 | {
536 | unsigned dir = udx_direction(pinfo);
537 | udx_flow_t *flow = udx_get_flow(conv, dir, id, &pinfo->abs_ts);
538 | udx_flow_t *rflow;
539 | udx_seg_t *seg;
540 | bool_Bool consumes_seq;
541 |
542 | if (!conv->have_client_dir) {
543 | conv->client_dir = dir;
544 | conv->have_client_dir = true1;
545 | }
546 |
547 | ppd->flow = flow;
548 | ppd->seq = seq;
549 | ppd->from_server = (dir != conv->client_dir);
550 |
551 | if (flow->paired == NULL((void*)0))
552 | udx_try_pair(conv, flow, ack);
553 | rflow = flow->paired;
554 |
555 | /* DATA and END occupy a sequence number; MESSAGE is an unordered
556 | * datagram outside the stream and a bare ACK only reports one. */
557 | consumes_seq = (flags & (UDX_FLAG_DATA0x01 | UDX_FLAG_END0x02)) != 0;
558 |
559 | if (consumes_seq) {
560 | seg = (udx_seg_t *) wmem_tree_lookup32(flow->segs, seq);
561 |
562 | if (seg == NULL((void*)0)) {
563 | /*
564 | * A tail loss probe does not have to repeat the tail: libudx can
565 | * send the next new packet as the probe instead. Seen from here
566 | * that is a fresh sequence number extending the flow after a
567 | * probe-sized pause, while earlier data is still unacknowledged,
568 | * which is the sender prodding for an acknowledgement rather than
569 | * an application with more to say.
570 | */
571 | if (payload_len > 0 && flow->have_tx && flow->have_seq &&
572 | flow->outstanding_pkts > 0 && seq == flow->max_seq + 1) {
573 | double idle = nstime_to_sec(&pinfo->abs_ts) -
574 | nstime_to_sec(&flow->last_tx_ts);
575 |
576 | if (idle >= (flow->have_rtt ? 2 * flow->srtt : UDX_MIN_RTO1.0))
577 | ppd->flags |= UDX_A_TLP0x0008;
578 | }
579 |
580 | if (flow->have_seq && UDX_SEQ_GT(seq, flow->max_seq + 1)((int32_t)((seq) - (flow->max_seq + 1)) > 0))
581 | ppd->flags |= UDX_A_LOST_SEGMENT0x0040;
582 | else if (flow->have_seq && UDX_SEQ_LT(seq, flow->max_seq)((int32_t)((seq) - (flow->max_seq)) < 0))
583 | ppd->flags |= UDX_A_OUT_OF_ORDER0x0020;
584 |
585 | seg = wmem_new0(wmem_file_scope(), udx_seg_t)((udx_seg_t*)wmem_alloc0((wmem_file_scope()), sizeof(udx_seg_t )));
586 | seg->frame = pinfo->num;
587 | seg->ts = pinfo->abs_ts;
588 | seg->len = payload_len;
589 | wmem_tree_insert32(flow->segs, seq, seg);
590 |
591 | if (!flow->have_seq) {
592 | flow->base_seq = seq;
593 | flow->low_seq = seq;
594 | flow->max_seq = seq;
595 | flow->have_seq = true1;
596 | } else {
597 | if (UDX_SEQ_GT(seq, flow->max_seq)((int32_t)((seq) - (flow->max_seq)) > 0))
598 | flow->max_seq = seq;
599 | /* The first packet on the wire need not be the oldest: if it
600 | * was lost and resent, a lower sequence number turns up later
601 | * and still has to be accounted for. */
602 | if (UDX_SEQ_LT(seq, flow->low_seq)((int32_t)((seq) - (flow->low_seq)) < 0))
603 | flow->low_seq = seq;
604 | }
605 | flow->outstanding_pkts++;
606 | flow->outstanding_bytes += payload_len;
607 | } else {
608 | double dt = nstime_to_sec(&pinfo->abs_ts) - nstime_to_sec(&seg->ts);
609 |
610 | seg->retrans++;
611 | ppd->flags |= UDX_A_RETRANS0x0001;
612 |
613 | if (seg->acked_in_frame != 0) {
614 | ppd->flags |= UDX_A_SPURIOUS0x0010;
615 | } else if (dt < UDX_DUP_WINDOW0.0005) {
616 | /* Too soon to be any sender timer: the datagram was
617 | * delivered, or captured, twice. */
618 | ppd->flags |= UDX_A_DUPLICATE0x4000;
619 | } else if (dt >= udx_rto(flow)) {
620 | /* A whole retransmission timeout has passed. That is a timer
621 | * firing, whatever the peer has selectively acknowledged in
622 | * the meantime, so this test comes before the SACK one. */
623 | ppd->flags |= UDX_A_RTO_RETRANS0x0004;
624 | } else if (flow->have_sacked && UDX_SEQ_GT(flow->max_sacked, seq)((int32_t)((flow->max_sacked) - (seq)) > 0)) {
625 | /* The peer has selectively acknowledged later packets, so
626 | * this one was resent because it was reported missing rather
627 | * than because a timer expired. */
628 | ppd->flags |= UDX_A_FAST_RETRANS0x0002;
629 | } else if (seq == flow->max_seq &&
630 | dt >= (flow->have_rtt ? 2 * flow->srtt : UDX_MIN_RTO1.0)) {
631 | /* A repeat of the tail after a probe-sized pause, with
632 | * nothing newer sent, is how a tail loss probe looks here. */
633 | ppd->flags |= UDX_A_TLP0x0008;
634 | }
635 | }
636 |
637 | /* Used to spot the pause before a tail loss probe. */
638 | flow->last_tx_ts = pinfo->abs_ts;
639 | flow->have_tx = true1;
640 |
641 | ppd->tracked = true1;
642 | ppd->bytes_in_flight = flow->outstanding_bytes;
643 | ppd->packets_in_flight = flow->outstanding_pkts;
644 |
645 | /*
646 | * Position within the stream, counted from the first packet seen on
647 | * this flow. Anything before that point arrived out of order at the
648 | * very start of the capture and cannot be placed.
649 | */
650 | if (payload_len > 0 && UDX_SEQ_GEQ(seq, flow->base_seq)((int32_t)((seq) - (flow->base_seq)) >= 0)) {
651 | ppd->follow_offset = seq - flow->base_seq;
652 | ppd->follow_ok = true1;
653 | }
654 | }
655 |
656 | if (flags & UDX_FLAG_END0x02)
657 | ppd->flags |= UDX_A_END0x1000;
658 | if (flags & UDX_FLAG_DESTROY0x10)
659 | ppd->flags |= UDX_A_DESTROY0x2000;
660 |
661 | /* An MTU probe pads between the header and the payload; the same byte
662 | * delimits SACK blocks when they are present. */
663 | if (data_offset > 0 && !(flags & UDX_FLAG_SACK0x04))
664 | ppd->flags |= UDX_A_MTU_PROBE0x0800;
665 |
666 | /* Acknowledgement side: retire the peer's segments, then record the
667 | * selective ranges so a later repeat can be recognised as recovery. */
668 | if (rflow != NULL((void*)0)) {
669 | udx_seg_t *newest_sack = NULL((void*)0);
670 | uint32_t newest_sack_seq = 0;
671 |
672 | if (!flow->have_ack || UDX_SEQ_GT(ack, flow->max_ack)((int32_t)((ack) - (flow->max_ack)) > 0))
673 | udx_process_ack(pinfo, rflow, ack, flow->max_ack,
674 | flow->have_ack, ppd);
675 |
676 | for (unsigned i = 0; i < n_sacks; i++) {
677 | uint32_t s;
678 | unsigned guard = 0;
679 |
680 | for (s = sack_start[i]; UDX_SEQ_LT(s, sack_end[i])((int32_t)((s) - (sack_end[i])) < 0) && guard < 1024;
681 | s++, guard++) {
682 | udx_seg_t *ss = (udx_seg_t *) wmem_tree_lookup32(rflow->segs, s);
683 |
684 | if (ss == NULL((void*)0))
685 | continue;
686 |
687 | ss->sacked = true1;
688 |
689 | /* A selective acknowledgement acknowledges the packet as
690 | * surely as a cumulative one: it leaves the flight, and this
691 | * is the frame that acknowledged it. */
692 | if (udx_retire_seg(pinfo, rflow, ss) &&
693 | (newest_sack == NULL((void*)0) || UDX_SEQ_GT(s, newest_sack_seq)((int32_t)((s) - (newest_sack_seq)) > 0))) {
694 | newest_sack = ss;
695 | newest_sack_seq = s;
696 | }
697 | }
698 |
699 | /* Remember how far the selective acknowledgements reach: a
700 | * retransmission below this point is loss recovery. */
701 | if (!rflow->have_sacked || UDX_SEQ_GT(sack_end[i] - 1, rflow->max_sacked)((int32_t)((sack_end[i] - 1) - (rflow->max_sacked)) > 0 )) {
702 | rflow->max_sacked = sack_end[i] - 1;
703 | rflow->have_sacked = true1;
704 | }
705 | }
706 |
707 | /* Nothing was newly acknowledged cumulatively, but a selective range
708 | * retired a packet, so report the link and the round trip from that. */
709 | if (newest_sack != NULL((void*)0) && !ppd->have_ack_rtt) {
710 | nstime_t rtt;
711 |
712 | nstime_delta(&rtt, &pinfo->abs_ts, &newest_sack->ts);
713 | ppd->acks_frame = newest_sack->frame;
714 | ppd->ack_rtt = rtt;
715 | ppd->have_ack_rtt = true1;
716 |
717 | /* Karn's algorithm again: never sample a retransmitted packet.
718 | * The round trip measured belongs to the flow that sent the data,
719 | * which is where the retransmission timeout is later judged. */
720 | if (newest_sack->retrans == 0)
721 | udx_update_rtt(rflow, nstime_to_sec(&rtt));
722 | }
723 | }
724 |
725 | if (!flow->have_ack || UDX_SEQ_GT(ack, flow->max_ack)((int32_t)((ack) - (flow->max_ack)) > 0)) {
726 | flow->max_ack = ack;
727 | flow->have_ack = true1;
728 | }
729 |
730 | /* Receive window transitions. */
731 | if (window == 0) {
732 | ppd->flags |= UDX_A_ZERO_WIN0x0200;
733 | flow->rwnd_zero = true1;
734 | } else if (flow->rwnd_zero) {
735 | ppd->flags |= UDX_A_WINDOW_UPDATE0x0400;
736 | flow->rwnd_zero = false0;
737 | }
738 | flow->last_rwnd = window;
739 |
740 | /*
741 | * Keepalives and zero-window probes are the same bytes on the wire: a
742 | * bare heartbeat. Only the peer's advertised window tells them apart.
743 | */
744 | if ((flags & UDX_FLAG_HEARTBEAT0x20) && payload_len == 0) {
745 | if (rflow != NULL((void*)0) && rflow->rwnd_zero)
746 | ppd->flags |= UDX_A_ZERO_WIN_PROBE0x0100;
747 | else
748 | ppd->flags |= UDX_A_KEEPALIVE0x0080;
749 | }
750 |
751 | ppd->stream = (flow->paired != NULL((void*)0) && flow->paired->stream_num < flow->stream_num)
752 | ? flow->paired->stream_num
753 | : flow->stream_num;
754 | }
755 |
756 | /*
757 | * Render the verdicts reached on the first pass. Nothing here computes: on a
758 | * revisit the stored results are simply replayed, so what is shown never
759 | * depends on how the packet was reached.
760 | */
761 | static void
762 | udx_show_analysis(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, udx_ppd_t *ppd)
763 | {
764 | proto_item *ti;
765 | proto_tree *an_tree;
766 | udx_seg_t *seg;
767 |
768 | if (ppd->flow == NULL((void*)0))
769 | return;
770 |
771 | ti = proto_tree_add_uint(tree, hf_udx_stream, tvb, 0, 0,
772 | (ppd->flow->paired != NULL((void*)0) &&
773 | ppd->flow->paired->stream_num < ppd->flow->stream_num)
774 | ? ppd->flow->paired->stream_num
775 | : ppd->flow->stream_num);
776 | proto_item_set_generated(ti);
777 |
778 | /* Nothing to report on a packet that neither carries data nor advances
779 | * an acknowledgement, so leave the subtree out entirely rather than
780 | * showing an empty one. */
781 | if (ppd->flags == 0 && ppd->acks_frame == 0 && !ppd->tracked &&
782 | ppd->flow->paired != NULL((void*)0))
783 | return;
784 |
785 | ti = proto_tree_add_item(tree, hf_udx_analysis, tvb, 0, 0, ENC_NA0x00000000);
786 | proto_item_set_generated(ti);
787 | an_tree = proto_item_add_subtree(ti, ett_udx_analysis);
788 |
789 | if (ppd->flow->paired == NULL((void*)0)) {
790 | proto_item *rev_ti = proto_tree_add_item(an_tree, hf_udx_analysis_no_reverse,
791 | tvb, 0, 0, ENC_NA0x00000000);
792 | proto_item_set_generated(rev_ti);
793 | }
794 |
795 | if (ppd->acks_frame != 0) {
796 | ti = proto_tree_add_uint(an_tree, hf_udx_analysis_acks_frame, tvb, 0, 0,
797 | ppd->acks_frame);
798 | proto_item_set_generated(ti);
799 |
800 | if (ppd->have_ack_rtt) {
801 | ti = proto_tree_add_time(an_tree, hf_udx_analysis_ack_rtt, tvb, 0, 0,
802 | &ppd->ack_rtt);
803 | proto_item_set_generated(ti);
804 | }
805 | }
806 |
807 | /* A packet that carried data learns only later which packet acked it. */
808 | if (ppd->tracked) {
809 | seg = (udx_seg_t *) wmem_tree_lookup32(ppd->flow->segs, ppd->seq);
810 | if (seg != NULL((void*)0) && seg->frame == pinfo->num && seg->acked_in_frame != 0) {
811 | nstime_t rtt;
812 |
813 | ti = proto_tree_add_uint(an_tree, hf_udx_analysis_acked_in, tvb, 0, 0,
814 | seg->acked_in_frame);
815 | proto_item_set_generated(ti);
816 |
817 | nstime_delta(&rtt, &seg->ack_ts, &seg->ts);
818 | ti = proto_tree_add_time(an_tree, hf_udx_analysis_ack_rtt, tvb, 0, 0, &rtt);
819 | proto_item_set_generated(ti);
820 | }
821 |
822 | ti = proto_tree_add_uint(an_tree, hf_udx_analysis_bytes_in_flight, tvb, 0, 0,
823 | ppd->bytes_in_flight);
824 | proto_item_set_generated(ti);
825 | ti = proto_tree_add_uint(an_tree, hf_udx_analysis_pkts_in_flight, tvb, 0, 0,
826 | ppd->packets_in_flight);
827 | proto_item_set_generated(ti);
828 | }
829 |
830 | /* Expert notes, most specific classification first. */
831 | if (ppd->flags & UDX_A_LOST_SEGMENT0x0040)
832 | expert_add_info(pinfo, ti, &ei_udx_lost_segment);
833 | if (ppd->flags & UDX_A_OUT_OF_ORDER0x0020)
834 | expert_add_info(pinfo, ti, &ei_udx_out_of_order);
835 |
836 | if (ppd->flags & UDX_A_SPURIOUS0x0010)
837 | expert_add_info(pinfo, ti, &ei_udx_spurious_retrans);
838 | else if (ppd->flags & UDX_A_FAST_RETRANS0x0002)
839 | expert_add_info(pinfo, ti, &ei_udx_fast_retrans);
840 | else if (ppd->flags & UDX_A_RTO_RETRANS0x0004)
841 | expert_add_info(pinfo, ti, &ei_udx_rto_retrans);
842 | else if (ppd->flags & UDX_A_TLP0x0008)
843 | expert_add_info(pinfo, ti, &ei_udx_tlp);
844 | else if (ppd->flags & UDX_A_DUPLICATE0x4000)
845 | expert_add_info(pinfo, ti, &ei_udx_duplicate);
846 | else if (ppd->flags & UDX_A_RETRANS0x0001)
847 | expert_add_info(pinfo, ti, &ei_udx_retrans);
848 |
849 | if (ppd->flags & UDX_A_ZERO_WIN_PROBE0x0100)
850 | expert_add_info(pinfo, ti, &ei_udx_zero_window_probe);
851 | else if (ppd->flags & UDX_A_KEEPALIVE0x0080)
852 | expert_add_info(pinfo, ti, &ei_udx_keepalive);
853 |
854 | if (ppd->flags & UDX_A_ZERO_WIN0x0200)
855 | expert_add_info(pinfo, ti, &ei_udx_zero_window);
856 | if (ppd->flags & UDX_A_WINDOW_UPDATE0x0400)
857 | expert_add_info(pinfo, ti, &ei_udx_window_update);
858 | if (ppd->flags & UDX_A_MTU_PROBE0x0800)
859 | expert_add_info(pinfo, ti, &ei_udx_mtu_probe);
860 | if (ppd->flags & UDX_A_END0x1000)
861 | expert_add_info(pinfo, ti, &ei_udx_end);
862 | if (ppd->flags & UDX_A_DESTROY0x2000)
863 | expert_add_info(pinfo, ti, &ei_udx_destroy);
864 | }
865 |
866 |
867 | /*
868 | * Conversations and endpoints.
869 | *
870 | * A UDX conversation is one stream, not one socket pair. Several streams can
871 | * be multiplexed over a single UDP flow, so the enclosing UDP conversation
872 | * counts them together and cannot say how much traffic any one of them
873 | * carried. Keying on the stream index the dissector already assigns splits
874 | * them apart.
875 | */
876 |
877 | static const char *
878 | udx_conv_get_filter_type(conv_item_t *conv, conv_filter_type_e filter)
879 | {
880 | if (filter == CONV_FT_SRC_PORT)
881 | return "udp.srcport";
882 |
883 | if (filter == CONV_FT_DST_PORT)
884 | return "udp.dstport";
885 |
886 | if (filter == CONV_FT_ANY_PORT)
887 | return "udp.port";
888 |
889 | if (conv == NULL((void*)0))
890 | return CONV_FILTER_INVALID"INVALID";
891 |
892 | if (filter == CONV_FT_SRC_ADDRESS) {
893 | if (conv->src_address.type == AT_IPv4)
894 | return "ip.src";
895 | if (conv->src_address.type == AT_IPv6)
896 | return "ipv6.src";
897 | }
898 |
899 | if (filter == CONV_FT_DST_ADDRESS) {
900 | if (conv->dst_address.type == AT_IPv4)
901 | return "ip.dst";
902 | if (conv->dst_address.type == AT_IPv6)
903 | return "ipv6.dst";
904 | }
905 |
906 | if (filter == CONV_FT_ANY_ADDRESS) {
907 | if (conv->src_address.type == AT_IPv4)
908 | return "ip.addr";
909 | if (conv->src_address.type == AT_IPv6)
910 | return "ipv6.addr";
911 | }
912 |
913 | return CONV_FILTER_INVALID"INVALID";
914 | }
915 |
916 | static ct_dissector_info_t udx_ct_dissector_info = { &udx_conv_get_filter_type };
917 |
918 | static tap_packet_status
919 | udx_conversation_packet(void *pct, packet_info *pinfo, epan_dissect_t *edt _U___attribute__((unused)),
920 | const void *vip, tap_flags_t flags)
921 | {
922 | conv_hash_t *hash = (conv_hash_t *) pct;
923 | const udx_info_t *udxh = (const udx_info_t *) vip;
924 |
925 | hash->flags = flags;
926 |
927 | add_conversation_table_data_with_conv_id(hash, &udxh->ip_src, &udxh->ip_dst,
928 | udxh->sport, udxh->dport,
929 | (conv_id_t) udxh->stream, 1,
930 | pinfo->fd->pkt_len,
931 | &pinfo->rel_ts, &pinfo->abs_ts,
932 | &udx_ct_dissector_info,
933 | CONVERSATION_UDX);
934 |
935 | return TAP_PACKET_REDRAW;
936 | }
937 |
938 | static const char *
939 | udx_endpoint_get_filter_type(endpoint_item_t *endpoint, conv_filter_type_e filter)
940 | {
941 | if (filter == CONV_FT_SRC_PORT)
942 | return "udp.srcport";
943 |
944 | if (filter == CONV_FT_DST_PORT)
945 | return "udp.dstport";
946 |
947 | if (filter == CONV_FT_ANY_PORT)
948 | return "udp.port";
949 |
950 | if (endpoint == NULL((void*)0))
951 | return CONV_FILTER_INVALID"INVALID";
952 |
953 | if (filter == CONV_FT_SRC_ADDRESS) {
954 | if (endpoint->myaddress.type == AT_IPv4)
955 | return "ip.src";
956 | if (endpoint->myaddress.type == AT_IPv6)
957 | return "ipv6.src";
958 | }
959 |
960 | if (filter == CONV_FT_DST_ADDRESS) {
961 | if (endpoint->myaddress.type == AT_IPv4)
962 | return "ip.dst";
963 | if (endpoint->myaddress.type == AT_IPv6)
964 | return "ipv6.dst";
965 | }
966 |
967 | if (filter == CONV_FT_ANY_ADDRESS) {
968 | if (endpoint->myaddress.type == AT_IPv4)
969 | return "ip.addr";
970 | if (endpoint->myaddress.type == AT_IPv6)
971 | return "ipv6.addr";
972 | }
973 |
974 | return CONV_FILTER_INVALID"INVALID";
975 | }
976 |
977 | static et_dissector_info_t udx_endpoint_dissector_info = { &udx_endpoint_get_filter_type };
978 |
979 | static tap_packet_status
980 | udx_endpoint_packet(void *pit, packet_info *pinfo, epan_dissect_t *edt _U___attribute__((unused)),
981 | const void *vip, tap_flags_t flags)
982 | {
983 | conv_hash_t *hash = (conv_hash_t *) pit;
984 | const udx_info_t *udxh = (const udx_info_t *) vip;
985 |
986 | hash->flags = flags;
987 |
988 | /* One pass per direction, so a datagram addressed to its own sender is
989 | * still counted for both endpoints. */
990 | add_endpoint_table_data(hash, &udxh->ip_src, udxh->sport, true1, 1,
991 | pinfo->fd->pkt_len, &udx_endpoint_dissector_info,
992 | ENDPOINT_UDXCONVERSATION_UDX);
993 | add_endpoint_table_data(hash, &udxh->ip_dst, udxh->dport, false0, 1,
994 | pinfo->fd->pkt_len, &udx_endpoint_dissector_info,
995 | ENDPOINT_UDXCONVERSATION_UDX);
996 |
997 | return TAP_PACKET_REDRAW;
998 | }
999 |
1000 |
1001 | /*
1002 | * Follow stream.
1003 | *
1004 | * Payload is delivered in sequence order per direction. A packet that
1005 | * arrives early is held until the gap before it is filled, and a payload
1006 | * already delivered - a retransmission - is dropped, so the reassembled
1007 | * conversation reads the way the application saw it rather than the way the
1008 | * network happened to deliver it.
1009 | */
1010 |
1011 | /* Stream numbers restart with every capture file, as they do for TCP. */
1012 | static void
1013 | udx_init(void)
1014 | {
1015 | udx_stream_count = 0;
1016 | }
1017 |
1018 | static char *
1019 | udx_follow_conv_filter(epan_dissect_t *edt _U___attribute__((unused)), packet_info *pinfo,
1020 | unsigned *stream, unsigned *sub_stream _U___attribute__((unused)))
1021 | {
1022 | udx_ppd_t *ppd = (udx_ppd_t *) p_get_proto_data(wmem_file_scope(), pinfo, proto_udx, 0);
1023 |
1024 | if (ppd == NULL((void*)0) || ppd->flow == NULL((void*)0))
1025 | return NULL((void*)0);
1026 |
1027 | *stream = ppd->stream;
1028 | return ws_strdup_printf("udx.stream eq %u", ppd->stream)wmem_strdup_printf(((void*)0), "udx.stream eq %u", ppd->stream );
1029 | }
1030 |
1031 | static char *
1032 | udx_follow_index_filter(unsigned stream, unsigned sub_stream _U___attribute__((unused)))
1033 | {
1034 | return ws_strdup_printf("udx.stream eq %u", stream)wmem_strdup_printf(((void*)0), "udx.stream eq %u", stream);
1035 | }
1036 |
1037 | static unsigned
1038 | udx_get_stream_count(void)
1039 | {
1040 | return udx_stream_count;
1041 | }
1042 |
1043 | static void
1044 | udx_follow_append(follow_info_t *follow_info, follow_record_t *record)
1045 | {
1046 | follow_info->payload = g_list_prepend(follow_info->payload, record);
1047 | follow_info->bytes_written[record->is_server ? 1 : 0] += record->data->len;
1048 | }
1049 |
1050 | static int
1051 | udx_follow_seq_cmp(const void *a, const void *b)
1052 | {
1053 | const follow_record_t *ra = (const follow_record_t *) a;
1054 | const follow_record_t *rb = (const follow_record_t *) b;
1055 |
1056 | if (ra->seq == rb->seq)
1057 | return 0;
1058 | return (ra->seq < rb->seq) ? -1 : 1;
1059 | }
1060 |
1061 | /*
1062 | * Release held payload that now continues the stream. The pending list is
1063 | * kept in sequence order, so this only ever walks its front.
1064 | */
1065 | static void
1066 | udx_follow_drain(follow_info_t *follow_info, int dir)
1067 | {
1068 | while (follow_info->fragments[dir] != NULL((void*)0)) {
1069 | follow_record_t *held = (follow_record_t *) follow_info->fragments[dir]->data;
1070 |
1071 | /*
1072 | * A copy of this packet reached the delivery point ahead of the one
1073 | * held here, which happens whenever a retransmission arrives while an
1074 | * earlier gap is still open. The held copy has nothing left to give,
1075 | * and leaving it at the head of the list would stop every packet
1076 | * behind it from ever being released.
1077 | */
1078 | if (held->seq < follow_info->seq[dir]) {
1079 | follow_info->fragments[dir] =
1080 | g_list_delete_link(follow_info->fragments[dir],
1081 | follow_info->fragments[dir]);
1082 | g_byte_array_free(held->data, true1);
1083 | g_free(held)(__builtin_object_size ((held), 0) != ((size_t) - 1)) ? g_free_sized (held, __builtin_object_size ((held), 0)) : (g_free) (held);
1084 | continue;
1085 | }
1086 |
1087 | if (held->seq != follow_info->seq[dir])
1088 | break;
1089 |
1090 | follow_info->seq[dir]++;
1091 | follow_info->fragments[dir] = g_list_delete_link(follow_info->fragments[dir],
1092 | follow_info->fragments[dir]);
1093 | udx_follow_append(follow_info, held);
1094 | }
1095 | }
1096 |
1097 | static tap_packet_status
1098 | udx_follow_tap_listener(void *tapdata, packet_info *pinfo, epan_dissect_t *edt _U___attribute__((unused)),
1099 | const void *data, tap_flags_t flags _U___attribute__((unused)))
1100 | {
1101 | follow_info_t *follow_info = (follow_info_t *) tapdata;
1102 | const udx_follow_tap_data_t *follow_data = (const udx_follow_tap_data_t *) data;
1103 | follow_record_t *record;
1104 | unsigned length = tvb_captured_length(follow_data->tvb);
1105 | int dir = follow_data->from_server ? 1 : 0;
1106 |
1107 | if (follow_info->stream_id != follow_data->stream)
1108 | return TAP_PACKET_DONT_REDRAW;
1109 |
1110 | /* Already delivered: a retransmission or a duplicate. */
1111 | if (follow_data->offset < follow_info->seq[dir])
1112 | return TAP_PACKET_DONT_REDRAW;
1113 |
1114 | record = g_new0(follow_record_t, 1)((follow_record_t *) g_malloc0_n ((1), sizeof (follow_record_t )));
1115 | record->is_server = follow_data->from_server;
1116 | record->packet_num = pinfo->fd->num;
1117 | record->abs_ts = pinfo->fd->abs_ts;
1118 | record->seq = follow_data->offset;
1119 | record->data = g_byte_array_sized_new(length);
1120 | record->data = g_byte_array_append(record->data,
1121 | tvb_get_ptr(follow_data->tvb, 0, length), length);
1122 |
1123 | if (follow_data->from_server) {
1124 | if (follow_info->server_port == 0) {
1125 | follow_info->server_port = pinfo->srcport;
1126 | copy_address(&follow_info->server_ip, &pinfo->src);
1127 | follow_info->client_port = pinfo->destport;
1128 | copy_address(&follow_info->client_ip, &pinfo->dst);
1129 | }
1130 | } else {
1131 | if (follow_info->client_port == 0) {
1132 | follow_info->client_port = pinfo->srcport;
1133 | copy_address(&follow_info->client_ip, &pinfo->src);
1134 | follow_info->server_port = pinfo->destport;
1135 | copy_address(&follow_info->server_ip, &pinfo->dst);
1136 | }
1137 | }
1138 |
1139 | if (follow_data->offset == follow_info->seq[dir]) {
1140 | follow_info->seq[dir]++;
1141 | udx_follow_append(follow_info, record);
1142 | udx_follow_drain(follow_info, dir);
1143 | } else {
1144 | /* Arrived early: hold it, in order, until the gap ahead is filled.
1145 | * The framework frees whatever is still pending when the stream is
1146 | * reset, so an unfilled gap leaks nothing. */
1147 | follow_info->fragments[dir] = g_list_insert_sorted(follow_info->fragments[dir],
1148 | record, udx_follow_seq_cmp);
1149 | }
1150 |
1151 | return TAP_PACKET_DONT_REDRAW;
1152 | }
1153 |
1154 | static int
1155 | dissect_udx(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void *data _U___attribute__((unused)))
1156 | {
1157 | proto_item *ti;
1158 | proto_tree *udx_tree;
1159 | uint8_t flags, data_offset;
1160 | uint32_t id, window, seq, ack;
1161 | int offset = 0;
1162 | int sack_end_offset;
1163 | unsigned payload_len;
1164 | char flags_str[64];
1165 | uint32_t sack_start[UDX_MAX_SACK_BLOCKS50];
1166 | uint32_t sack_end[UDX_MAX_SACK_BLOCKS50];
1167 | unsigned n_sacks = 0;
1168 | udx_ppd_t *ppd = NULL((void*)0);
1169 |
1170 | /*
1171 | * Reached either from the heuristic, which has already validated the
1172 | * header, or directly once a conversation has been claimed or through
1173 | * "Decode As". The latter routes make this check load bearing.
1174 | */
1175 | if (tvb_reported_length(tvb) < UDX_HEADER_SIZE20)
1176 | return 0;
1177 |
1178 | col_set_str(pinfo->cinfo, COL_PROTOCOL, "UDX");
1179 | col_clear(pinfo->cinfo, COL_INFO);
1180 |
1181 | flags = tvb_get_uint8(tvb, 2);
1182 | data_offset = tvb_get_uint8(tvb, 3);
1183 | id = tvb_get_letohl(tvb, 4);
1184 | window = tvb_get_letohl(tvb, 8);
1185 | seq = tvb_get_letohl(tvb, 12);
1186 | ack = tvb_get_letohl(tvb, 16);
1187 |
1188 | udx_flags_to_str(flags, flags_str, sizeof(flags_str));
1189 |
1190 | /* Collect the selective acknowledgement ranges before anything is added
1191 | * to the tree: the analysis below needs them, and the display needs the
1192 | * analysis. */
1193 | if (flags & UDX_FLAG_SACK0x04) {
1194 | /* Blocks fill the area delimited by data_offset; a packet with no
1195 | * payload may leave that byte zero and run to the end instead. */
1196 | sack_end_offset = (data_offset > 0)
1197 | ? UDX_HEADER_SIZE20 + data_offset
1198 | : (int) tvb_reported_length(tvb);
1199 | } else {
1200 | /* Anything reserved without SACK blocks is MTU probe padding. */
1201 | sack_end_offset = UDX_HEADER_SIZE20 + data_offset;
1202 | }
1203 |
1204 | /* data_offset is not trustworthy on a packet this dissector did not
1205 | * validate, so never let it point past the datagram. */
1206 | 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)));
1207 |
1208 | if (flags & UDX_FLAG_SACK0x04) {
1209 | int pos = UDX_HEADER_SIZE20;
1210 |
1211 | while (pos + 8 <= sack_end_offset && n_sacks < UDX_MAX_SACK_BLOCKS50) {
1212 | sack_start[n_sacks] = tvb_get_letohl(tvb, pos);
1213 | sack_end[n_sacks] = tvb_get_letohl(tvb, pos + 4);
1214 | n_sacks++;
1215 | pos += 8;
1216 | }
1217 | }
1218 |
1219 | 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));
1220 |
1221 | if (udx_analyze_sequence_numbers) {
1222 | if (!PINFO_FD_VISITED(pinfo)((pinfo)->fd->visited)) {
1223 | conversation_t *conversation = find_or_create_conversation(pinfo);
1224 | udx_conv_t *conv;
1225 |
1226 | conv = (udx_conv_t *) conversation_get_proto_data(conversation, proto_udx);
1227 | if (conv == NULL((void*)0)) {
1228 | conv = wmem_new0(wmem_file_scope(), udx_conv_t)((udx_conv_t*)wmem_alloc0((wmem_file_scope()), sizeof(udx_conv_t )));
1229 | conv->flows = wmem_map_new(wmem_file_scope(), g_int64_hash, g_int64_equal);
1230 | conversation_add_proto_data(conversation, proto_udx, conv);
1231 | }
1232 |
1233 | ppd = wmem_new0(wmem_file_scope(), udx_ppd_t)((udx_ppd_t*)wmem_alloc0((wmem_file_scope()), sizeof(udx_ppd_t )));
1234 | udx_analyze(pinfo, conv, flags, data_offset, id, window, seq, ack,
1235 | payload_len, sack_start, sack_end, n_sacks, ppd);
1236 | p_add_proto_data(wmem_file_scope(), pinfo, proto_udx, 0, ppd);
1237 | } else {
1238 | ppd = (udx_ppd_t *) p_get_proto_data(wmem_file_scope(), pinfo, proto_udx, 0);
1239 | }
1240 | }
1241 |
1242 | ti = proto_tree_add_item(tree, proto_udx, tvb, 0, -1, ENC_NA0x00000000);
1243 | proto_item_append_text(ti, ", %s, Id: %u, Seq: %u, Ack: %u", flags_str, id, seq, ack);
1244 | udx_tree = proto_item_add_subtree(ti, ett_udx);
1245 |
1246 | proto_tree_add_item(udx_tree, hf_udx_magic, tvb, offset, 1, ENC_NA0x00000000);
1247 | offset += 1;
1248 | proto_tree_add_item(udx_tree, hf_udx_version, tvb, offset, 1, ENC_NA0x00000000);
1249 | offset += 1;
1250 | proto_tree_add_bitmask(udx_tree, tvb, offset, hf_udx_flags, ett_udx_flags,
1251 | udx_flag_fields, ENC_NA0x00000000);
1252 | offset += 1;
1253 | proto_tree_add_item(udx_tree, hf_udx_data_offset, tvb, offset, 1, ENC_NA0x00000000);
1254 | offset += 1;
1255 | proto_tree_add_item(udx_tree, hf_udx_id, tvb, offset, 4, ENC_LITTLE_ENDIAN0x80000000);
1256 | offset += 4;
1257 | proto_tree_add_item(udx_tree, hf_udx_window, tvb, offset, 4, ENC_LITTLE_ENDIAN0x80000000);
1258 | offset += 4;
1259 | proto_tree_add_item(udx_tree, hf_udx_seq, tvb, offset, 4, ENC_LITTLE_ENDIAN0x80000000);
1260 | offset += 4;
1261 | proto_tree_add_item(udx_tree, hf_udx_ack, tvb, offset, 4, ENC_LITTLE_ENDIAN0x80000000);
1262 | offset += 4;
1263 |
1264 | if (n_sacks > 0) {
1265 | proto_item *sacks_ti;
1266 | proto_tree *sacks_tree, *block_tree;
1267 |
1268 | sacks_ti = proto_tree_add_item(udx_tree, hf_udx_sacks, tvb, offset,
1269 | sack_end_offset - offset, ENC_NA0x00000000);
1270 | proto_item_append_text(sacks_ti, " (%u)", n_sacks);
1271 | sacks_tree = proto_item_add_subtree(sacks_ti, ett_udx_sacks);
1272 |
1273 | for (unsigned i = 0; i < n_sacks; i++) {
1274 | block_tree = proto_tree_add_subtree_format(sacks_tree, tvb, offset, 8,
1275 | ett_udx_sack_block, NULL((void*)0),
1276 | "SACK: %u-%u",
1277 | sack_start[i], sack_end[i]);
1278 | proto_tree_add_item(block_tree, hf_udx_sack_start, tvb, offset, 4,
1279 | ENC_LITTLE_ENDIAN0x80000000);
1280 | proto_tree_add_item(block_tree, hf_udx_sack_end, tvb, offset + 4, 4,
1281 | ENC_LITTLE_ENDIAN0x80000000);
1282 | offset += 8;
1283 | }
1284 | } else if (!(flags & UDX_FLAG_SACK0x04) && data_offset > 0) {
1285 | /*
1286 | * Padding between header and payload with no SACK blocks: inserted by
1287 | * mtu_probeify_packet() in libudx - this datagram is an MTU probe.
1288 | */
1289 | proto_tree_add_item(udx_tree, hf_udx_padding, tvb, offset, data_offset, ENC_NA0x00000000);
1290 | offset += data_offset;
| Value stored to 'offset' is never read
1291 | }
1292 |
1293 | if (payload_len > 0) {
1294 | ti = proto_tree_add_uint(udx_tree, hf_udx_payload_len, tvb, 0, 0, payload_len);
1295 | proto_item_set_generated(ti);
1296 | proto_tree_add_item(udx_tree, hf_udx_payload, tvb, sack_end_offset,
1297 | (int) payload_len, ENC_NA0x00000000);
1298 | }
1299 |
1300 | if (ppd != NULL((void*)0)) {
1301 | udx_show_analysis(tvb, pinfo, udx_tree, ppd);
1302 |
1303 | if (have_tap_listener(udx_tap)) {
1304 | udx_info_t *udxh = wmem_new0(pinfo->pool, udx_info_t)((udx_info_t*)wmem_alloc0((pinfo->pool), sizeof(udx_info_t )));
1305 |
1306 | udxh->id = id;
1307 | udxh->seq = seq;
1308 | udxh->ack = ack;
1309 | udxh->window = window;
1310 | udxh->payload_len = payload_len;
1311 | udxh->stream = ppd->stream;
1312 | udxh->sport = pinfo->srcport;
1313 | udxh->dport = pinfo->destport;
1314 | udxh->flags = flags;
1315 | udxh->data_offset = data_offset;
1316 | copy_address_shallow(&udxh->ip_src, &pinfo->src);
1317 | copy_address_shallow(&udxh->ip_dst, &pinfo->dst);
1318 |
1319 | udxh->num_sack_blocks = n_sacks;
1320 | for (unsigned i = 0; i < n_sacks; i++) {
1321 | udxh->sack_start[i] = sack_start[i];
1322 | udxh->sack_end[i] = sack_end[i];
1323 | }
1324 |
1325 | tap_queue_packet(udx_tap, pinfo, udxh);
1326 | }
1327 |
1328 | /* MESSAGE payloads travel outside the ordered stream, so they are
1329 | * shown per packet but left out of the reassembled conversation. */
1330 | if (ppd->follow_ok && !(flags & UDX_FLAG_MESSAGE0x08) &&
1331 | have_tap_listener(udx_follow_tap)) {
1332 | 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)));
1333 |
1334 | follow_data->tvb = tvb_new_subset_length(tvb, sack_end_offset, (int) payload_len);
1335 | follow_data->stream = ppd->stream;
1336 | follow_data->offset = ppd->follow_offset;
1337 | follow_data->from_server = ppd->from_server;
1338 | tap_queue_packet(udx_follow_tap, pinfo, follow_data);
1339 | }
1340 | }
1341 |
1342 | col_add_fstr(pinfo->cinfo, COL_INFO, "%s Id=%u Seq=%u Ack=%u Rwnd=%u",
1343 | flags_str, id, seq, ack, window);
1344 | if (payload_len > 0)
1345 | col_append_fstr(pinfo->cinfo, COL_INFO, " Len=%u", payload_len);
1346 | if (ppd != NULL((void*)0) && (ppd->flags & UDX_A_RETRANS0x0001))
1347 | col_append_str(pinfo->cinfo, COL_INFO, " [retransmission]");
1348 |
1349 | return tvb_reported_length(tvb);
1350 | }
1351 |
1352 | static bool_Bool
1353 | test_udx(tvbuff_t *tvb)
1354 | {
1355 | uint8_t flags, data_offset;
1356 |
1357 | if (tvb_captured_length(tvb) < UDX_HEADER_SIZE20)
1358 | return false0;
1359 | if (tvb_get_uint8(tvb, 0) != UDX_MAGIC_BYTE0xff)
1360 | return false0;
1361 | if (tvb_get_uint8(tvb, 1) != UDX_VERSION1)
1362 | return false0;
1363 |
1364 | flags = tvb_get_uint8(tvb, 2);
1365 | if (flags & ~UDX_FLAG_MASK0x3f)
1366 | return false0;
1367 |
1368 | data_offset = tvb_get_uint8(tvb, 3);
1369 | if (UDX_HEADER_SIZE20 + (unsigned) data_offset > tvb_reported_length(tvb))
1370 | return false0;
1371 | /* The area delimited by data_offset holds SACK blocks (uint32 pairs) when
1372 | * the SACK flag is set - anything not a multiple of 8 is not UDX. */
1373 | if ((flags & UDX_FLAG_SACK0x04) && data_offset > 0 && (data_offset % 8) != 0)
1374 | return false0;
1375 |
1376 | /*
1377 | * Only DATA and MESSAGE packets carry a payload. Everything else is the
1378 | * fixed header followed at most by selective acknowledgement blocks, so
1379 | * its length is known exactly and anything else is not UDX.
1380 | */
1381 | if (!(flags & (UDX_FLAG_DATA0x01 | UDX_FLAG_MESSAGE0x08))) {
1382 | unsigned trailing = tvb_reported_length(tvb) - UDX_HEADER_SIZE20;
1383 |
1384 | if (flags & UDX_FLAG_SACK0x04) {
1385 | if ((trailing % 8) != 0)
1386 | return false0;
1387 | } else if (trailing != 0) {
1388 | return false0;
1389 | }
1390 | }
1391 |
1392 | return true1;
1393 | }
1394 |
1395 | static bool_Bool
1396 | dissect_udx_heur(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void *data)
1397 | {
1398 | conversation_t *conversation;
1399 |
1400 | if (!test_udx(tvb))
1401 | return false0;
1402 |
1403 | /* Claim the whole UDP conversation so weaker frames (e.g. bare 20-byte
1404 | * heartbeats) and future packets skip the heuristic. */
1405 | conversation = find_or_create_conversation(pinfo);
1406 | conversation_set_dissector(conversation, udx_handle);
1407 |
1408 | dissect_udx(tvb, pinfo, tree, data);
1409 | return true1;
1410 | }
1411 |
1412 | void
1413 | proto_register_udx(void)
1414 | {
1415 | static hf_register_info hf[] = {
1416 | { &hf_udx_magic,
1417 | { "Magic Byte", "udx.magic_byte", FT_UINT8, BASE_HEX, NULL((void*)0), 0x0,
1418 | "Always 0xff", HFILL-1, 0, HF_REF_TYPE_NONE, -1, ((void*)0) }
1419 | },
1420 | { &hf_udx_version,
1421 | { "Version", "udx.version", FT_UINT8, BASE_DEC, NULL((void*)0), 0x0,
1422 | NULL((void*)0), HFILL-1, 0, HF_REF_TYPE_NONE, -1, ((void*)0) }
1423 | },
1424 | { &hf_udx_flags,
1425 | { "Type", "udx.type", FT_UINT8, BASE_HEX, NULL((void*)0), 0x0,
1426 | "Packet type flags", HFILL-1, 0, HF_REF_TYPE_NONE, -1, ((void*)0) }
1427 | },
1428 | { &hf_udx_flags_data,
1429 | { "Data", "udx.type.data", FT_BOOLEAN, 8, NULL((void*)0), UDX_FLAG_DATA0x01,
1430 | "Carries stream payload", HFILL-1, 0, HF_REF_TYPE_NONE, -1, ((void*)0) }
1431 | },
1432 | { &hf_udx_flags_end,
1433 | { "End", "udx.type.end", FT_BOOLEAN, 8, NULL((void*)0), UDX_FLAG_END0x02,
1434 | "Graceful end of stream (consumes a sequence number)", HFILL-1, 0, HF_REF_TYPE_NONE, -1, ((void*)0) }
1435 | },
1436 | { &hf_udx_flags_sack,
1437 | { "SACK", "udx.type.sack", FT_BOOLEAN, 8, NULL((void*)0), UDX_FLAG_SACK0x04,
1438 | "Carries selective acknowledgement blocks", HFILL-1, 0, HF_REF_TYPE_NONE, -1, ((void*)0) }
1439 | },
1440 | { &hf_udx_flags_message,
1441 | { "Message", "udx.type.message", FT_BOOLEAN, 8, NULL((void*)0), UDX_FLAG_MESSAGE0x08,
1442 | "Unordered datagram outside the byte stream", HFILL-1, 0, HF_REF_TYPE_NONE, -1, ((void*)0) }
1443 | },
1444 | { &hf_udx_flags_destroy,
1445 | { "Destroy", "udx.type.destroy", FT_BOOLEAN, 8, NULL((void*)0), UDX_FLAG_DESTROY0x10,
1446 | "Abrupt stream termination", HFILL-1, 0, HF_REF_TYPE_NONE, -1, ((void*)0) }
1447 | },
1448 | { &hf_udx_flags_heartbeat,
1449 | { "Heartbeat", "udx.type.heartbeat", FT_BOOLEAN, 8, NULL((void*)0), UDX_FLAG_HEARTBEAT0x20,
1450 | "Keepalive or zero-window probe", HFILL-1, 0, HF_REF_TYPE_NONE, -1, ((void*)0) }
1451 | },
1452 | { &hf_udx_data_offset,
1453 | { "Data Offset", "udx.data_offset", FT_UINT8, BASE_DEC, NULL((void*)0), 0x0,
1454 | "Bytes between the fixed header and the payload", HFILL-1, 0, HF_REF_TYPE_NONE, -1, ((void*)0) }
1455 | },
1456 | { &hf_udx_id,
1457 | { "Id", "udx.id", FT_UINT32, BASE_DEC, NULL((void*)0), 0x0,
1458 | "Receiver's stream id", HFILL-1, 0, HF_REF_TYPE_NONE, -1, ((void*)0) }
1459 | },
1460 | { &hf_udx_window,
1461 | { "Window", "udx.rwnd", FT_UINT32, BASE_DEC, NULL((void*)0), 0x0,
1462 | "Sender's receive window in bytes", HFILL-1, 0, HF_REF_TYPE_NONE, -1, ((void*)0) }
1463 | },
1464 | { &hf_udx_seq,
1465 | { "Seq", "udx.seq", FT_UINT32, BASE_DEC, NULL((void*)0), 0x0,
1466 | "Packet sequence number (counts packets, not bytes)", HFILL-1, 0, HF_REF_TYPE_NONE, -1, ((void*)0) }
1467 | },
1468 | { &hf_udx_ack,
1469 | { "Ack", "udx.ack", FT_UINT32, BASE_DEC, NULL((void*)0), 0x0,
1470 | "Next sequence number expected from the peer", HFILL-1, 0, HF_REF_TYPE_NONE, -1, ((void*)0) }
1471 | },
1472 | { &hf_udx_sacks,
1473 | { "SACK Blocks", "udx.sacks", FT_NONE, BASE_NONE, NULL((void*)0), 0x0,
1474 | "Selective acknowledgement ranges", HFILL-1, 0, HF_REF_TYPE_NONE, -1, ((void*)0) }
1475 | },
1476 | { &hf_udx_sack_block,
1477 | { "SACK Block", "udx.sack", FT_NONE, BASE_NONE, NULL((void*)0), 0x0,
1478 | NULL((void*)0), HFILL-1, 0, HF_REF_TYPE_NONE, -1, ((void*)0) }
1479 | },
1480 | { &hf_udx_sack_start,
1481 | { "Start", "udx.sack.start", FT_UINT32, BASE_DEC, NULL((void*)0), 0x0,
1482 | "First sequence number in the acknowledged range", HFILL-1, 0, HF_REF_TYPE_NONE, -1, ((void*)0) }
1483 | },
1484 | { &hf_udx_sack_end,
1485 | { "End", "udx.sack.end", FT_UINT32, BASE_DEC, NULL((void*)0), 0x0,
1486 | "One past the last acknowledged sequence number", HFILL-1, 0, HF_REF_TYPE_NONE, -1, ((void*)0) }
1487 | },
1488 | { &hf_udx_padding,
1489 | { "Padding", "udx.padding", FT_BYTES, BASE_NONE, NULL((void*)0), 0x0,
1490 | "MTU probe padding", HFILL-1, 0, HF_REF_TYPE_NONE, -1, ((void*)0) }
1491 | },
1492 | { &hf_udx_payload,
1493 | { "Payload", "udx.payload", FT_BYTES, BASE_NONE, NULL((void*)0), 0x0,
1494 | NULL((void*)0), HFILL-1, 0, HF_REF_TYPE_NONE, -1, ((void*)0) }
1495 | },
1496 | { &hf_udx_payload_len,
1497 | { "Payload Length", "udx.length", FT_UINT32, BASE_DEC, NULL((void*)0), 0x0,
1498 | NULL((void*)0), HFILL-1, 0, HF_REF_TYPE_NONE, -1, ((void*)0) }
1499 | },
1500 | { &hf_udx_stream,
1501 | { "Stream index", "udx.stream", FT_UINT32, BASE_DEC, NULL((void*)0), 0x0,
1502 | "Index of the paired flows carrying this stream", HFILL-1, 0, HF_REF_TYPE_NONE, -1, ((void*)0) }
1503 | },
1504 | { &hf_udx_analysis,
1505 | { "SEQ/ACK analysis", "udx.analysis", FT_NONE, BASE_NONE, NULL((void*)0), 0x0,
1506 | "Results of the sequence number analysis", HFILL-1, 0, HF_REF_TYPE_NONE, -1, ((void*)0) }
1507 | },
1508 | { &hf_udx_analysis_acks_frame,
1509 | { "This is an ACK to the packet in frame", "udx.analysis.acks_frame",
1510 | FT_FRAMENUM, BASE_NONE, FRAMENUM_TYPE(FT_FRAMENUM_ACK)((gpointer) (glong) (FT_FRAMENUM_ACK)), 0x0,
1511 | NULL((void*)0), HFILL-1, 0, HF_REF_TYPE_NONE, -1, ((void*)0) }
1512 | },
1513 | { &hf_udx_analysis_acked_in,
1514 | { "ACKed in frame", "udx.analysis.acked_in", FT_FRAMENUM, BASE_NONE,
1515 | FRAMENUM_TYPE(FT_FRAMENUM_NONE)((gpointer) (glong) (FT_FRAMENUM_NONE)), 0x0,
1516 | "The frame that acknowledged this packet", HFILL-1, 0, HF_REF_TYPE_NONE, -1, ((void*)0) }
1517 | },
1518 | { &hf_udx_analysis_ack_rtt,
1519 | { "Time to ACK", "udx.analysis.ack_rtt", FT_RELATIVE_TIME, BASE_NONE, NULL((void*)0), 0x0,
1520 | "Time between the packet and its acknowledgement", HFILL-1, 0, HF_REF_TYPE_NONE, -1, ((void*)0) }
1521 | },
1522 | { &hf_udx_analysis_bytes_in_flight,
1523 | { "Bytes in flight", "udx.analysis.bytes_in_flight", FT_UINT32, BASE_DEC,
1524 | NULL((void*)0), 0x0, "Unacknowledged payload bytes on this flow", HFILL-1, 0, HF_REF_TYPE_NONE, -1, ((void*)0) }
1525 | },
1526 | { &hf_udx_analysis_pkts_in_flight,
1527 | { "Packets in flight", "udx.analysis.packets_in_flight", FT_UINT32, BASE_DEC,
1528 | NULL((void*)0), 0x0, "Unacknowledged packets on this flow", HFILL-1, 0, HF_REF_TYPE_NONE, -1, ((void*)0) }
1529 | },
1530 | { &hf_udx_analysis_no_reverse,
1531 | { "Reverse flow not identified", "udx.analysis.no_reverse", FT_NONE, BASE_NONE,
1532 | NULL((void*)0), 0x0, "The stream carrying the other direction has not been paired",
1533 | HFILL-1, 0, HF_REF_TYPE_NONE, -1, ((void*)0) }
1534 | },
1535 | };
1536 |
1537 | static int *ett[] = {
1538 | &ett_udx,
1539 | &ett_udx_flags,
1540 | &ett_udx_sacks,
1541 | &ett_udx_sack_block,
1542 | &ett_udx_analysis,
1543 | };
1544 |
1545 | static ei_register_info ei[] = {
1546 | { &ei_udx_retrans,
1547 | { "udx.analysis.retransmission", PI_SEQUENCE0x02000000, PI_NOTE0x00400000,
1548 | "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)}} }
1549 | },
1550 | { &ei_udx_fast_retrans,
1551 | { "udx.analysis.fast_retransmission", PI_SEQUENCE0x02000000, PI_NOTE0x00400000,
1552 | "Fast retransmission: resent while later packets were selectively"
1553 | " 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)}} }
1554 | },
1555 | { &ei_udx_rto_retrans,
1556 | { "udx.analysis.rto_retransmission", PI_SEQUENCE0x02000000, PI_NOTE0x00400000,
1557 | "Retransmission timeout: resent after more than the estimated RTO",
1558 | 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)}} }
1559 | },
1560 | { &ei_udx_tlp,
1561 | { "udx.analysis.tail_loss_probe", PI_SEQUENCE0x02000000, PI_NOTE0x00400000,
1562 | "Tail loss probe: sent after a pause to draw an acknowledgement"
1563 | " out of the peer", 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)}} }
1564 | },
1565 | { &ei_udx_spurious_retrans,
1566 | { "udx.analysis.spurious_retransmission", PI_SEQUENCE0x02000000, PI_NOTE0x00400000,
1567 | "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)}} }
1568 | },
1569 | { &ei_udx_duplicate,
1570 | { "udx.analysis.duplicate", PI_SEQUENCE0x02000000, PI_NOTE0x00400000,
1571 | "Duplicate packet: the same packet was seen twice in quick succession",
1572 | 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)}} }
1573 | },
1574 | { &ei_udx_out_of_order,
1575 | { "udx.analysis.out_of_order", PI_SEQUENCE0x02000000, PI_NOTE0x00400000,
1576 | "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)}} }
1577 | },
1578 | { &ei_udx_lost_segment,
1579 | { "udx.analysis.lost_segment", PI_SEQUENCE0x02000000, PI_WARN0x00600000,
1580 | "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)}} }
1581 | },
1582 | { &ei_udx_keepalive,
1583 | { "udx.analysis.keepalive", PI_SEQUENCE0x02000000, PI_NOTE0x00400000,
1584 | "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)}} }
1585 | },
1586 | { &ei_udx_zero_window_probe,
1587 | { "udx.analysis.zero_window_probe", PI_SEQUENCE0x02000000, PI_NOTE0x00400000,
1588 | "Zero window probe: sent while the peer advertised no receive window",
1589 | 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)}} }
1590 | },
1591 | { &ei_udx_zero_window,
1592 | { "udx.analysis.zero_window", PI_SEQUENCE0x02000000, PI_WARN0x00600000,
1593 | "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)}} }
1594 | },
1595 | { &ei_udx_window_update,
1596 | { "udx.analysis.window_update", PI_SEQUENCE0x02000000, PI_CHAT0x00200000,
1597 | "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)}} }
1598 | },
1599 | { &ei_udx_mtu_probe,
1600 | { "udx.analysis.mtu_probe", PI_SEQUENCE0x02000000, PI_CHAT0x00200000,
1601 | "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)}} }
1602 | },
1603 | { &ei_udx_end,
1604 | { "udx.analysis.end", PI_SEQUENCE0x02000000, PI_CHAT0x00200000,
1605 | "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)}} }
1606 | },
1607 | { &ei_udx_destroy,
1608 | { "udx.analysis.destroy", PI_SEQUENCE0x02000000, PI_WARN0x00600000,
1609 | "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)}} }
1610 | },
1611 | };
1612 |
1613 | expert_module_t *expert_udx;
1614 | module_t *udx_module;
1615 |
1616 | proto_udx = proto_register_protocol("UDX Protocol", "UDX", "udx");
1617 | proto_register_field_array(proto_udx, hf, array_length(hf)(sizeof (hf) / sizeof (hf)[0]));
1618 | proto_register_subtree_array(ett, array_length(ett)(sizeof (ett) / sizeof (ett)[0]));
1619 |
1620 | expert_udx = expert_register_protocol(proto_udx);
1621 | expert_register_field_array(expert_udx, ei, array_length(ei)(sizeof (ei) / sizeof (ei)[0]));
1622 |
1623 | udx_handle = register_dissector("udx", dissect_udx, proto_udx);
1624 |
1625 | register_init_routine(udx_init);
1626 |
1627 | udx_follow_tap = register_tap("udx_follow");
1628 | register_follow_stream(proto_udx, "udx_follow",
1629 | udx_follow_conv_filter, udx_follow_index_filter,
1630 | udp_follow_address_filter, udp_port_to_display,
1631 | udx_follow_tap_listener, udx_get_stream_count, NULL((void*)0));
1632 |
1633 | /* The conversation and endpoint tables read this tap. It carries the
1634 | * stream index, which only exists while sequence analysis is on, so the
1635 | * tables follow the "analyze_sequence_numbers" preference. */
1636 | udx_tap = register_tap("udx");
1637 | register_conversation_table(proto_udx, false0,
1638 | udx_conversation_packet, udx_endpoint_packet);
1639 |
1640 | udx_module = prefs_register_protocol(proto_udx, NULL((void*)0));
1641 | prefs_register_bool_preference(udx_module, "analyze_sequence_numbers",
1642 | "Analyze UDX sequence numbers",
1643 | "Track sequence and acknowledgement numbers to pair flows, measure "
1644 | "round-trip times and flag retransmissions",
1645 | &udx_analyze_sequence_numbers);
1646 | }
1647 |
1648 | void
1649 | proto_reg_handoff_udx(void)
1650 | {
1651 | heur_dissector_add("udp", dissect_udx_heur, "UDX over UDP", "udx_udp",
1652 | proto_udx, HEURISTIC_DISABLE);
1653 | dissector_add_for_decode_as_with_preference("udp.port", udx_handle);
1654 | }
1655 |
1656 | /*
1657 | * Editor modelines - https://www.wireshark.org/tools/modelines.html
1658 | *
1659 | * Local variables:
1660 | * c-basic-offset: 4
1661 | * tab-width: 8
1662 | * indent-tabs-mode: nil
1663 | * End:
1664 | *
1665 | * vi: set shiftwidth=4 tabstop=8 expandtab:
1666 | * :indentSize=4:tabSize=8:noTabs=true:
1667 | */