/src/wireshark/epan/dissectors/packet-udpcp.c
Line | Count | Source |
1 | | /* packet-udpcp.c |
2 | | * |
3 | | * Routines for UDPCP packet dissection (UDP-based reliable communication protocol). |
4 | | * Described in the Open Base Station Initiative Reference Point 1 Specification |
5 | | * (see https://web.archive.org/web/20171206005927/http://www.obsai.com/specs/RP1%20Spec%20v2_1.pdf, Appendix A) |
6 | | * |
7 | | * Wireshark - Network traffic analyzer |
8 | | * By Gerald Combs <gerald@wireshark.org> |
9 | | * Copyright 1998 Gerald Combs |
10 | | * |
11 | | * |
12 | | * SPDX-License-Identifier: GPL-2.0-or-later |
13 | | */ |
14 | | |
15 | | /* TODO: |
16 | | * - Calculate/verify Checksum field |
17 | | */ |
18 | | |
19 | | #include "config.h" |
20 | | |
21 | | #include <epan/conversation.h> |
22 | | #include <epan/reassemble.h> |
23 | | #include <epan/expert.h> |
24 | | #include <epan/prefs.h> |
25 | | |
26 | | |
27 | | void proto_register_udpcp(void); |
28 | | |
29 | | static int proto_udpcp; |
30 | | |
31 | | static int hf_udpcp_checksum; |
32 | | static int hf_udpcp_msg_type; |
33 | | static int hf_udpcp_version; |
34 | | |
35 | | static int hf_udpcp_packet_transfer_options; |
36 | | static int hf_udpcp_n; |
37 | | static int hf_udpcp_c; |
38 | | static int hf_udpcp_s; |
39 | | static int hf_udpcp_d; |
40 | | static int hf_udpcp_reserved; |
41 | | |
42 | | static int hf_udpcp_fragment_amount; |
43 | | static int hf_udpcp_fragment_number; |
44 | | |
45 | | static int hf_udpcp_message_id; |
46 | | static int hf_udpcp_message_data_length; |
47 | | |
48 | | static int hf_udpcp_payload; |
49 | | |
50 | | static int hf_udpcp_ack_frame; |
51 | | static int hf_udpcp_sn_frame; |
52 | | |
53 | | |
54 | | /* For reassembly */ |
55 | | static int hf_udpcp_fragments; |
56 | | static int hf_udpcp_fragment; |
57 | | static int hf_udpcp_fragment_overlap; |
58 | | static int hf_udpcp_fragment_overlap_conflict; |
59 | | static int hf_udpcp_fragment_multiple_tails; |
60 | | static int hf_udpcp_fragment_too_long_fragment; |
61 | | static int hf_udpcp_fragment_error; |
62 | | static int hf_udpcp_fragment_count; |
63 | | static int hf_udpcp_reassembled_in; |
64 | | static int hf_udpcp_reassembled_length; |
65 | | static int hf_udpcp_reassembled_data; |
66 | | |
67 | | |
68 | | /* Subtrees */ |
69 | | static int ett_udpcp; |
70 | | static int ett_udpcp_packet_transfer_options; |
71 | | static int ett_udpcp_fragments; |
72 | | static int ett_udpcp_fragment; |
73 | | |
74 | | static const fragment_items udpcp_frag_items = { |
75 | | &ett_udpcp_fragment, |
76 | | &ett_udpcp_fragments, |
77 | | &hf_udpcp_fragments, |
78 | | &hf_udpcp_fragment, |
79 | | &hf_udpcp_fragment_overlap, |
80 | | &hf_udpcp_fragment_overlap_conflict, |
81 | | &hf_udpcp_fragment_multiple_tails, |
82 | | &hf_udpcp_fragment_too_long_fragment, |
83 | | &hf_udpcp_fragment_error, |
84 | | &hf_udpcp_fragment_count, |
85 | | &hf_udpcp_reassembled_in, |
86 | | &hf_udpcp_reassembled_length, |
87 | | &hf_udpcp_reassembled_data, |
88 | | "UDPCP fragments" |
89 | | }; |
90 | | |
91 | | |
92 | | static expert_field ei_udpcp_checksum_should_be_zero; |
93 | | static expert_field ei_udpcp_d_not_zero_for_data; |
94 | | static expert_field ei_udpcp_reserved_not_zero; |
95 | | static expert_field ei_udpcp_n_s_ack; |
96 | | static expert_field ei_udpcp_payload_wrong_size; |
97 | | static expert_field ei_udpcp_wrong_sequence_number; |
98 | | static expert_field ei_udpcp_no_ack; |
99 | | static expert_field ei_udpcp_no_sn_frame; |
100 | | |
101 | | static dissector_handle_t udpcp_handle; |
102 | | |
103 | | |
104 | | void proto_reg_handoff_udpcp (void); |
105 | | |
106 | | /* User definable values */ |
107 | | static range_t *global_udpcp_port_range; |
108 | | |
109 | 0 | #define DATA_FORMAT 0x01 |
110 | 0 | #define ACK_FORMAT 0x02 |
111 | | |
112 | | |
113 | | static const value_string msg_type_vals[] = { |
114 | | { DATA_FORMAT, "Data Packet" }, |
115 | | { ACK_FORMAT, "Ack Packet" }, |
116 | | { 0, NULL } |
117 | | }; |
118 | | |
119 | | typedef struct { |
120 | | /* Protocol is bi-directional, so need to distinguish */ |
121 | | uint16_t first_dest_port; |
122 | | address first_dest_address; |
123 | | |
124 | | /* Main these so can link between SN frames and ACKs */ |
125 | | wmem_tree_t *sn_table_first; |
126 | | wmem_tree_t *ack_table_first; |
127 | | wmem_tree_t *sn_table_second; |
128 | | wmem_tree_t *ack_table_second; |
129 | | |
130 | | /* Remember next expected message-id in each direction */ |
131 | | uint32_t next_message_id_first; |
132 | | uint32_t next_message_id_second; |
133 | | } udpcp_conversation_t; |
134 | | |
135 | | |
136 | | /* Framenum -> expected_sequence_number */ |
137 | | static wmem_tree_t *sequence_number_result_table; |
138 | | |
139 | | |
140 | | /* Reassembly table. */ |
141 | | static reassembly_table udpcp_reassembly_table; |
142 | | |
143 | | static void *udpcp_temporary_key(const packet_info *pinfo _U_, const uint32_t id _U_, const void *data) |
144 | 0 | { |
145 | 0 | return (void *)data; |
146 | 0 | } |
147 | | |
148 | | static void *udpcp_persistent_key(const packet_info *pinfo _U_, const uint32_t id _U_, |
149 | | const void *data) |
150 | 0 | { |
151 | 0 | return (void *)data; |
152 | 0 | } |
153 | | |
154 | | static void udpcp_free_temporary_key(void *ptr _U_) |
155 | 0 | { |
156 | 0 | } |
157 | | |
158 | | static void udpcp_free_persistent_key(void *ptr _U_) |
159 | 0 | { |
160 | 0 | } |
161 | | |
162 | | static reassembly_table_functions udpcp_reassembly_table_functions = |
163 | | { |
164 | | g_direct_hash, |
165 | | g_direct_equal, |
166 | | udpcp_temporary_key, |
167 | | udpcp_persistent_key, |
168 | | udpcp_free_temporary_key, |
169 | | udpcp_free_persistent_key |
170 | | }; |
171 | | |
172 | | |
173 | | /**************************************************************************/ |
174 | | /* Preferences state */ |
175 | | /**************************************************************************/ |
176 | | |
177 | | /* Reassemble by default */ |
178 | | static bool global_udpcp_reassemble = true; |
179 | | |
180 | | /* By default do try to decode payload as XML/SOAP */ |
181 | | static bool global_udpcp_decode_payload_as_soap = true; |
182 | | |
183 | | |
184 | | static dissector_handle_t xml_handle; |
185 | | |
186 | | /******************************/ |
187 | | /* Main dissection function. */ |
188 | | static int |
189 | | dissect_udpcp(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void* data _U_) |
190 | 0 | { |
191 | 0 | proto_tree *udpcp_tree; |
192 | 0 | proto_item *root_ti; |
193 | 0 | int offset = 0; |
194 | | |
195 | | /* Must be at least 12 bytes */ |
196 | 0 | if (tvb_reported_length(tvb) < 12) { |
197 | 0 | return 0; |
198 | 0 | } |
199 | | |
200 | | /* Has to be Data or Ack format. */ |
201 | 0 | uint32_t msg_type = tvb_get_uint8(tvb, 4) >> 6; |
202 | 0 | if ((msg_type != DATA_FORMAT) && (msg_type != ACK_FORMAT)) { |
203 | 0 | return 0; |
204 | 0 | } |
205 | | |
206 | | /* Protocol column */ |
207 | 0 | col_set_str(pinfo->cinfo, COL_PROTOCOL, "UDPCP"); |
208 | | |
209 | | /* Protocol root */ |
210 | 0 | root_ti = proto_tree_add_item(tree, proto_udpcp, tvb, offset, -1, ENC_NA); |
211 | 0 | udpcp_tree = proto_item_add_subtree(root_ti, ett_udpcp); |
212 | | |
213 | | /* Checksum */ |
214 | 0 | uint32_t checksum; |
215 | 0 | proto_item *checksum_ti = proto_tree_add_item_ret_uint(udpcp_tree, hf_udpcp_checksum, tvb, offset, 4, ENC_BIG_ENDIAN, &checksum); |
216 | 0 | offset += 4; |
217 | | |
218 | | /* Msg-type */ |
219 | 0 | proto_tree_add_item_ret_uint(udpcp_tree, hf_udpcp_msg_type, tvb, offset, 1, ENC_BIG_ENDIAN, &msg_type); |
220 | 0 | col_add_str(pinfo->cinfo, COL_INFO, |
221 | 0 | (msg_type == DATA_FORMAT) ? "[Data] " : "[Ack] "); |
222 | 0 | proto_item_append_text(root_ti, (msg_type == DATA_FORMAT) ? " [Data]" : " [Ack]"); |
223 | | |
224 | | /* Version */ |
225 | 0 | proto_tree_add_item(udpcp_tree, hf_udpcp_version, tvb, offset, 1, ENC_BIG_ENDIAN); |
226 | | |
227 | | |
228 | | /***************************/ |
229 | | /* Packet Transfer Options */ |
230 | 0 | proto_item *packet_transfer_options_ti = |
231 | 0 | proto_tree_add_string_format(udpcp_tree, hf_udpcp_packet_transfer_options, tvb, offset, 2, |
232 | 0 | "", "Packet Transfer Options ("); |
233 | 0 | proto_tree *packet_transfer_options_tree = |
234 | 0 | proto_item_add_subtree(packet_transfer_options_ti, ett_udpcp_packet_transfer_options); |
235 | 0 | uint32_t n, c, s, d; |
236 | | |
237 | | /* N */ |
238 | 0 | proto_tree_add_item_ret_uint(packet_transfer_options_tree, hf_udpcp_n, tvb, offset, 1, ENC_BIG_ENDIAN, &n); |
239 | 0 | if (n) { |
240 | 0 | proto_item_append_text(packet_transfer_options_ti, "N"); |
241 | 0 | } |
242 | | |
243 | | /* C */ |
244 | 0 | proto_tree_add_item_ret_uint(packet_transfer_options_tree, hf_udpcp_c, tvb, offset, 1, ENC_BIG_ENDIAN, &c); |
245 | 0 | if (c) { |
246 | 0 | proto_item_append_text(packet_transfer_options_ti, "C"); |
247 | 0 | } |
248 | 0 | if (!c && checksum) { |
249 | | /* Expert info warning that checksum should be 0 if !c */ |
250 | 0 | expert_add_info(pinfo, checksum_ti, &ei_udpcp_checksum_should_be_zero); |
251 | 0 | } |
252 | | |
253 | | /* S */ |
254 | 0 | proto_tree_add_item_ret_uint(packet_transfer_options_tree, hf_udpcp_s, tvb, offset, 1, ENC_BIG_ENDIAN, &s); |
255 | 0 | offset++; |
256 | 0 | if (s) { |
257 | 0 | proto_item_append_text(packet_transfer_options_ti, "S"); |
258 | 0 | } |
259 | | |
260 | | /* D */ |
261 | 0 | proto_item *d_ti = proto_tree_add_item_ret_uint(packet_transfer_options_tree, hf_udpcp_d, tvb, offset, 1, ENC_BIG_ENDIAN, &d); |
262 | 0 | if (d) { |
263 | 0 | proto_item_append_text(packet_transfer_options_ti, "D"); |
264 | 0 | } |
265 | | /* Expert info if D not zero for data */ |
266 | 0 | if ((msg_type == DATA_FORMAT) && d) { |
267 | 0 | expert_add_info(pinfo, d_ti, &ei_udpcp_d_not_zero_for_data); |
268 | 0 | } |
269 | | |
270 | | /* Reserved */ |
271 | 0 | uint32_t reserved; |
272 | 0 | proto_item *reserved_ti = proto_tree_add_item_ret_uint(packet_transfer_options_tree, hf_udpcp_reserved, tvb, offset, 1, ENC_BIG_ENDIAN, &reserved); |
273 | 0 | offset++; |
274 | | /* Expert info if reserved not 0 */ |
275 | 0 | if (reserved) { |
276 | 0 | expert_add_info(pinfo, reserved_ti, &ei_udpcp_reserved_not_zero); |
277 | 0 | } |
278 | |
|
279 | 0 | proto_item_append_text(packet_transfer_options_ti, ")"); |
280 | | /*************************/ |
281 | | |
282 | | |
283 | | /* Fragment Amount & Fragment Number */ |
284 | 0 | uint32_t fragment_amount, fragment_number; |
285 | 0 | proto_tree_add_item_ret_uint(udpcp_tree, hf_udpcp_fragment_amount, tvb, offset, 1, ENC_BIG_ENDIAN, &fragment_amount); |
286 | 0 | offset++; |
287 | 0 | proto_tree_add_item_ret_uint(udpcp_tree, hf_udpcp_fragment_number, tvb, offset, 1, ENC_BIG_ENDIAN, &fragment_number); |
288 | 0 | offset++; |
289 | | |
290 | | /* Message ID & Message Data Length */ |
291 | 0 | uint32_t message_id; |
292 | 0 | proto_item *message_id_ti = proto_tree_add_item_ret_uint(udpcp_tree, hf_udpcp_message_id, tvb, offset, 2, ENC_BIG_ENDIAN, &message_id); |
293 | 0 | col_append_fstr(pinfo->cinfo, COL_INFO, " Msg_ID=%3u", message_id); |
294 | 0 | offset += 2; |
295 | 0 | uint32_t data_length; |
296 | 0 | proto_tree_add_item_ret_uint(udpcp_tree, hf_udpcp_message_data_length, tvb, offset, 2, ENC_BIG_ENDIAN, &data_length); |
297 | 0 | offset += 2; |
298 | |
|
299 | 0 | if (msg_type == DATA_FORMAT) { |
300 | 0 | if (!data_length) { |
301 | | /* This could just be a sync frame */ |
302 | 0 | if (!message_id && !n && !s) { |
303 | 0 | col_append_str(pinfo->cinfo, COL_INFO, " [Sync]"); |
304 | 0 | } |
305 | 0 | } |
306 | | |
307 | | /* Show if/when this frame should be acknowledged */ |
308 | 0 | if (!n && !s) { |
309 | 0 | proto_item_append_text(packet_transfer_options_ti, " (All packets ACKd)"); |
310 | 0 | } |
311 | 0 | else if (!n && s) { |
312 | 0 | proto_item_append_text(packet_transfer_options_ti, " (Last fragment ACKd)"); |
313 | 0 | } |
314 | 0 | if (n) { |
315 | 0 | proto_item_append_text(packet_transfer_options_ti, " (Not ACKd)"); |
316 | 0 | } |
317 | | |
318 | | /* Show fragment numbering. Ignore confusing 0-based fragment numbering.. */ |
319 | 0 | col_append_fstr(pinfo->cinfo, COL_INFO, " [Frag %u/%u]", |
320 | 0 | fragment_number+1, fragment_amount); |
321 | | |
322 | | /* There is data */ |
323 | 0 | if ((fragment_amount == 1) && (fragment_number == 0) && data_length) { |
324 | | /* Not fragmented - show payload now */ |
325 | 0 | proto_item *data_ti = proto_tree_add_item(udpcp_tree, hf_udpcp_payload, tvb, offset, -1, ENC_NA); |
326 | 0 | col_append_fstr(pinfo->cinfo, COL_INFO, " Data (%u bytes)", data_length); |
327 | | |
328 | | /* Check length is as signalled */ |
329 | 0 | if (data_length != (uint32_t)tvb_reported_length_remaining(tvb, offset)) { |
330 | 0 | expert_add_info_format(pinfo, data_ti, &ei_udpcp_payload_wrong_size, "Data length field was %u but %u bytes found", |
331 | 0 | data_length, tvb_reported_length_remaining(tvb, offset)); |
332 | 0 | } |
333 | |
|
334 | 0 | if (global_udpcp_decode_payload_as_soap) { |
335 | | /* Send to XML dissector */ |
336 | 0 | tvbuff_t *next_tvb = tvb_new_subset_remaining(tvb, offset); |
337 | 0 | call_dissector_only(xml_handle, next_tvb, pinfo, tree, NULL); |
338 | 0 | } |
339 | 0 | } |
340 | 0 | else { |
341 | | /* Fragmented */ |
342 | 0 | if (global_udpcp_reassemble && data_length) { |
343 | | /* Reassembly */ |
344 | | /* Set fragmented flag. */ |
345 | 0 | bool save_fragmented = pinfo->fragmented; |
346 | 0 | pinfo->fragmented = true; |
347 | 0 | fragment_head *fh; |
348 | 0 | unsigned frag_data_len = tvb_reported_length_remaining(tvb, offset); |
349 | | |
350 | | /* Add this fragment into reassembly */ |
351 | 0 | fh = fragment_add_seq_check(&udpcp_reassembly_table, tvb, offset, pinfo, |
352 | 0 | message_id, /* id */ |
353 | 0 | GUINT_TO_POINTER(message_id), /* data */ |
354 | 0 | fragment_number, /* frag_number */ |
355 | 0 | frag_data_len, /* frag_data_len */ |
356 | 0 | (fragment_number < (fragment_amount-1)) /* more_frags */ |
357 | 0 | ); |
358 | |
|
359 | 0 | bool update_col_info = true; |
360 | | /* See if this completes an SDU */ |
361 | 0 | tvbuff_t *next_tvb = process_reassembled_data(tvb, offset, pinfo, "Reassembled UDPCP Payload", |
362 | 0 | fh, &udpcp_frag_items, |
363 | 0 | &update_col_info, udpcp_tree); |
364 | 0 | if (next_tvb) { |
365 | | /* Have reassembled data */ |
366 | 0 | proto_item *data_ti = proto_tree_add_item(udpcp_tree, hf_udpcp_payload, next_tvb, 0, -1, ENC_NA); |
367 | 0 | col_append_fstr(pinfo->cinfo, COL_INFO, " Reassembled Data (%u bytes)", data_length); |
368 | | |
369 | | /* Check length is as signalled */ |
370 | 0 | if (data_length != (uint32_t)tvb_reported_length_remaining(next_tvb, 0)) { |
371 | 0 | expert_add_info_format(pinfo, data_ti, &ei_udpcp_payload_wrong_size, "Data length field was %u but %u bytes found (reassembled)", |
372 | 0 | data_length, tvb_reported_length_remaining(next_tvb, 0)); |
373 | 0 | } |
374 | |
|
375 | 0 | if (global_udpcp_decode_payload_as_soap) { |
376 | | /* Send to XML dissector */ |
377 | 0 | call_dissector_only(xml_handle, next_tvb, pinfo, tree, NULL); |
378 | 0 | } |
379 | 0 | } |
380 | | |
381 | | /* Restore fragmented flag */ |
382 | 0 | pinfo->fragmented = save_fragmented; |
383 | 0 | } |
384 | 0 | } |
385 | 0 | } |
386 | 0 | else if (msg_type == ACK_FORMAT) { |
387 | | /* N and S should be set - complain if not */ |
388 | 0 | if (!n || !s) { |
389 | 0 | expert_add_info(pinfo, packet_transfer_options_ti, &ei_udpcp_n_s_ack); |
390 | 0 | } |
391 | |
|
392 | 0 | if (d) { |
393 | | /* Duplicate data detected */ |
394 | 0 | proto_item_append_text(packet_transfer_options_ti, " (duplicate)"); |
395 | 0 | col_append_str(pinfo->cinfo, COL_INFO, " (duplicate)"); |
396 | 0 | } |
397 | |
|
398 | 0 | col_append_fstr(pinfo->cinfo, COL_INFO, " ACK for Msg_ID=%3u", message_id); |
399 | 0 | } |
400 | | |
401 | | /* Look up conversation */ |
402 | 0 | if (!PINFO_FD_VISITED(pinfo)) { |
403 | | /* First pass */ |
404 | 0 | conversation_t *p_conv; |
405 | 0 | udpcp_conversation_t *p_conv_data; |
406 | |
|
407 | 0 | p_conv = find_conversation(pinfo->num, &pinfo->net_dst, &pinfo->net_src, |
408 | 0 | conversation_pt_to_conversation_type(pinfo->ptype), |
409 | 0 | pinfo->destport, pinfo->srcport, |
410 | 0 | 0 /* options */); |
411 | | |
412 | | /* Look up data from conversation */ |
413 | 0 | p_conv_data = (udpcp_conversation_t *)conversation_get_proto_data(p_conv, proto_udpcp); |
414 | | |
415 | | /* Create new data for conversation data if not found */ |
416 | 0 | if (!p_conv_data) { |
417 | 0 | p_conv_data = wmem_new(wmem_file_scope(), udpcp_conversation_t); |
418 | | |
419 | | /* Set initial values */ |
420 | 0 | p_conv_data->first_dest_port = pinfo->destport; |
421 | 0 | copy_address(&p_conv_data->first_dest_address, &pinfo->dst); |
422 | 0 | p_conv_data->next_message_id_first = 0; |
423 | 0 | p_conv_data->next_message_id_second = 0; |
424 | | |
425 | | /* SN and ACK tables */ |
426 | 0 | p_conv_data->sn_table_first = wmem_tree_new(wmem_file_scope()); |
427 | 0 | p_conv_data->ack_table_first = wmem_tree_new(wmem_file_scope()); |
428 | 0 | p_conv_data->sn_table_second = wmem_tree_new(wmem_file_scope()); |
429 | 0 | p_conv_data->ack_table_second = wmem_tree_new(wmem_file_scope()); |
430 | | |
431 | | /* Store in conversation */ |
432 | 0 | conversation_add_proto_data(p_conv, proto_udpcp, p_conv_data); |
433 | 0 | } |
434 | | |
435 | | /* Check which direction this is in */ |
436 | 0 | bool first_dir = (pinfo->destport == p_conv_data->first_dest_port) && |
437 | 0 | addresses_equal(&pinfo->dst, &p_conv_data->first_dest_address); |
438 | | |
439 | | /* Check for expected sequence number */ |
440 | 0 | if (msg_type == DATA_FORMAT) { |
441 | 0 | if (first_dir) { |
442 | 0 | if (message_id != p_conv_data->next_message_id_first) { |
443 | 0 | wmem_tree_insert32(sequence_number_result_table, pinfo->num, GUINT_TO_POINTER(p_conv_data->next_message_id_first)); |
444 | 0 | } |
445 | | /* Only inc when have seen last fragment */ |
446 | 0 | if (fragment_number == fragment_amount-1) { |
447 | 0 | p_conv_data->next_message_id_first = message_id + 1; |
448 | 0 | } |
449 | | |
450 | | /* Store SN entry in table */ |
451 | 0 | wmem_tree_insert32(p_conv_data->sn_table_first, message_id, GUINT_TO_POINTER(pinfo->num)); |
452 | 0 | } |
453 | | /* 2nd Direction */ |
454 | 0 | else { |
455 | 0 | if (message_id != p_conv_data->next_message_id_second) { |
456 | 0 | wmem_tree_insert32(sequence_number_result_table, pinfo->num, GUINT_TO_POINTER(p_conv_data->next_message_id_second)); |
457 | 0 | } |
458 | | /* Only inc when have seen last fragment */ |
459 | 0 | if (fragment_number == fragment_amount-1) { |
460 | 0 | p_conv_data->next_message_id_second = message_id + 1; |
461 | 0 | } |
462 | | |
463 | | /* Store SN entry in table */ |
464 | 0 | wmem_tree_insert32(p_conv_data->sn_table_second, message_id, GUINT_TO_POINTER(pinfo->num)); |
465 | 0 | } |
466 | 0 | } |
467 | |
|
468 | 0 | if (msg_type == ACK_FORMAT) { |
469 | | /* N.B., directions reversed here to apply to data direction */ |
470 | 0 | if (first_dir) { |
471 | 0 | wmem_tree_insert32(p_conv_data->ack_table_first, message_id, GUINT_TO_POINTER(pinfo->num)); |
472 | 0 | } |
473 | 0 | else { |
474 | 0 | wmem_tree_insert32(p_conv_data->ack_table_second, message_id, GUINT_TO_POINTER(pinfo->num)); |
475 | 0 | } |
476 | 0 | } |
477 | 0 | } |
478 | 0 | else { |
479 | | /* Later passes - look up conversation here */ |
480 | 0 | conversation_t *p_conv; |
481 | 0 | udpcp_conversation_t *p_conv_data; |
482 | |
|
483 | 0 | p_conv = find_conversation(pinfo->num, &pinfo->net_dst, &pinfo->net_src, |
484 | 0 | conversation_pt_to_conversation_type(pinfo->ptype), |
485 | 0 | pinfo->destport, pinfo->srcport, |
486 | 0 | 0 /* options */); |
487 | | |
488 | | /* Look up data from conversation */ |
489 | 0 | p_conv_data = (udpcp_conversation_t *)conversation_get_proto_data(p_conv, proto_udpcp); |
490 | 0 | if (!p_conv_data) { |
491 | | /* TODO: error if not found? */ |
492 | 0 | return offset; |
493 | 0 | } |
494 | | |
495 | | /* Check which direction this is in */ |
496 | 0 | bool first_dir = (pinfo->destport == p_conv_data->first_dest_port) && |
497 | 0 | addresses_equal(&pinfo->dst, &p_conv_data->first_dest_address); |
498 | | |
499 | |
|
500 | 0 | if (msg_type == DATA_FORMAT) { |
501 | | /* Check for unexpected sequence number, but not if message_id is still 0 (as it may be repeated) */ |
502 | 0 | if (message_id > 1) { |
503 | 0 | if (wmem_tree_contains32(sequence_number_result_table, pinfo->num)) { |
504 | 0 | uint32_t seqno = GPOINTER_TO_UINT(wmem_tree_lookup32(sequence_number_result_table, pinfo->num)); |
505 | 0 | expert_add_info_format(pinfo, message_id_ti, &ei_udpcp_wrong_sequence_number, "SN %u expected, but found %u instead", |
506 | 0 | seqno, message_id); |
507 | 0 | } |
508 | 0 | } |
509 | | |
510 | | /* Look for ACK for this data PDU, link or expert info */ |
511 | 0 | wmem_tree_t *ack_table = (first_dir) ? p_conv_data->ack_table_second : p_conv_data->ack_table_first; |
512 | 0 | if (wmem_tree_contains32(ack_table, message_id)) { |
513 | 0 | uint32_t ack = GPOINTER_TO_UINT(wmem_tree_lookup32(ack_table, message_id)); |
514 | 0 | proto_tree_add_uint(udpcp_tree, hf_udpcp_ack_frame, tvb, 0, 0, ack); |
515 | 0 | } |
516 | 0 | else { |
517 | 0 | expert_add_info_format(pinfo, message_id_ti, &ei_udpcp_no_ack, "No ACK seen for this data frame (message_id=%u", |
518 | 0 | message_id); |
519 | |
|
520 | 0 | } |
521 | |
|
522 | 0 | } |
523 | 0 | else if (msg_type == ACK_FORMAT) { |
524 | | /* Look up corresponding Data frame, link or expert info */ |
525 | 0 | wmem_tree_t *sn_table = (first_dir) ? p_conv_data->sn_table_second : p_conv_data->sn_table_first; |
526 | 0 | if (wmem_tree_contains32(sn_table, message_id)) { |
527 | 0 | uint32_t sn_frame = GPOINTER_TO_UINT(wmem_tree_lookup32(sn_table, message_id)); |
528 | 0 | proto_tree_add_uint(udpcp_tree, hf_udpcp_sn_frame, tvb, 0, 0, sn_frame); |
529 | 0 | } |
530 | 0 | else { |
531 | 0 | expert_add_info_format(pinfo, message_id_ti, &ei_udpcp_no_sn_frame, "No SN frame seen corresponding to this ACK (message_id=%u", |
532 | 0 | message_id); |
533 | 0 | } |
534 | 0 | } |
535 | 0 | } |
536 | | |
537 | 0 | return offset; |
538 | 0 | } |
539 | | |
540 | | void |
541 | | proto_register_udpcp(void) |
542 | 15 | { |
543 | 15 | static hf_register_info hf[] = { |
544 | 15 | { &hf_udpcp_checksum, |
545 | 15 | { "Checksum", "udpcp.checksum", FT_UINT32, BASE_HEX, |
546 | 15 | NULL, 0x0, "Adler32 checksum", HFILL }}, |
547 | 15 | { &hf_udpcp_msg_type, |
548 | 15 | { "Msg Type", "udpcp.msg-type", FT_UINT8, BASE_HEX, |
549 | 15 | VALS(msg_type_vals), 0xc0, NULL, HFILL }}, |
550 | 15 | { &hf_udpcp_version, |
551 | 15 | { "Version", "udpcp.version", FT_UINT8, BASE_HEX, |
552 | 15 | NULL, 0x38, NULL, HFILL }}, |
553 | | |
554 | 15 | { &hf_udpcp_packet_transfer_options, |
555 | 15 | { "Packet Transport Options", "udpcp.pto", FT_STRING, BASE_NONE, |
556 | 15 | NULL, 0x0, NULL, HFILL }}, |
557 | 15 | { &hf_udpcp_n, |
558 | 15 | { "N", "udpcp.n", FT_UINT8, BASE_HEX, |
559 | 15 | NULL, 0x04, "Along with S bit, indicates whether acknowledgements should be sent", HFILL }}, |
560 | 15 | { &hf_udpcp_c, |
561 | 15 | { "C", "udpcp.c", FT_UINT8, BASE_HEX, |
562 | 15 | NULL, 0x02, "When set, the checksum should be valid", HFILL }}, |
563 | 15 | { &hf_udpcp_s, |
564 | 15 | { "S", "udpcp.s", FT_UINT8, BASE_HEX, |
565 | 15 | NULL, 0x01, "Along with N bit, indicates whether acknowledgements should be sent", HFILL }}, |
566 | 15 | { &hf_udpcp_d, |
567 | 15 | { "D", "udpcp.d", FT_UINT8, BASE_HEX, |
568 | 15 | NULL, 0x80, "For ACK, indicates duplicate ACK", HFILL }}, |
569 | 15 | { &hf_udpcp_reserved, |
570 | 15 | { "Reserved", "udpcp.reserved", FT_UINT8, BASE_HEX, |
571 | 15 | NULL, 0x7f, "Shall be set to 0", HFILL }}, |
572 | | |
573 | 15 | { &hf_udpcp_fragment_amount, |
574 | 15 | { "Fragment Amount", "udpcp.fragment-amount", FT_UINT8, BASE_DEC, |
575 | 15 | NULL, 0x0, "Total number of fragments of a message", HFILL }}, |
576 | 15 | { &hf_udpcp_fragment_number, |
577 | 15 | { "Fragment Number", "udpcp.fragment-number", FT_UINT8, BASE_DEC, |
578 | 15 | NULL, 0x0, "Fragment number of current packet within msg. Starts at 0", HFILL }}, |
579 | | |
580 | 15 | { &hf_udpcp_message_id, |
581 | 15 | { "Message ID", "udpcp.message-id", FT_UINT16, BASE_DEC, |
582 | 15 | NULL, 0x0, NULL, HFILL }}, |
583 | 15 | { &hf_udpcp_message_data_length, |
584 | 15 | { "Message Data Length", "udpcp.message-data-length", FT_UINT16, BASE_DEC, |
585 | 15 | NULL, 0x0, NULL, HFILL }}, |
586 | | |
587 | 15 | { &hf_udpcp_payload, |
588 | 15 | { "Payload", "udpcp.payload", FT_BYTES, BASE_SHOW_ASCII_PRINTABLE, |
589 | 15 | NULL, 0x0, "Complete or reassembled payload", HFILL }}, |
590 | | |
591 | | /* Reassembly */ |
592 | 15 | { &hf_udpcp_fragment, |
593 | 15 | { "Fragment", "udpcp.fragment", FT_FRAMENUM, BASE_NONE, |
594 | 15 | NULL, 0x0, NULL, HFILL }}, |
595 | 15 | { &hf_udpcp_fragments, |
596 | 15 | { "Fragments", "udpcp.fragments", FT_BYTES, BASE_NONE, |
597 | 15 | NULL, 0x0, NULL, HFILL }}, |
598 | 15 | { &hf_udpcp_fragment_overlap, |
599 | 15 | { "Fragment overlap", "udpcp.fragment.overlap", FT_BOOLEAN, BASE_NONE, |
600 | 15 | NULL, 0x0, "Fragment overlaps with other fragments", HFILL }}, |
601 | 15 | { &hf_udpcp_fragment_overlap_conflict, |
602 | 15 | { "Conflicting data in fragment overlap", "udpcp.fragment.overlap.conflict", |
603 | 15 | FT_BOOLEAN, BASE_NONE, NULL, 0x0, |
604 | 15 | "Overlapping fragments contained conflicting data", HFILL }}, |
605 | 15 | { &hf_udpcp_fragment_multiple_tails, |
606 | 15 | { "Multiple tail fragments found", "udpcp.fragment.multipletails", |
607 | 15 | FT_BOOLEAN, BASE_NONE, NULL, 0x0, |
608 | 15 | "Several tails were found when defragmenting the packet", HFILL }}, |
609 | 15 | { &hf_udpcp_fragment_too_long_fragment, |
610 | 15 | { "Fragment too long", "udpcp.fragment.toolongfragment", |
611 | 15 | FT_BOOLEAN, BASE_NONE, NULL, 0x0, |
612 | 15 | "Fragment contained data past end of packet", HFILL }}, |
613 | 15 | { &hf_udpcp_fragment_error, |
614 | 15 | { "Defragmentation error", "udpcp.fragment.error", FT_FRAMENUM, BASE_NONE, |
615 | 15 | NULL, 0x0, "Defragmentation error due to illegal fragments", HFILL }}, |
616 | 15 | { &hf_udpcp_fragment_count, |
617 | 15 | { "Fragment count", "udpcp.fragment.count", FT_UINT32, BASE_DEC, |
618 | 15 | NULL, 0x0, NULL, HFILL }}, |
619 | 15 | { &hf_udpcp_reassembled_in, |
620 | 15 | { "Reassembled payload in frame", "udpcp.reassembled_in", FT_FRAMENUM, BASE_NONE, |
621 | 15 | NULL, 0x0, "This payload packet is reassembled in this frame", HFILL }}, |
622 | 15 | { &hf_udpcp_reassembled_length, |
623 | 15 | { "Reassembled payload length", "udpcp.reassembled.length", FT_UINT32, BASE_DEC, |
624 | 15 | NULL, 0x0, "The total length of the reassembled payload", HFILL }}, |
625 | 15 | { &hf_udpcp_reassembled_data, |
626 | 15 | { "Reassembled data", "udpcp.reassembled.data", FT_BYTES, BASE_NONE, |
627 | 15 | NULL, 0x0, "The reassembled payload", HFILL }}, |
628 | | |
629 | 15 | { &hf_udpcp_ack_frame, |
630 | 15 | { "Ack Frame", "udpcp.ack-frame", FT_FRAMENUM, BASE_NONE, |
631 | 15 | NULL, 0x0, "Frame that ACKs this data", HFILL }}, |
632 | 15 | { &hf_udpcp_sn_frame, |
633 | 15 | { "SN Frame", "udpcp.sn-frame", FT_FRAMENUM, BASE_NONE, |
634 | 15 | NULL, 0x0, "Data frame ACKd by this one", HFILL }}, |
635 | 15 | }; |
636 | | |
637 | 15 | static int *ett[] = { |
638 | 15 | &ett_udpcp, |
639 | 15 | &ett_udpcp_packet_transfer_options, |
640 | 15 | &ett_udpcp_fragments, |
641 | 15 | &ett_udpcp_fragment |
642 | 15 | }; |
643 | | |
644 | 15 | static ei_register_info ei[] = { |
645 | 15 | { &ei_udpcp_checksum_should_be_zero, { "udpcp.checksum-not-zero", PI_CHECKSUM, PI_WARN, "Checksum should be zero if !C.", EXPFILL }}, |
646 | 15 | { &ei_udpcp_d_not_zero_for_data, { "udpcp.d-not-zero-data", PI_SEQUENCE, PI_ERROR, "D should be zero for data frames", EXPFILL }}, |
647 | 15 | { &ei_udpcp_reserved_not_zero, { "udpcp.reserved-not-zero", PI_MALFORMED, PI_WARN, "Reserved bits not zero", EXPFILL }}, |
648 | 15 | { &ei_udpcp_n_s_ack, { "udpcp.n-s-set-ack", PI_MALFORMED, PI_ERROR, "N or S set for ACK frame", EXPFILL }}, |
649 | 15 | { &ei_udpcp_payload_wrong_size, { "udpcp.payload-wrong-size", PI_MALFORMED, PI_ERROR, "Payload seen does not match size field", EXPFILL }}, |
650 | 15 | { &ei_udpcp_wrong_sequence_number, { "udpcp.sequence-number-wrong", PI_SEQUENCE, PI_WARN, "Unexpected sequence number", EXPFILL }}, |
651 | 15 | { &ei_udpcp_no_ack, { "udpcp.no-ack", PI_SEQUENCE, PI_WARN, "No ACK seen for data frame", EXPFILL }}, |
652 | 15 | { &ei_udpcp_no_sn_frame, { "udpcp.no-sn-frame", PI_SEQUENCE, PI_WARN, "No SN frame seen for ACK", EXPFILL }}, |
653 | 15 | }; |
654 | | |
655 | 15 | module_t *udpcp_module; |
656 | 15 | expert_module_t *expert_udpcp; |
657 | | |
658 | 15 | proto_udpcp = proto_register_protocol("UDPCP", "UDPCP", "udpcp"); |
659 | 15 | proto_register_field_array(proto_udpcp, hf, array_length(hf)); |
660 | 15 | proto_register_subtree_array(ett, array_length(ett)); |
661 | 15 | expert_udpcp = expert_register_protocol(proto_udpcp); |
662 | 15 | expert_register_field_array(expert_udpcp, ei, array_length(ei)); |
663 | | |
664 | 15 | udpcp_handle = register_dissector("udpcp", dissect_udpcp, proto_udpcp); |
665 | | |
666 | | /* Register reassembly table. */ |
667 | 15 | reassembly_table_register(&udpcp_reassembly_table, |
668 | 15 | &udpcp_reassembly_table_functions); |
669 | | |
670 | | /* Preferences */ |
671 | 15 | udpcp_module = prefs_register_protocol(proto_udpcp, NULL); |
672 | | |
673 | | /* Payload reassembly */ |
674 | 15 | prefs_register_bool_preference(udpcp_module, "attempt_reassembly", |
675 | 15 | "Reassemble payload", |
676 | 15 | "", |
677 | 15 | &global_udpcp_reassemble); |
678 | | |
679 | | /* Whether to try XML dissector on payload. |
680 | | * TODO: are there any other payload types we might see? */ |
681 | 15 | prefs_register_bool_preference(udpcp_module, "attempt_xml_decode", |
682 | 15 | "Call XML dissector for payload", |
683 | 15 | "", |
684 | 15 | &global_udpcp_decode_payload_as_soap); |
685 | | |
686 | 15 | sequence_number_result_table = wmem_tree_new_autoreset(wmem_epan_scope(), wmem_file_scope()); |
687 | 15 | } |
688 | | |
689 | | static void |
690 | | apply_udpcp_prefs(void) |
691 | 15 | { |
692 | 15 | global_udpcp_port_range = prefs_get_range_value("udpcp", "udp.port"); |
693 | 15 | } |
694 | | |
695 | | void |
696 | | proto_reg_handoff_udpcp(void) |
697 | 15 | { |
698 | 15 | dissector_add_uint_range_with_preference("udp.port", "", udpcp_handle); |
699 | 15 | apply_udpcp_prefs(); |
700 | | |
701 | 15 | xml_handle = find_dissector("xml"); |
702 | 15 | } |
703 | | |
704 | | /* |
705 | | * Editor modelines - https://www.wireshark.org/tools/modelines.html |
706 | | * |
707 | | * Local variables: |
708 | | * c-basic-offset: 4 |
709 | | * tab-width: 8 |
710 | | * indent-tabs-mode: nil |
711 | | * End: |
712 | | * |
713 | | * vi: set shiftwidth=4 tabstop=8 expandtab: |
714 | | * :indentSize=4:tabSize=8:noTabs=true: |
715 | | */ |