/src/wireshark/epan/dissectors/packet-hcrt.c
Line | Count | Source |
1 | | /* packet-hcrt.c |
2 | | * |
3 | | * Routines for Hotline Command-Response Transaction (HCrt) |
4 | | * Protocol specifications (draft) are available here |
5 | | * https://github.com/ShepardSiegel/hotline/tree/master/doc |
6 | | * |
7 | | * Copyright 2013 Dario Lombardo (lomato@gmail.com) |
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 | | #include <epan/packet.h> |
18 | | #include <epan/prefs.h> |
19 | | #include <epan/expert.h> |
20 | | #include <epan/tfs.h> |
21 | | |
22 | | static int proto_hcrt; |
23 | | |
24 | 15 | #define HCRT_UDP_PORTS_DEFAULT "47000" |
25 | | |
26 | | static unsigned ethertype_pref = 0xf052; |
27 | | |
28 | | static int hf_hcrt_header; |
29 | | static int hf_hcrt_message_tag; |
30 | | static int hf_hcrt_message_type; |
31 | | static int hf_hcrt_am; |
32 | | static int hf_hcrt_do; |
33 | | static int hf_hcrt_1st_dword_enable; |
34 | | static int hf_hcrt_last_dword_enable; |
35 | | static int hf_hcrt_resp_code; |
36 | | static int hf_hcrt_adl; |
37 | | static int hf_hcrt_last; |
38 | | static int hf_hcrt_body; |
39 | | static int hf_hcrt_addr_32; |
40 | | static int hf_hcrt_addr_64; |
41 | | static int hf_hcrt_data_32; |
42 | | static int hf_hcrt_data_64; |
43 | | static int hf_hcrt_command_nop; |
44 | | |
45 | | static int ett_hcrt; |
46 | | static int ett_hcrt_msg; |
47 | | static int ett_hcrt_hdr; |
48 | | static int ett_hcrt_body; |
49 | | |
50 | | static expert_field ei_hcrt_error; |
51 | | |
52 | | void proto_reg_handoff_hcrt(void); |
53 | | void proto_register_hcrt(void); |
54 | | |
55 | | static dissector_handle_t hcrt_handle; |
56 | | |
57 | 455 | #define HCRT_HDR_LEN 4 |
58 | | |
59 | 580 | #define HCRT_NOP 0x0 |
60 | 63 | #define HCRT_WRITE 0x1 |
61 | 214 | #define HCRT_READ 0x2 |
62 | 966 | #define HCRT_RESPONSE 0x3 |
63 | | |
64 | 137 | #define ADDR_MODE_32 1 |
65 | | #define ADDR_MODE_64 2 |
66 | | |
67 | | /* Message types */ |
68 | | static const value_string hcrt_message_types[] = { |
69 | | {0x00, "NOP"}, |
70 | | {0x01, "Write"}, |
71 | | {0x02, "Read"}, |
72 | | {0x03, "Response"}, |
73 | | {0, NULL} |
74 | | }; |
75 | | |
76 | | /* Addressing modes */ |
77 | | static const value_string hcrt_ams[] = { |
78 | | {0x0, "32 bit"}, |
79 | | {0x1, "64 bit"}, |
80 | | {0, NULL} |
81 | | }; |
82 | | |
83 | | /* Discovery operations */ |
84 | | static const true_false_string hcrt_dos = { |
85 | | "DO", |
86 | | "not DO", |
87 | | }; |
88 | | |
89 | | static const value_string dword_enable_vals[] = { |
90 | | {0xF, "4B"}, |
91 | | {0xC, "2B (MS)"}, |
92 | | {0x3, "2B (LS)"}, |
93 | | {0x8, "1B (B3)"}, |
94 | | {0x4, "1B (B2)"}, |
95 | | {0x2, "1B (B1)"}, |
96 | | {0x1, "1B (B0)"}, |
97 | | {0, NULL} |
98 | | }; |
99 | | |
100 | | static const value_string response_codes[] = { |
101 | | {0x0, "OK"}, |
102 | | {0x1, "Timeout"}, |
103 | | {0x2, "Error"}, |
104 | | {0x3, "Reserved"}, |
105 | | {0x4, "Reserved"}, |
106 | | {0x5, "Reserved"}, |
107 | | {0x6, "Reserved"}, |
108 | | {0x7, "Reserved"}, |
109 | | {0x8, "Reserved"}, |
110 | | {0x9, "Reserved"}, |
111 | | {0xa, "Reserved"}, |
112 | | {0xb, "Reserved"}, |
113 | | {0xc, "Reserved"}, |
114 | | {0xd, "Reserved"}, |
115 | | {0xe, "Reserved"}, |
116 | | {0xf, "Reserved"}, |
117 | | {0, NULL} |
118 | | }; |
119 | | |
120 | | |
121 | | static void dissect_hcrt_body(tvbuff_t* tvb, proto_tree* tree , unsigned* offset, |
122 | | int type, int addr_mode, int adl, int body_len) |
123 | 455 | { |
124 | 455 | proto_item* ti_body; |
125 | 455 | proto_tree* hcrt_body_tree; |
126 | 455 | int i; |
127 | | |
128 | 455 | ti_body = proto_tree_add_item(tree, hf_hcrt_body, tvb, *offset, body_len, ENC_NA); |
129 | 455 | hcrt_body_tree = proto_item_add_subtree(ti_body, ett_hcrt_body); |
130 | | |
131 | 455 | switch (type) { |
132 | 290 | case HCRT_NOP: |
133 | 290 | proto_tree_add_item(hcrt_body_tree, hf_hcrt_command_nop, tvb, *offset, |
134 | 290 | body_len, ENC_NA); |
135 | 290 | break; |
136 | 31 | case HCRT_WRITE: |
137 | 31 | if (addr_mode == ADDR_MODE_32) { |
138 | | /* Address (32) */ |
139 | 16 | proto_tree_add_item(hcrt_body_tree, hf_hcrt_addr_32, tvb, *offset, |
140 | 16 | 4, ENC_LITTLE_ENDIAN); |
141 | | |
142 | | /* Data */ |
143 | 156 | for (i = 1; i <= adl; i++) { |
144 | 140 | proto_tree_add_item(hcrt_body_tree, hf_hcrt_data_32, tvb, |
145 | 140 | *offset + i * 4, 4, ENC_LITTLE_ENDIAN); |
146 | 140 | } |
147 | 16 | } else { |
148 | | /* Address (64) */ |
149 | 15 | proto_tree_add_item(hcrt_body_tree, hf_hcrt_addr_64, tvb, *offset, |
150 | 15 | 8, ENC_LITTLE_ENDIAN); |
151 | | |
152 | | /* Data */ |
153 | 98 | for (i = 1; i <= adl; i++) |
154 | 83 | proto_tree_add_item(hcrt_body_tree, hf_hcrt_data_64, tvb, |
155 | 83 | *offset + i * 8, 8, ENC_LITTLE_ENDIAN); |
156 | 15 | } |
157 | 31 | break; |
158 | 106 | case HCRT_READ: |
159 | 106 | if (addr_mode == ADDR_MODE_32) { |
160 | | /* Address (32) */ |
161 | 67 | proto_tree_add_item(hcrt_body_tree, hf_hcrt_addr_32, tvb, *offset, 4, |
162 | 67 | ENC_LITTLE_ENDIAN); |
163 | 67 | } else { |
164 | | /* Address (64) */ |
165 | 39 | proto_tree_add_item(hcrt_body_tree, hf_hcrt_addr_64, tvb, *offset, 8, |
166 | 39 | ENC_LITTLE_ENDIAN); |
167 | 39 | } |
168 | 106 | break; |
169 | 28 | case HCRT_RESPONSE: |
170 | 28 | if (body_len > 0) { |
171 | 7 | proto_tree_add_item(hcrt_body_tree, hf_hcrt_command_nop, tvb, *offset, |
172 | 7 | body_len, ENC_NA); |
173 | 7 | } |
174 | 28 | break; |
175 | 0 | default: |
176 | 0 | DISSECTOR_ASSERT_NOT_REACHED(); |
177 | 0 | break; |
178 | 455 | } |
179 | | |
180 | 426 | (*offset) += body_len; |
181 | 426 | } |
182 | | |
183 | | /* Returns true if this is the last message */ |
184 | | static bool dissect_hcrt_header(tvbuff_t* tvb, packet_info* pinfo, proto_tree* tree, |
185 | | unsigned* offset, uint8_t b0_first, uint8_t b0_current) |
186 | 455 | { |
187 | 455 | proto_item* ti_hdr; |
188 | 455 | proto_tree* hcrt_hdr_tree; |
189 | 455 | bool last; |
190 | 455 | uint8_t type; |
191 | | |
192 | 455 | ti_hdr = proto_tree_add_item(tree, hf_hcrt_header, tvb, *offset, 4, ENC_NA); |
193 | 455 | hcrt_hdr_tree = proto_item_add_subtree(ti_hdr, ett_hcrt_hdr); |
194 | | |
195 | 455 | if (b0_first != b0_current) { |
196 | 327 | expert_add_info_format(pinfo, hcrt_hdr_tree, &ei_hcrt_error, |
197 | 327 | "Invalid Byte 0 in Header. Must be equal in all HCrt messages. " |
198 | 327 | "Expected: %.2X, got: %.2X", b0_first, b0_current); |
199 | 327 | } |
200 | | |
201 | 455 | type = (b0_current & 0x30) >> 4; |
202 | | |
203 | | /* == Byte 0 == */ |
204 | | /* TAG */ |
205 | 455 | proto_tree_add_item(hcrt_hdr_tree, hf_hcrt_message_tag, tvb, |
206 | 455 | *offset, 1, ENC_NA); |
207 | | /* Message Type */ |
208 | 455 | proto_tree_add_item(hcrt_hdr_tree, hf_hcrt_message_type, tvb, |
209 | 455 | *offset, 1, ENC_NA); |
210 | | /* Addressing Mode */ |
211 | 455 | proto_tree_add_item(hcrt_hdr_tree, hf_hcrt_am, tvb, |
212 | 455 | *offset, 1, ENC_NA); |
213 | | /* Discovery Operation */ |
214 | 455 | proto_tree_add_item(hcrt_hdr_tree, hf_hcrt_do, tvb, |
215 | 455 | *offset, 1, ENC_NA); |
216 | 455 | (*offset)++; |
217 | | |
218 | | /* == Byte 1 == */ |
219 | 455 | if (type != HCRT_RESPONSE) { |
220 | | /* 1st DWORD enable */ |
221 | 427 | proto_tree_add_item(hcrt_hdr_tree, hf_hcrt_1st_dword_enable, tvb, |
222 | 427 | *offset, 1, ENC_NA); |
223 | 427 | } else { |
224 | | /* Response Code */ |
225 | 28 | proto_tree_add_item(hcrt_hdr_tree, hf_hcrt_resp_code, tvb, |
226 | 28 | *offset, 1, ENC_NA); |
227 | 28 | } |
228 | | |
229 | 455 | if (type != HCRT_RESPONSE) { |
230 | | /* Last DWORD enable */ |
231 | 427 | proto_tree_add_item(hcrt_hdr_tree, hf_hcrt_last_dword_enable, tvb, |
232 | 427 | *offset, 1, ENC_NA); |
233 | 427 | } |
234 | 455 | (*offset)++; |
235 | | |
236 | | /* == Byte 2 & 3 == */ |
237 | | /* ADL */ |
238 | 455 | proto_tree_add_item(hcrt_hdr_tree, hf_hcrt_adl, tvb, *offset, 2, ENC_LITTLE_ENDIAN); |
239 | | /* Last */ |
240 | 455 | proto_tree_add_item(hcrt_hdr_tree, hf_hcrt_last, tvb, *offset, 2, ENC_LITTLE_ENDIAN); |
241 | | |
242 | | /* last */ |
243 | 455 | last = (tvb_get_letohs(tvb, *offset) & 0x8000) != 0; |
244 | 455 | (*offset) += 2; |
245 | 455 | return last; |
246 | 455 | } |
247 | | |
248 | | /* Return true if this is the last message */ |
249 | | static bool dissect_hcrt_message(tvbuff_t* tvb, packet_info* pinfo, proto_tree* tree, |
250 | | unsigned* offset, uint8_t b0_first, int i) |
251 | 460 | { |
252 | 460 | bool last; |
253 | 460 | unsigned adl; |
254 | 460 | unsigned addr_mode; |
255 | 460 | unsigned body_len; |
256 | 460 | proto_tree* hcrt_msg_tree; |
257 | 460 | uint8_t b0_current; |
258 | 460 | int type; |
259 | | |
260 | | /* Save byte 0 of current packet */ |
261 | 460 | b0_current = tvb_get_uint8(tvb, *offset); |
262 | | |
263 | | /* Get details from header */ |
264 | 460 | adl = tvb_get_letohs(tvb, *offset + 2) & 0x0FFF; |
265 | 460 | addr_mode = (1 + ((b0_current & 0x40) >> 6)); |
266 | 460 | type = (b0_current & 0x30) >> 4; |
267 | | |
268 | 460 | switch (type) { |
269 | 290 | case HCRT_NOP: |
270 | 290 | body_len = 4 * addr_mode * adl; |
271 | 290 | break; |
272 | 31 | case HCRT_WRITE: |
273 | 31 | body_len = 4 * addr_mode * (adl + 1); |
274 | 31 | break; |
275 | 106 | case HCRT_READ: |
276 | 106 | body_len = 4 * addr_mode; |
277 | 106 | break; |
278 | 28 | case HCRT_RESPONSE: |
279 | 28 | body_len = 4 * addr_mode * adl; |
280 | 28 | break; |
281 | 0 | default: |
282 | 0 | DISSECTOR_ASSERT_NOT_REACHED(); |
283 | 0 | break; |
284 | 460 | } |
285 | | |
286 | 455 | hcrt_msg_tree = proto_tree_add_subtree_format(tree, tvb, *offset, |
287 | 455 | HCRT_HDR_LEN + body_len, ett_hcrt_msg, NULL, "Message %d", i); |
288 | | |
289 | 455 | last = dissect_hcrt_header(tvb, pinfo, hcrt_msg_tree, offset, b0_first, b0_current); |
290 | 455 | dissect_hcrt_body(tvb, hcrt_msg_tree, offset, type, addr_mode, adl, body_len); |
291 | | |
292 | 455 | return last; |
293 | 460 | } |
294 | | |
295 | | static int dissect_hcrt(tvbuff_t* tvb, packet_info* pinfo, proto_tree* tree, void* data _U_) |
296 | 37 | { |
297 | 37 | uint8_t type; |
298 | 37 | proto_item* ti; |
299 | 37 | proto_tree* hcrt_tree; |
300 | 37 | unsigned offset; |
301 | 37 | int i = 1; |
302 | 37 | uint8_t b0_first; |
303 | 37 | uint8_t tag; |
304 | 37 | unsigned adl; |
305 | | |
306 | 37 | col_set_str(pinfo->cinfo, COL_PROTOCOL, "HCrt"); |
307 | 37 | col_clear(pinfo->cinfo, COL_INFO); |
308 | | |
309 | | /* Save byte 0 of first message. Will be checked against byte 0 of other messages */ |
310 | 37 | b0_first = tvb_get_uint8(tvb, 0); |
311 | | |
312 | 37 | tag = b0_first & 0x0F; |
313 | 37 | type = (b0_first & 0x30) >> 4; |
314 | 37 | adl = tvb_get_letohs(tvb, 2) & 0x0FFF; |
315 | | |
316 | 37 | col_add_fstr(pinfo->cinfo, COL_INFO, "Type: %s, Tag: 0x%X, ADL: %u", |
317 | 37 | val_to_str(pinfo->pool, type, hcrt_message_types, "Unknown (0x%02x)"), tag, adl); |
318 | | |
319 | 37 | if (adl == 1) { |
320 | 1 | if (type == HCRT_READ || type == HCRT_WRITE) { |
321 | 1 | col_append_fstr(pinfo->cinfo, COL_INFO, ", Address: 0x%.8X", tvb_get_letohl(tvb, 4)); |
322 | 1 | } |
323 | 1 | if (type == HCRT_WRITE) { |
324 | 0 | col_append_fstr(pinfo->cinfo, COL_INFO, ", Data: 0x%.8X", tvb_get_letohl(tvb, 8)); |
325 | 0 | } |
326 | 1 | } |
327 | | |
328 | 37 | offset = 0; |
329 | 37 | ti = proto_tree_add_item(tree, proto_hcrt, tvb, 0, -1, ENC_NA); |
330 | 37 | hcrt_tree = proto_item_add_subtree(ti, ett_hcrt); |
331 | | |
332 | 460 | while (!dissect_hcrt_message(tvb, pinfo, hcrt_tree, &offset, b0_first, i)) { |
333 | 423 | i++; |
334 | 423 | } |
335 | 37 | return tvb_captured_length(tvb); |
336 | 37 | } |
337 | | |
338 | | void proto_register_hcrt(void) |
339 | 15 | { |
340 | 15 | expert_module_t* expert_hcrt; |
341 | 15 | module_t* hcrt_module; |
342 | | |
343 | 15 | static hf_register_info hf[] = { |
344 | 15 | { &hf_hcrt_header, |
345 | 15 | { "Header", "hcrt.hdr", |
346 | 15 | FT_NONE, BASE_NONE, |
347 | 15 | NULL, 0x00, |
348 | 15 | NULL, HFILL } |
349 | 15 | }, |
350 | 15 | { &hf_hcrt_message_tag, |
351 | 15 | { "Tag", "hcrt.tag", |
352 | 15 | FT_UINT8, BASE_HEX, |
353 | 15 | NULL, 0x0F, |
354 | 15 | NULL, HFILL } |
355 | 15 | }, |
356 | 15 | { &hf_hcrt_message_type, |
357 | 15 | { "Type", "hcrt.type", |
358 | 15 | FT_UINT8, BASE_DEC, |
359 | 15 | VALS(hcrt_message_types), 0x30, |
360 | 15 | NULL, HFILL } |
361 | 15 | }, |
362 | 15 | { &hf_hcrt_am, |
363 | 15 | { "Addressing Mode", "hcrt.am", |
364 | 15 | FT_UINT8, BASE_DEC, |
365 | 15 | VALS(hcrt_ams), 0x40, |
366 | 15 | NULL, HFILL } |
367 | 15 | }, |
368 | 15 | { &hf_hcrt_do, |
369 | 15 | { "Discovery Operation", "hcrt.do", |
370 | 15 | FT_BOOLEAN, 8, |
371 | 15 | TFS(&hcrt_dos), 0x80, |
372 | 15 | NULL, HFILL } |
373 | 15 | }, |
374 | 15 | { &hf_hcrt_1st_dword_enable, |
375 | 15 | { "1st DWORD enable", "hcrt.first_dword_enable", |
376 | 15 | FT_UINT8, BASE_HEX, |
377 | 15 | VALS(dword_enable_vals), 0xF0, |
378 | 15 | NULL, HFILL } |
379 | 15 | }, |
380 | 15 | { &hf_hcrt_last_dword_enable, |
381 | 15 | { "Last DWORD enable", "hcrt.last_dword_enable", |
382 | 15 | FT_UINT8, BASE_HEX, |
383 | 15 | VALS(dword_enable_vals), 0x0F, |
384 | 15 | NULL, HFILL } |
385 | 15 | }, |
386 | 15 | { &hf_hcrt_resp_code, |
387 | 15 | { "Response code", "hcrt.response_code", |
388 | 15 | FT_UINT8, BASE_HEX, |
389 | 15 | VALS(response_codes), 0xF0, |
390 | 15 | NULL, HFILL } |
391 | 15 | }, |
392 | 15 | { &hf_hcrt_adl, |
393 | 15 | { "ADL", "hcrt.adl", |
394 | 15 | FT_UINT16, BASE_DEC, |
395 | 15 | NULL, 0x0FFF, |
396 | 15 | NULL, HFILL } |
397 | 15 | }, |
398 | 15 | { &hf_hcrt_last, |
399 | 15 | { "Last message", "hcrt.last", |
400 | 15 | FT_BOOLEAN, 16, |
401 | 15 | NULL, 0x8000, |
402 | 15 | NULL, HFILL } |
403 | 15 | }, |
404 | 15 | { &hf_hcrt_body, |
405 | 15 | { "Body", "hcrt.body", |
406 | 15 | FT_NONE, BASE_NONE, |
407 | 15 | NULL, 0x00, |
408 | 15 | NULL, HFILL } |
409 | 15 | }, |
410 | 15 | { &hf_hcrt_addr_32, |
411 | 15 | { "Address", "hcrt.address32", |
412 | 15 | FT_UINT32, BASE_HEX, |
413 | 15 | NULL, 0x0, |
414 | 15 | NULL, HFILL } |
415 | 15 | }, |
416 | 15 | { &hf_hcrt_addr_64, |
417 | 15 | { "Address", "hcrt.address64", |
418 | 15 | FT_UINT64, BASE_HEX, |
419 | 15 | NULL, 0x0, |
420 | 15 | NULL, HFILL } |
421 | 15 | }, |
422 | 15 | { &hf_hcrt_data_32, |
423 | 15 | { "Data", "hcrt.data32", |
424 | 15 | FT_UINT32, BASE_HEX, |
425 | 15 | NULL, 0x0, |
426 | 15 | NULL, HFILL } |
427 | 15 | }, |
428 | 15 | { &hf_hcrt_data_64, |
429 | 15 | { "Data", "hcrt.data64", |
430 | 15 | FT_UINT64, BASE_HEX, |
431 | 15 | NULL, 0x0, |
432 | 15 | NULL, HFILL } |
433 | 15 | }, |
434 | 15 | { &hf_hcrt_command_nop, |
435 | 15 | { "Command", "hcrt.command_nop", |
436 | 15 | FT_BYTES, BASE_NONE, |
437 | 15 | NULL, 0x0, |
438 | 15 | NULL, HFILL } |
439 | 15 | } |
440 | 15 | }; |
441 | | |
442 | 15 | static ei_register_info ei[] = { |
443 | 15 | { &ei_hcrt_error, { "hcrt.error", PI_MALFORMED, PI_ERROR, "Unusual error code", EXPFILL }} |
444 | 15 | }; |
445 | | |
446 | | /* Setup protocol subtree array */ |
447 | 15 | static int* ett[] = { |
448 | 15 | &ett_hcrt, |
449 | 15 | &ett_hcrt_msg, |
450 | 15 | &ett_hcrt_hdr, |
451 | 15 | &ett_hcrt_body, |
452 | 15 | }; |
453 | | |
454 | 15 | proto_hcrt = proto_register_protocol ("Hotline Command-Response Transaction protocol", "HCrt", "hcrt"); |
455 | | |
456 | 15 | proto_register_field_array(proto_hcrt, hf, array_length(hf)); |
457 | 15 | proto_register_subtree_array(ett, array_length(ett)); |
458 | 15 | expert_hcrt = expert_register_protocol(proto_hcrt); |
459 | 15 | expert_register_field_array(expert_hcrt, ei, array_length(ei)); |
460 | | |
461 | 15 | hcrt_module = prefs_register_protocol(proto_hcrt, proto_reg_handoff_hcrt); |
462 | 15 | prefs_register_uint_preference(hcrt_module, |
463 | 15 | "dissector_ethertype", |
464 | 15 | "Ethernet type", |
465 | 15 | "The ethernet type used for L2 communications", |
466 | 15 | 10, ðertype_pref); |
467 | | |
468 | 15 | hcrt_handle = register_dissector("hcrt", dissect_hcrt, proto_hcrt); |
469 | 15 | } |
470 | | |
471 | | void proto_reg_handoff_hcrt(void) |
472 | 15 | { |
473 | 15 | static bool hcrt_prefs_initialized = false; |
474 | 15 | static int hcrt_ethertype; |
475 | | |
476 | 15 | if (!hcrt_prefs_initialized) { |
477 | | /* Also register as a dissector that can be selected by a TCP port number via |
478 | | "decode as" */ |
479 | 15 | dissector_add_for_decode_as_with_preference("tcp.port", hcrt_handle); |
480 | 15 | dissector_add_uint_range_with_preference("udp.port", HCRT_UDP_PORTS_DEFAULT, hcrt_handle); |
481 | 15 | hcrt_prefs_initialized = true; |
482 | 15 | } else { |
483 | 0 | dissector_delete_uint("ethertype", hcrt_ethertype, hcrt_handle); |
484 | 0 | } |
485 | | |
486 | 15 | hcrt_ethertype = ethertype_pref; |
487 | | |
488 | 15 | dissector_add_uint("ethertype", hcrt_ethertype, hcrt_handle); |
489 | 15 | } |
490 | | |
491 | | /* |
492 | | * Editor modelines - https://www.wireshark.org/tools/modelines.html |
493 | | * |
494 | | * Local variables: |
495 | | * c-basic-offset: 4 |
496 | | * tab-width: 8 |
497 | | * indent-tabs-mode: nil |
498 | | * End: |
499 | | * |
500 | | * vi: set shiftwidth=4 tabstop=8 expandtab: |
501 | | * :indentSize=4:tabSize=8:noTabs=true: |
502 | | */ |