/src/wireshark/epan/dissectors/packet-livewire.c
Line | Count | Source |
1 | | /* packet-livewire.c |
2 | | * Routines for dissection of Axia Livewire Audio over IP protocols |
3 | | * By Dennis Graiani <dennis.graiani@gmail.com> |
4 | | * Copyright 2026 Dennis Graiani |
5 | | * |
6 | | * Wireshark - Network traffic analyzer |
7 | | * By Gerald Combs <gerald@wireshark.org> |
8 | | * Copyright 1998 Gerald Combs |
9 | | * |
10 | | * SPDX-License-Identifier: GPL-2.0-or-later |
11 | | * |
12 | | */ |
13 | | |
14 | | #include <config.h> |
15 | | |
16 | | #include <epan/packet.h> |
17 | | #include <epan/addr_resolv.h> |
18 | | #include <epan/conversation.h> |
19 | | #include <epan/dissectors/packet-rtp.h> |
20 | | #include <epan/expert.h> |
21 | | #include <epan/dissectors/packet-tcp.h> |
22 | | |
23 | 0 | #define AXIA_MAGIC_NUMBER 0x03000207 |
24 | | |
25 | 16 | #define AXIA_FAST_CLOCK_ADDR "239.192.255.1" |
26 | 0 | #define AXIA_FAST_CLOCK_PORT 5004 |
27 | 16 | #define AXIA_SLOW_CLOCK_ADDR "239.192.255.2" |
28 | 0 | #define AXIA_SLOW_CLOCK_PORT 7000 |
29 | 0 | #define AXIA_CLOCK_RTP_VERSION 0x90 |
30 | 0 | #define AXIA_CLOCK_RTP_PAYLOAD_TYPE 0xff |
31 | 0 | #define AXIA_CLOCK_RTP_EXTENSION_PROFILE 0xfa1a |
32 | | |
33 | 16 | #define AXIA_ADV_ADDR "239.192.255.3" |
34 | 0 | #define AXIA_ADV_PORT 4001 |
35 | | |
36 | 16 | #define AXIA_GPIO_ADDR "239.192.255.4" |
37 | 0 | #define AXIA_GPIO_CONSOLE_PORT 2060 |
38 | 0 | #define AXIA_GPIO_NODE_PORT 2055 |
39 | | #define AXIA_LWCP_PORT 4010 |
40 | 0 | #define AXIA_LWCP_MODULE_PORT 4011 |
41 | 0 | #define AXIA_LWCP_CONSOLE_PORT 4012 |
42 | | |
43 | 16 | #define AXIA_INTERCOM_ADDR "239.192.255.10" |
44 | 242 | #define AXIA_INTERCOM_PORT 5000 |
45 | 0 | #define AXIA_RTP_PORT 5004 |
46 | | |
47 | | void proto_register_lwadv(void); |
48 | | void proto_reg_handoff_lwadv(void); |
49 | | |
50 | | static int proto_axia_adv; |
51 | | static int proto_axia_gpio; |
52 | | static int proto_axia_clock; |
53 | | static int proto_axia_intercom; |
54 | | static int proto_axia_lwcp; |
55 | | |
56 | | static int hf_axia_magic_num; |
57 | | static int hf_axia_seq; |
58 | | static int hf_axia_opcode; |
59 | | static int hf_axia_msg_count; |
60 | | static int hf_axia_pver; |
61 | | static int hf_axia_advt; |
62 | | static int hf_axia_unk_u8; |
63 | | static int hf_axia_unk_u16; |
64 | | static int hf_axia_unk_u32; |
65 | | static int hf_axia_unk_data; |
66 | | static int hf_axia_unk_str; |
67 | | |
68 | | static int hf_axia_term; |
69 | | static int hf_axia_term_inip; |
70 | | static int hf_axia_term_advv; |
71 | | static int hf_axia_term_hwid; |
72 | | static int hf_axia_term_udpc; |
73 | | static int hf_axia_term_nums; |
74 | | static int hf_axia_term_atrn; |
75 | | static int hf_axia_term_type; |
76 | | |
77 | | static int hf_axia_src; |
78 | | static int hf_axia_src_psid; |
79 | | static int hf_axia_src_shab; |
80 | | static int hf_axia_src_fsid; |
81 | | static int hf_axia_src_bsid; |
82 | | static int hf_axia_src_psnm; |
83 | | static int hf_axia_src_labl; |
84 | | static int hf_axia_src_lpid; |
85 | | static int hf_axia_src_setup_frame; |
86 | | static int hf_axia_src_is_mm; |
87 | | |
88 | | static int hf_axia_busy; |
89 | | static int hf_axia_busy_hwid; |
90 | | static int hf_axia_busy_fader; |
91 | | static int hf_axia_busy_ip; |
92 | | static int hf_axia_busy_prefix; |
93 | | |
94 | | static int hf_axia_gpio_lcid; |
95 | | static int hf_axia_gpio_state; |
96 | | static int hf_axia_gpio_state2; |
97 | | static int hf_axia_gpio_pmult; |
98 | | static int hf_axia_gpio_plen; |
99 | | |
100 | | static int hf_axia_clock_prio; |
101 | | static int hf_axia_clock_hwid; |
102 | | static int hf_axia_clock_mac; |
103 | | static int hf_axia_clock_samp; |
104 | | static int hf_axia_clock_fast; |
105 | | static int hf_axia_clock_seq; |
106 | | static int hf_axia_clock_rate; |
107 | | static int hf_axia_clock_type; |
108 | | |
109 | | static int hf_axia_lwcp_opcode; |
110 | | static int hf_axia_lwcp_object; |
111 | | static int hf_axia_lwcp_property; |
112 | | |
113 | | static int ett_axia_adv; |
114 | | static int ett_axia_gpio; |
115 | | static int ett_axia_clock; |
116 | | static int ett_axia_lwcp; |
117 | | |
118 | | static expert_field ei_axia_clock_changed; |
119 | | |
120 | | // Tree which stores axia_src_info_t structs by PSID (Channel Number) |
121 | | // All sources picked up from advertisements in this capture should be in here. |
122 | | // PSID is negated for backfeed sources. |
123 | | static wmem_tree_t *axia_sources; |
124 | | |
125 | | // Tree which stores axia_term_info_t structs by HWID (two LSBs from IP address) |
126 | | // All nodes picked up from advertisements in this capture should be in here. |
127 | | static wmem_tree_t *axia_nodes; |
128 | | |
129 | | static address fast_clock_address; |
130 | | static address slow_clock_address; |
131 | | static address advertisement_address; |
132 | | static address gpio_address; |
133 | | static address intercom_address; |
134 | | |
135 | | // Enum to keep track of where we are in the advertisement packet |
136 | | typedef enum |
137 | | { |
138 | | SECTION_ADV_BASE, |
139 | | SECTION_TERM, |
140 | | SECTION_SOURCE, |
141 | | SECTION_GPIO, |
142 | | } axia_adv_section_e; |
143 | | |
144 | | // Node information as received from advertisements |
145 | | typedef struct |
146 | | { |
147 | | uint16_t hwid; // Hardware ID (2 LSBs of IP) |
148 | | char *atrn; // Name of the node |
149 | | ws_in4_addr inip; // IP address of the node |
150 | | uint16_t udpc; // UDP port on which messages can be sent to the node |
151 | | conversation_t *conversation; // Pointer to the conversation on the IP and UDP port |
152 | | uint32_t nums; // Number of sources on this node |
153 | | bool in_tree; // Whether this entry has been inserted into axia_nodes |
154 | | } axia_term_info_t; |
155 | | |
156 | | // Source information as received from advertisements |
157 | | typedef struct |
158 | | { |
159 | | uint32_t psid; // Primary source ID (Livewire channel number) |
160 | | ws_in4_addr fsid; // Multicast IP for RTP audio |
161 | | ws_in4_addr bsid; // Multicast IP for RTP backfeed audio |
162 | | char *psnm; // Source name |
163 | | axia_term_info_t *term; // The node where this audio originates |
164 | | ws_in4_addr rtp_added; // Stores the RTP address when added to the RTP conversation table |
165 | | uint32_t setup_frame; // The frame number where this RTP source was originally setup |
166 | | bool in_tree; // Whether this entry has been inserted into axia_sources |
167 | | } axia_src_info_t; |
168 | | |
169 | | // Struct to hold Node and Source information as we go through an advertisement |
170 | | typedef struct |
171 | | { |
172 | | axia_term_info_t *term_info; |
173 | | axia_src_info_t *src_info; |
174 | | uint32_t lpid; |
175 | | int32_t nums; |
176 | | } axia_adv_info_t; |
177 | | |
178 | | // Struct to keep track of master clock properties |
179 | | typedef struct |
180 | | { |
181 | | address mac_address; |
182 | | uint32_t priority; // 7 = always master. 0 = never master |
183 | | } axia_clock_t; |
184 | | |
185 | | // The master clock last dissected |
186 | | static axia_clock_t axia_master_clock; |
187 | | |
188 | | static dissector_handle_t axia_adv_handle; |
189 | | static dissector_handle_t axia_gpio_handle; |
190 | | static dissector_handle_t axia_clock_handle; |
191 | | static dissector_handle_t axia_intercom_handle; |
192 | | static dissector_handle_t json_handle; |
193 | | static dissector_handle_t lwcp_handle; |
194 | | static dissector_handle_t lwcp_tcp_handle; |
195 | | |
196 | | static const value_string advtypenames[] = { |
197 | | {0x1, "Verbose announcement"}, |
198 | | {0x2, "Periodic announcement"}, |
199 | | {0x3, "Source allocation state"}, |
200 | | {0, NULL}}; |
201 | | static const value_string clocktypenames[] = { |
202 | | {0x0a, "Fast clock, packet A"}, |
203 | | {0x0b, "Fast clock, packet B"}, |
204 | | {0x0c, "Slow clock"}, |
205 | | {0, NULL}, |
206 | | }; |
207 | | static const char *get_opcode_description(const char *opcode) |
208 | 0 | { |
209 | 0 | if (!opcode) |
210 | 0 | return NULL; |
211 | 0 | if (!opcode[0]) |
212 | 0 | return NULL; |
213 | 0 | if (strcmp(opcode, "INDI") == 0) |
214 | 0 | return "Value Indication"; |
215 | 0 | if (strcmp(opcode, "WRNI") == 0) |
216 | 0 | return "Write value - returning the value indication is not requested"; |
217 | 0 | if (strcmp(opcode, "WRIN") == 0) |
218 | 0 | return "Write value - returning the value indication is requested"; |
219 | 0 | if (strcmp(opcode, "READ") == 0) |
220 | 0 | return "Read value"; |
221 | 0 | if (strcmp(opcode, "STAT") == 0) |
222 | 0 | return "Status indication"; |
223 | 0 | if (strcmp(opcode, "NEST") == 0) |
224 | 0 | return "No operation - container for nested messages"; |
225 | 0 | return NULL; |
226 | 0 | } |
227 | | static void setup_axia_transport(packet_info *pinfo, uint16_t psid) |
228 | 0 | { |
229 | 0 | if (pinfo->fd->visited) |
230 | 0 | { |
231 | 0 | return; |
232 | 0 | } |
233 | 0 | axia_src_info_t *src_info = (axia_src_info_t *)wmem_tree_lookup32(axia_sources, psid); |
234 | 0 | if (src_info && src_info->fsid && src_info->rtp_added != src_info->fsid) |
235 | 0 | { |
236 | | // set up an rtp stream here |
237 | 0 | address rtp_address; |
238 | 0 | rtp_dyn_payload_t *dyn_payload = rtp_dyn_payload_new(); |
239 | 0 | rtp_dyn_payload_insert(dyn_payload, 96, "L24", 48000, 2); |
240 | 0 | rtp_dyn_payload_insert(dyn_payload, 99, "L24", 48000, 8); |
241 | 0 | rtp_dyn_payload_insert(dyn_payload, 101, "L24", 48000, 1); |
242 | 0 | rtp_dyn_payload_insert(dyn_payload, 103, "L24", 48000, 3); |
243 | 0 | rtp_dyn_payload_insert(dyn_payload, 104, "L24", 48000, 4); |
244 | 0 | rtp_dyn_payload_insert(dyn_payload, 105, "L24", 48000, 5); |
245 | 0 | rtp_dyn_payload_insert(dyn_payload, 106, "L24", 48000, 6); |
246 | 0 | rtp_dyn_payload_insert(dyn_payload, 107, "L24", 48000, 7); |
247 | 0 | rtp_dyn_payload_insert(dyn_payload, 111, "L16", 48000, 1); |
248 | 0 | rtp_dyn_payload_insert(dyn_payload, 112, "L16", 48000, 2); |
249 | 0 | rtp_dyn_payload_insert(dyn_payload, 113, "L16", 48000, 3); |
250 | 0 | rtp_dyn_payload_insert(dyn_payload, 114, "L16", 48000, 4); |
251 | 0 | rtp_dyn_payload_insert(dyn_payload, 115, "L16", 48000, 5); |
252 | 0 | rtp_dyn_payload_insert(dyn_payload, 116, "L16", 48000, 6); |
253 | 0 | rtp_dyn_payload_insert(dyn_payload, 117, "L16", 48000, 7); |
254 | 0 | rtp_dyn_payload_insert(dyn_payload, 118, "L16", 48000, 8); |
255 | 0 | alloc_address_wmem(wmem_file_scope(), &rtp_address, AT_IPv4, sizeof(ws_in4_addr), &src_info->fsid); |
256 | 0 | rtp_add_address(pinfo, PT_UDP, &rtp_address, AXIA_RTP_PORT, 0, "Livewire", pinfo->num, RTP_MEDIA_AUDIO, dyn_payload); |
257 | 0 | free_address_wmem(wmem_file_scope(), &rtp_address); |
258 | 0 | rtp_dyn_payload_free(dyn_payload); |
259 | 0 | src_info->rtp_added = src_info->fsid; |
260 | 0 | } |
261 | 0 | } |
262 | | static void setup_adv_conversation(packet_info *pinfo, axia_term_info_t *term_info) |
263 | 0 | { |
264 | 0 | if (term_info->conversation) |
265 | 0 | return; |
266 | 0 | if (term_info->inip && term_info->udpc) |
267 | 0 | { |
268 | 0 | address node_address; |
269 | 0 | alloc_address_wmem(wmem_file_scope(), &node_address, AT_IPv4, sizeof(ws_in4_addr), &term_info->inip); |
270 | 0 | term_info->conversation = conversation_new(pinfo->num, &node_address, NULL, CONVERSATION_UDP, term_info->udpc, 0, NO_ADDR2 | NO_PORT2); |
271 | 0 | free_address_wmem(wmem_file_scope(), &node_address); |
272 | 0 | conversation_set_dissector(term_info->conversation, axia_adv_handle); |
273 | 0 | } |
274 | 0 | } |
275 | | static bool validate_header(tvbuff_t *tvb) |
276 | 0 | { |
277 | 0 | if (tvb_captured_length(tvb) < 16) |
278 | 0 | { |
279 | 0 | return false; |
280 | 0 | } |
281 | 0 | else if (tvb_get_ntohl(tvb, 0) != AXIA_MAGIC_NUMBER) |
282 | 0 | { |
283 | 0 | return false; |
284 | 0 | } |
285 | 0 | if (tvb_get_uint64(tvb, 8, ENC_BIG_ENDIAN) != 0) |
286 | 0 | { |
287 | 0 | return false; |
288 | 0 | } |
289 | 0 | return true; |
290 | 0 | } |
291 | | static void write_src_info(axia_adv_info_t *info) |
292 | 0 | { |
293 | 0 | bool is_backfeed = info->src_info->fsid && info->src_info->bsid && |
294 | 0 | info->src_info->fsid == info->src_info->bsid; |
295 | 0 | if (is_backfeed) |
296 | 0 | { |
297 | 0 | info->src_info->psid = (uint32_t)(0 - (int32_t)info->src_info->psid); |
298 | 0 | } |
299 | | /* Check for an existing src_info entry for this source, by PSID |
300 | | If there is already an entry for this source, copy the new values into it, and delete the new one. |
301 | | If not, insert the new src_info entry into the tree |
302 | | If the new src_info is already in the tree, do nothing. */ |
303 | 0 | axia_src_info_t *existing = (axia_src_info_t *)wmem_tree_lookup32(axia_sources, info->src_info->psid); |
304 | 0 | if (existing && existing != info->src_info) |
305 | 0 | { |
306 | 0 | if (info->src_info->fsid) |
307 | 0 | existing->fsid = info->src_info->fsid; |
308 | 0 | if (info->src_info->bsid) |
309 | 0 | existing->bsid = info->src_info->bsid; |
310 | 0 | if (info->src_info->psnm) |
311 | 0 | existing->psnm = info->src_info->psnm; |
312 | 0 | if (info->src_info->term) |
313 | 0 | existing->term = info->src_info->term; |
314 | 0 | if (info->src_info->rtp_added) |
315 | 0 | existing->rtp_added = info->src_info->rtp_added; |
316 | 0 | if (info->src_info->setup_frame) |
317 | 0 | existing->setup_frame = info->src_info->setup_frame; |
318 | | /* Only free the entry we are discarding if it is not itself in the |
319 | | tree (reached here because its PSID was reassigned to one held by |
320 | | a different entry); freeing a tree entry would leave a dangling |
321 | | pointer under its original key. */ |
322 | 0 | if (!info->src_info->in_tree) |
323 | 0 | wmem_free(wmem_file_scope(), info->src_info); |
324 | 0 | info->src_info = existing; |
325 | 0 | } |
326 | 0 | else if (!existing) |
327 | 0 | { |
328 | 0 | info->src_info->in_tree = true; |
329 | 0 | wmem_tree_insert32(axia_sources, info->src_info->psid, (void *)info->src_info); |
330 | 0 | } |
331 | 0 | } |
332 | | static int axia_adv_tree_add_field(proto_tree *tree, tvbuff_t *tvb, int offset, int hf) |
333 | 0 | { |
334 | 0 | int str_len; |
335 | 0 | switch (tvb_get_uint8(tvb, offset)) |
336 | 0 | { |
337 | 0 | case 0x0: |
338 | 0 | case 0x7: |
339 | 0 | proto_tree_add_item(tree, hf, tvb, offset + 1, 1, ENC_BIG_ENDIAN); |
340 | 0 | return 2; |
341 | 0 | case 0x1: |
342 | 0 | proto_tree_add_item(tree, hf, tvb, offset + 1, 4, ENC_BIG_ENDIAN); |
343 | 0 | return 5; |
344 | 0 | case 0x3: |
345 | 0 | str_len = tvb_get_uint16(tvb, offset + 1, ENC_BIG_ENDIAN); |
346 | 0 | proto_tree_add_item(tree, hf, tvb, offset + 3, str_len, ENC_ASCII); |
347 | 0 | return str_len + 3; |
348 | 0 | case 0x6: |
349 | 0 | case 0x8: |
350 | 0 | proto_tree_add_item(tree, hf, tvb, offset + 1, 2, ENC_BIG_ENDIAN); |
351 | 0 | return 3; |
352 | 0 | case 0x9: |
353 | 0 | proto_tree_add_item(tree, hf, tvb, offset + 1, 8, ENC_BIG_ENDIAN); |
354 | 0 | return 9; |
355 | 0 | } |
356 | 0 | return 0; |
357 | 0 | } |
358 | | static int dissect_axia_adv_unk(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, int offset) |
359 | 0 | { |
360 | 0 | if (offset < 4) |
361 | 0 | return 0; |
362 | 0 | int len = 0; |
363 | 0 | int str_len; |
364 | 0 | unsigned char *msg_type = tvb_get_string_enc(pinfo->pool, tvb, offset - 4, 4, ENC_ASCII); |
365 | 0 | proto_item *ti = NULL; |
366 | 0 | switch (tvb_get_uint8(tvb, offset)) |
367 | 0 | { |
368 | 0 | case 0x0: |
369 | 0 | case 0x7: |
370 | 0 | ti = proto_tree_add_item(tree, hf_axia_unk_u8, tvb, offset + 1, 1, ENC_BIG_ENDIAN); |
371 | 0 | len = 2; |
372 | 0 | break; |
373 | 0 | case 0x1: |
374 | 0 | ti = proto_tree_add_item(tree, hf_axia_unk_u32, tvb, offset + 1, 4, ENC_BIG_ENDIAN); |
375 | 0 | len = 5; |
376 | 0 | break; |
377 | 0 | case 0x3: |
378 | 0 | str_len = tvb_get_uint16(tvb, offset + 1, ENC_BIG_ENDIAN); |
379 | 0 | ti = proto_tree_add_item(tree, hf_axia_unk_str, tvb, offset + 3, str_len, ENC_ASCII); |
380 | 0 | len = (str_len + 3); |
381 | 0 | break; |
382 | 0 | case 0x6: |
383 | 0 | case 0x8: |
384 | 0 | ti = proto_tree_add_item(tree, hf_axia_unk_u16, tvb, offset + 1, 2, ENC_BIG_ENDIAN); |
385 | 0 | len = 3; |
386 | 0 | break; |
387 | 0 | case 0x9: |
388 | 0 | ti = proto_tree_add_item(tree, hf_axia_unk_data, tvb, offset + 1, 8, ENC_NA); |
389 | 0 | len = 9; |
390 | 0 | break; |
391 | 0 | } |
392 | 0 | if (ti) |
393 | 0 | proto_item_append_text(ti, " (%s)", msg_type); |
394 | 0 | return len; |
395 | 0 | } |
396 | | |
397 | | /* |
398 | | Implements increment_dissection_depth and decrement_dissection_depth |
399 | | */ |
400 | | // NOLINTNEXTLINE(misc-no-recursion) |
401 | | static int dissect_axia_adv_msg(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, int offset, axia_adv_section_e section, axia_adv_info_t *info) |
402 | 0 | { |
403 | 0 | char *msg_type; |
404 | 0 | proto_item *ti; |
405 | 0 | uint32_t lpid; |
406 | 0 | uint32_t lcid; |
407 | 0 | uint32_t state; |
408 | 0 | uint32_t mult; |
409 | 0 | uint32_t plen; |
410 | 0 | bool gpi = false; |
411 | 0 | bool source_is_new = false; |
412 | 0 | msg_type = (char *)tvb_get_string_enc(pinfo->pool, tvb, offset, 4, ENC_ASCII); |
413 | 0 | offset += 4; |
414 | 0 | if (info == NULL) |
415 | 0 | { |
416 | 0 | info = wmem_new0(pinfo->pool, axia_adv_info_t); |
417 | 0 | } |
418 | 0 | if (get_opcode_description(msg_type)) |
419 | 0 | { |
420 | 0 | int msg_count = (int)tvb_get_uint8(tvb, offset + 1); |
421 | 0 | proto_tree_add_string_format_value(tree, hf_axia_opcode, tvb, offset - 4, 4, msg_type, |
422 | 0 | "%s (%s)", get_opcode_description(msg_type), msg_type); |
423 | 0 | offset += axia_adv_tree_add_field(tree, tvb, offset, hf_axia_msg_count); |
424 | 0 | for (int i = 0; i < msg_count; i++) |
425 | 0 | { |
426 | 0 | increment_dissection_depth(pinfo); |
427 | 0 | offset = dissect_axia_adv_msg(tvb, pinfo, tree, offset, section, info); |
428 | 0 | decrement_dissection_depth(pinfo); |
429 | 0 | } |
430 | 0 | if (section == SECTION_ADV_BASE && info->src_info && |
431 | 0 | info->term_info && info->term_info->nums) |
432 | 0 | { |
433 | 0 | col_append_fstr(pinfo->cinfo, COL_INFO, " (%d of %d Sources)", info->nums, info->term_info->nums); |
434 | 0 | } |
435 | 0 | return offset; |
436 | 0 | } |
437 | 0 | else if (section == SECTION_GPIO) |
438 | 0 | offset -= 4; |
439 | 0 | switch (section) |
440 | 0 | { |
441 | 0 | case SECTION_ADV_BASE: |
442 | 0 | ws_assert(msg_type); |
443 | 0 | if (strcmp(msg_type, "PVER") == 0) |
444 | 0 | { |
445 | 0 | return offset + axia_adv_tree_add_field(tree, tvb, offset, hf_axia_pver); |
446 | 0 | } |
447 | 0 | else if (strcmp(msg_type, "ADVT") == 0) |
448 | 0 | { |
449 | 0 | col_set_str(pinfo->cinfo, COL_INFO, val_to_str_const(tvb_get_uint8(tvb, offset + 1), advtypenames, "Unknown Livewire Advertisement type")); |
450 | 0 | return offset + axia_adv_tree_add_field(tree, tvb, offset, hf_axia_advt); |
451 | 0 | } |
452 | 0 | else if (strcmp(msg_type, "TERM") == 0) |
453 | 0 | { |
454 | 0 | int term_len = tvb_get_uint16(tvb, offset + 1, ENC_BIG_ENDIAN); |
455 | 0 | ti = proto_tree_add_item(tree, hf_axia_term, tvb, offset - 4, term_len + 7, ENC_NA); |
456 | 0 | proto_tree *term_tree = proto_item_add_subtree(ti, ett_axia_adv); |
457 | 0 | proto_item_set_text(ti, "Terminal Information"); |
458 | 0 | info->term_info = wmem_new0(wmem_file_scope(), axia_term_info_t); |
459 | 0 | increment_dissection_depth(pinfo); |
460 | 0 | dissect_axia_adv_msg(tvb, pinfo, term_tree, offset + 3, SECTION_TERM, info); |
461 | 0 | decrement_dissection_depth(pinfo); |
462 | 0 | if (info->term_info->inip && info->term_info->atrn) |
463 | 0 | { |
464 | 0 | add_ipv4_name(info->term_info->inip, info->term_info->atrn, false); |
465 | 0 | } |
466 | 0 | if (info->term_info->inip && info->term_info->udpc) |
467 | 0 | { |
468 | 0 | setup_adv_conversation(pinfo, info->term_info); |
469 | 0 | } |
470 | 0 | return offset + term_len + 3; |
471 | 0 | } |
472 | 0 | else if (msg_type[0] == 'S' && |
473 | 0 | msg_type[1] >= '0' && msg_type[1] <= '9' && |
474 | 0 | msg_type[2] >= '0' && msg_type[2] <= '9' && |
475 | 0 | msg_type[3] >= '0' && msg_type[3] <= '9') |
476 | 0 | { |
477 | 0 | info->nums++; |
478 | 0 | int src_num = ((msg_type[1] - '0') * 100) + ((msg_type[2] - '0') * 10) + (msg_type[3] - '0'); |
479 | 0 | int src_len = tvb_get_uint16(tvb, offset + 1, ENC_BIG_ENDIAN); |
480 | 0 | ti = proto_tree_add_item(tree, hf_axia_src, tvb, offset - 4, src_len + 7, ENC_NA); |
481 | 0 | proto_tree *source_tree = proto_item_add_subtree(ti, ett_axia_adv); |
482 | 0 | proto_item_set_text(ti, "Source %d", src_num); |
483 | 0 | info->src_info = wmem_new0(wmem_file_scope(), axia_src_info_t); |
484 | 0 | increment_dissection_depth(pinfo); |
485 | 0 | dissect_axia_adv_msg(tvb, pinfo, source_tree, offset + 3, SECTION_SOURCE, info); |
486 | 0 | decrement_dissection_depth(pinfo); |
487 | 0 | if (info->term_info) |
488 | 0 | info->src_info->term = info->term_info; |
489 | 0 | proto_item_append_text(ti, ": %d", info->src_info->psid); |
490 | 0 | write_src_info(info); |
491 | 0 | if (info->src_info->psnm) |
492 | 0 | { |
493 | 0 | proto_item_append_text(ti, " [%s", info->src_info->psnm); |
494 | 0 | if (info->src_info->term && info->src_info->term->atrn) |
495 | 0 | { |
496 | 0 | proto_item_append_text(ti, "@%s", info->src_info->term->atrn); |
497 | 0 | } |
498 | 0 | proto_item_append_text(ti, "]"); |
499 | 0 | if (!info->src_info->setup_frame) |
500 | 0 | { |
501 | 0 | info->src_info->setup_frame = pinfo->num; |
502 | 0 | } |
503 | 0 | else if (info->src_info->setup_frame != pinfo->num) |
504 | 0 | { |
505 | 0 | ti = proto_tree_add_uint(source_tree, hf_axia_src_setup_frame, tvb, 0, 0, info->src_info->setup_frame); |
506 | 0 | proto_item_set_generated(ti); |
507 | 0 | } |
508 | 0 | if (info->src_info->fsid && info->src_info->bsid) |
509 | 0 | { |
510 | 0 | bool is_mm = info->src_info->bsid == info->src_info->fsid; |
511 | 0 | ti = proto_tree_add_boolean(source_tree, hf_axia_src_is_mm, |
512 | 0 | tvb, 0, 0, is_mm); |
513 | 0 | if (is_mm) |
514 | 0 | { |
515 | 0 | proto_item_set_text(ti, "Source is a backfeed"); |
516 | 0 | } |
517 | 0 | else |
518 | 0 | { |
519 | 0 | proto_item_set_hidden(ti); |
520 | 0 | } |
521 | 0 | proto_item_set_generated(ti); |
522 | 0 | } |
523 | 0 | } |
524 | 0 | setup_axia_transport(pinfo, info->src_info->psid); |
525 | 0 | return offset + src_len + 3; |
526 | 0 | } |
527 | 0 | break; |
528 | 0 | case SECTION_TERM: |
529 | 0 | ws_assert(msg_type); |
530 | 0 | if (strcmp(msg_type, "INIP") == 0) |
531 | 0 | { |
532 | 0 | info->term_info->inip = tvb_get_ipv4(tvb, offset + 1); |
533 | 0 | return offset + axia_adv_tree_add_field(tree, tvb, offset, hf_axia_term_inip); |
534 | 0 | } |
535 | 0 | else if (strcmp(msg_type, "HWID") == 0) |
536 | 0 | { |
537 | 0 | info->term_info->hwid = tvb_get_uint16(tvb, offset + 1, ENC_BIG_ENDIAN); |
538 | | |
539 | | /* Check for an existing term_info entry for this node, by HWID |
540 | | If there is already an entry for this node, copy the new values into it, and delete the new one. |
541 | | If not, insert the new term_info entry into the tree |
542 | | If the new term_info is already in the tree, do nothing. */ |
543 | 0 | axia_term_info_t *existing = (axia_term_info_t *)wmem_tree_lookup32(axia_nodes, info->term_info->hwid); |
544 | 0 | if (existing && existing != info->term_info) |
545 | 0 | { |
546 | 0 | if (info->term_info->atrn) |
547 | 0 | existing->atrn = info->term_info->atrn; |
548 | 0 | if (info->term_info->inip) |
549 | 0 | existing->inip = info->term_info->inip; |
550 | 0 | if (info->term_info->udpc) |
551 | 0 | existing->udpc = info->term_info->udpc; |
552 | | /* Only free the entry we are discarding if it is not |
553 | | itself in the tree (reached here because its HWID was |
554 | | reassigned to one held by a different entry); freeing a |
555 | | tree entry would leave a dangling pointer under its |
556 | | original key. */ |
557 | 0 | if (!info->term_info->in_tree) |
558 | 0 | wmem_free(wmem_file_scope(), info->term_info); |
559 | 0 | info->term_info = existing; |
560 | 0 | } |
561 | 0 | else if (!existing) |
562 | 0 | { |
563 | 0 | info->term_info->in_tree = true; |
564 | 0 | wmem_tree_insert32(axia_nodes, info->term_info->hwid, (void *)info->term_info); |
565 | 0 | } |
566 | 0 | return offset + axia_adv_tree_add_field(tree, tvb, offset, hf_axia_term_hwid); |
567 | 0 | } |
568 | 0 | else if (strcmp(msg_type, "ADVV") == 0) |
569 | 0 | { |
570 | 0 | return offset + axia_adv_tree_add_field(tree, tvb, offset, hf_axia_term_advv); |
571 | 0 | } |
572 | 0 | else if (strcmp(msg_type, "UDPC") == 0) |
573 | 0 | { |
574 | 0 | info->term_info->udpc = tvb_get_uint16(tvb, offset + 1, ENC_BIG_ENDIAN); |
575 | 0 | return offset + axia_adv_tree_add_field(tree, tvb, offset, hf_axia_term_udpc); |
576 | 0 | } |
577 | 0 | else if (strcmp(msg_type, "NUMS") == 0) |
578 | 0 | { |
579 | 0 | info->term_info->nums = tvb_get_uint16(tvb, offset + 1, ENC_BIG_ENDIAN); |
580 | 0 | return offset + axia_adv_tree_add_field(tree, tvb, offset, hf_axia_term_nums); |
581 | 0 | } |
582 | 0 | else if (strcmp(msg_type, "ATRN") == 0) |
583 | 0 | { |
584 | 0 | int str_len = tvb_get_uint16(tvb, offset + 1, ENC_BIG_ENDIAN); |
585 | 0 | char *atrn = (char *)tvb_get_string_enc(wmem_file_scope(), tvb, offset + 3, str_len, ENC_ASCII); |
586 | 0 | info->term_info->atrn = atrn; |
587 | 0 | return offset + axia_adv_tree_add_field(tree, tvb, offset, hf_axia_term_atrn); |
588 | 0 | } |
589 | 0 | else if (strcmp(msg_type, "TYPE") == 0) |
590 | 0 | { |
591 | 0 | return offset + axia_adv_tree_add_field(tree, tvb, offset, hf_axia_term_type); |
592 | 0 | } |
593 | 0 | break; |
594 | 0 | case SECTION_SOURCE: |
595 | 0 | ws_assert(msg_type); |
596 | 0 | if (strcmp(msg_type, "PSID") == 0) |
597 | 0 | { |
598 | 0 | info->src_info->psid = tvb_get_uint32(tvb, offset + 1, ENC_BIG_ENDIAN); |
599 | 0 | return offset + axia_adv_tree_add_field(tree, tvb, offset, hf_axia_src_psid); |
600 | 0 | } |
601 | 0 | else if (strcmp(msg_type, "PSNM") == 0) |
602 | 0 | { |
603 | 0 | int str_len = tvb_get_uint16(tvb, offset + 1, ENC_BIG_ENDIAN); |
604 | 0 | char *psnm = (char *)tvb_get_string_enc(wmem_file_scope(), tvb, offset + 3, str_len, ENC_ASCII); |
605 | 0 | info->src_info->psnm = psnm; |
606 | 0 | return offset + axia_adv_tree_add_field(tree, tvb, offset, hf_axia_src_psnm); |
607 | 0 | } |
608 | 0 | else if (strcmp(msg_type, "LABL") == 0) |
609 | 0 | { |
610 | 0 | return offset + axia_adv_tree_add_field(tree, tvb, offset, hf_axia_src_labl); |
611 | 0 | } |
612 | 0 | else if (strcmp(msg_type, "FSID") == 0) |
613 | 0 | { |
614 | 0 | ws_in4_addr fsid = tvb_get_ipv4(tvb, offset + 1); |
615 | 0 | info->src_info->fsid = fsid; |
616 | 0 | return offset + axia_adv_tree_add_field(tree, tvb, offset, hf_axia_src_fsid); |
617 | 0 | } |
618 | 0 | else if (strcmp(msg_type, "BSID") == 0) |
619 | 0 | { |
620 | 0 | info->src_info->bsid = tvb_get_ipv4(tvb, offset + 1); |
621 | 0 | return offset + axia_adv_tree_add_field(tree, tvb, offset, hf_axia_src_bsid); |
622 | 0 | } |
623 | 0 | else if (strcmp(msg_type, "SHAB") == 0) |
624 | 0 | { |
625 | 0 | return offset + axia_adv_tree_add_field(tree, tvb, offset, hf_axia_src_shab); |
626 | 0 | } |
627 | 0 | else if (strcmp(msg_type, "LPID") == 0) |
628 | 0 | { |
629 | 0 | return offset + axia_adv_tree_add_field(tree, tvb, offset, hf_axia_src_lpid); |
630 | 0 | } |
631 | 0 | else if (strcmp(msg_type, "BUSY") == 0 && tvb_get_uint8(tvb, offset) == 0x9) |
632 | 0 | { |
633 | 0 | ti = proto_tree_add_item(tree, hf_axia_busy, tvb, offset + 1, 8, ENC_NA); |
634 | 0 | if (tvb_get_uint64(tvb, offset + 1, ENC_BIG_ENDIAN) == 0) |
635 | 0 | { |
636 | | // free |
637 | 0 | proto_item_append_text(ti, ": Free"); |
638 | 0 | } |
639 | 0 | else |
640 | 0 | { |
641 | 0 | ws_in4_addr console_ip; |
642 | 0 | uint32_t prefix; |
643 | 0 | uint32_t hwid; |
644 | 0 | unsigned fader_num; |
645 | 0 | char addr_str[16]; |
646 | 0 | proto_tree *busy_tree = proto_item_add_subtree(ti, ett_axia_adv); |
647 | 0 | proto_tree_add_item_ret_uint(busy_tree, hf_axia_busy_hwid, tvb, offset + 3, 2, ENC_BIG_ENDIAN, &hwid); |
648 | 0 | proto_tree_add_item_ret_uint(busy_tree, hf_axia_busy_prefix, tvb, offset + 7, 2, ENC_BIG_ENDIAN, &prefix); |
649 | 0 | console_ip = (ws_in4_addr)((g_htonl(prefix) >> 16) + g_htonl(hwid)); |
650 | 0 | ws_inet_ntop4(&console_ip, addr_str, sizeof(addr_str)); |
651 | 0 | proto_tree_add_ipv4(busy_tree, hf_axia_busy_ip, tvb, offset + 3, 6, console_ip); |
652 | 0 | proto_item *fader = proto_tree_add_item_ret_uint(busy_tree, hf_axia_busy_fader, tvb, offset + 6, 1, ENC_BIG_ENDIAN, &fader_num); |
653 | 0 | proto_item_set_text(fader, "Fader: %d", fader_num + 1); |
654 | 0 | axia_term_info_t *console = wmem_tree_lookup32(axia_nodes, tvb_get_uint16(tvb, offset + 3, ENC_BIG_ENDIAN)); |
655 | 0 | bool have_name = false; |
656 | 0 | if (console && console->atrn) |
657 | 0 | have_name = true; |
658 | 0 | proto_item_append_text(ti, " [Console %s, Fader %d]", have_name ? console->atrn : addr_str, fader_num + 1); |
659 | 0 | } |
660 | 0 | return offset + 9; |
661 | 0 | } |
662 | 0 | break; |
663 | 0 | case SECTION_GPIO: |
664 | 0 | ti = proto_tree_add_item(tree, proto_axia_gpio, tvb, offset + 1, 5, ENC_NA); |
665 | 0 | proto_tree *gpio_tree = proto_item_add_subtree(ti, ett_axia_adv); |
666 | 0 | proto_item *lpid_item = proto_tree_add_item_ret_uint(gpio_tree, hf_axia_src_lpid, tvb, offset + 1, 2, ENC_BIG_ENDIAN, &lpid); |
667 | 0 | if (lpid != 0xff) |
668 | 0 | { |
669 | 0 | if (info->lpid != lpid) |
670 | 0 | source_is_new = true; |
671 | 0 | info->lpid = lpid; |
672 | 0 | } |
673 | 0 | else |
674 | 0 | { |
675 | 0 | lpid = info->lpid; |
676 | 0 | proto_item_append_text(lpid_item, " (same as previous)"); |
677 | 0 | } |
678 | 0 | axia_src_info_t *source = wmem_tree_lookup32(axia_sources, lpid); |
679 | 0 | axia_term_info_t *term; |
680 | 0 | if (source) |
681 | 0 | term = source->term; |
682 | 0 | if (source && source->psnm && term && term->atrn) |
683 | 0 | { |
684 | 0 | proto_item_append_text(lpid_item, " [%s@%s]", source->psnm, term->atrn); |
685 | 0 | proto_tree *setup_tree = proto_item_add_subtree(lpid_item, ett_axia_gpio); |
686 | 0 | proto_item *setup_frm = proto_tree_add_uint(setup_tree, hf_axia_src_setup_frame, tvb, 0, 0, source->setup_frame); |
687 | 0 | proto_item_set_generated(setup_frm); |
688 | 0 | } |
689 | 0 | proto_item *lcid_item = proto_tree_add_item_ret_uint(gpio_tree, hf_axia_gpio_lcid, tvb, offset + 3, 1, ENC_BIG_ENDIAN, &lcid); |
690 | 0 | if (lcid < 9) |
691 | 0 | lcid = 9 - lcid; |
692 | 0 | else |
693 | 0 | { |
694 | 0 | lcid = 14 - lcid; |
695 | 0 | gpi = true; |
696 | 0 | } |
697 | 0 | proto_item_append_text(lcid_item, gpi ? " (GPI Pin %d)" : " (GPO Pin %d)", lcid); |
698 | 0 | proto_item *pmult_item = proto_tree_add_item_ret_uint(gpio_tree, hf_axia_gpio_pmult, tvb, offset + 5, 1, ENC_BIG_ENDIAN, &mult); |
699 | 0 | proto_item *state_item = proto_tree_add_item_ret_uint(gpio_tree, hf_axia_gpio_state, tvb, offset + 5, 1, ENC_BIG_ENDIAN, &state); |
700 | 0 | proto_item *plen_item = proto_tree_add_item_ret_uint(gpio_tree, hf_axia_gpio_plen, tvb, offset + 5, 1, ENC_BIG_ENDIAN, &plen); |
701 | 0 | if (!state && !mult && plen <= 1) |
702 | 0 | { |
703 | 0 | proto_item_set_hidden(state_item); |
704 | 0 | proto_item_set_hidden(plen_item); |
705 | 0 | proto_item_set_hidden(pmult_item); |
706 | 0 | state_item = proto_tree_add_item_ret_uint(gpio_tree, hf_axia_gpio_state2, tvb, offset + 5, 1, ENC_BIG_ENDIAN, &state); |
707 | 0 | plen = 0; |
708 | 0 | } |
709 | 0 | else |
710 | 0 | { |
711 | 0 | plen *= mult ? 10 : 250; |
712 | 0 | proto_item_append_text(pmult_item, " (%s)", mult ? "10 mS" : "250 mS"); |
713 | 0 | } |
714 | 0 | proto_item_append_text(state_item, " (%s)", state ? "Low" : "High"); |
715 | 0 | if (plen) |
716 | 0 | proto_item_append_text(plen_item, " (%d mS)", plen); |
717 | 0 | proto_item_append_text(ti, ": LPID=%d ", lpid); |
718 | 0 | if (source_is_new) |
719 | 0 | col_append_fstr(pinfo->cinfo, COL_INFO, "LPID=%d ", lpid); |
720 | 0 | if (source && source->psnm && term && term->atrn) |
721 | 0 | { |
722 | 0 | proto_item_append_text(ti, "[%s@%s] ", source->psnm, term->atrn); |
723 | 0 | if (source_is_new) |
724 | 0 | col_append_fstr(pinfo->cinfo, COL_INFO, "[%s@%s] ", source->psnm, term->atrn); |
725 | 0 | } |
726 | 0 | proto_item_append_text(ti, "Pin=%s %d State=", gpi ? "GPI" : "GPO", lcid); |
727 | 0 | col_append_fstr(pinfo->cinfo, COL_INFO, "Pin=%s %d State=", gpi ? "GPI" : "GPO", lcid); |
728 | 0 | if (plen) |
729 | 0 | { |
730 | 0 | proto_item_append_text(ti, "Pulse "); |
731 | 0 | col_append_str(pinfo->cinfo, COL_INFO, "Pulse "); |
732 | 0 | } |
733 | 0 | proto_item_append_text(ti, "%s ", state ? "Low" : "High"); |
734 | 0 | col_append_fstr(pinfo->cinfo, COL_INFO, "%s ", state ? "Low" : "High"); |
735 | 0 | if (plen) |
736 | 0 | { |
737 | 0 | proto_item_append_text(ti, "for %dmS ", plen); |
738 | 0 | col_append_fstr(pinfo->cinfo, COL_INFO, "for %dmS ", plen); |
739 | 0 | } |
740 | 0 | return offset + 6; |
741 | 0 | break; |
742 | 0 | } |
743 | 0 | return offset + dissect_axia_adv_unk(tvb, pinfo, tree, offset); |
744 | 0 | } |
745 | | static int dissect_lwadv(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void *data _U_) |
746 | 0 | { |
747 | 0 | if (!validate_header(tvb)) /* This is not an Axia packet */ |
748 | 0 | return 0; |
749 | 0 | col_set_str(pinfo->cinfo, COL_PROTOCOL, "LWADV"); |
750 | 0 | col_clear(pinfo->cinfo, COL_INFO); |
751 | |
|
752 | 0 | proto_item *ti = proto_tree_add_item(tree, proto_axia_adv, tvb, 0, -1, ENC_NA); |
753 | 0 | proto_tree *axia_adv_tree = proto_item_add_subtree(ti, ett_axia_adv); |
754 | 0 | proto_tree_add_item(axia_adv_tree, hf_axia_magic_num, tvb, 0, 4, ENC_NA); |
755 | 0 | proto_tree_add_item(axia_adv_tree, hf_axia_seq, tvb, 4, 4, ENC_BIG_ENDIAN); |
756 | 0 | int offset = 16; |
757 | 0 | dissect_axia_adv_msg(tvb, pinfo, axia_adv_tree, offset, SECTION_ADV_BASE, NULL); |
758 | 0 | return offset; |
759 | 0 | } |
760 | | static int dissect_lwgpio(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void *data _U_) |
761 | 0 | { |
762 | 0 | if (!validate_header(tvb)) /* This is not an Axia packet */ |
763 | 0 | return 0; |
764 | 0 | col_set_str(pinfo->cinfo, COL_PROTOCOL, "LWGPIO"); |
765 | 0 | col_clear(pinfo->cinfo, COL_INFO); |
766 | 0 | proto_item *ti = proto_tree_add_item(tree, proto_axia_gpio, tvb, 0, -1, ENC_NA); |
767 | 0 | proto_tree *axia_adv_tree = proto_item_add_subtree(ti, ett_axia_gpio); |
768 | 0 | proto_tree_add_item(axia_adv_tree, hf_axia_magic_num, tvb, 0, 4, ENC_NA); |
769 | 0 | proto_tree_add_item(axia_adv_tree, hf_axia_seq, tvb, 4, 4, ENC_BIG_ENDIAN); |
770 | 0 | int offset = 16; |
771 | 0 | dissect_axia_adv_msg(tvb, pinfo, axia_adv_tree, offset, SECTION_GPIO, NULL); |
772 | 0 | return offset; |
773 | 0 | } |
774 | | static int dissect_lwclock(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void *data _U_) |
775 | 0 | { |
776 | 0 | if (tvb_captured_length(tvb) != 36) |
777 | 0 | return 0; |
778 | 0 | uint32_t rtp_timestamp; |
779 | 0 | uint32_t seq; |
780 | 0 | uint32_t type; |
781 | 0 | uint32_t priority; |
782 | 0 | uint8_t mac_address[FT_ETHER_LEN]; |
783 | 0 | address current_clock_mac; |
784 | |
|
785 | 0 | col_set_str(pinfo->cinfo, COL_PROTOCOL, "LWCLOCK"); |
786 | 0 | col_clear(pinfo->cinfo, COL_INFO); |
787 | 0 | proto_item *ti = proto_tree_add_item(tree, proto_axia_clock, tvb, 0, -1, ENC_NA); |
788 | 0 | proto_tree *axia_clock_tree = proto_item_add_subtree(ti, ett_axia_clock); |
789 | 0 | proto_tree_add_item_ret_uint(axia_clock_tree, hf_axia_clock_seq, tvb, 2, 2, ENC_BIG_ENDIAN, &seq); |
790 | 0 | proto_tree_add_item_ret_uint(axia_clock_tree, hf_axia_clock_samp, tvb, 4, 4, ENC_BIG_ENDIAN, &rtp_timestamp); |
791 | 0 | proto_tree_add_item(axia_clock_tree, hf_axia_clock_fast, tvb, 16, 4, ENC_BIG_ENDIAN); |
792 | 0 | proto_tree_add_item_ret_uint(axia_clock_tree, hf_axia_clock_type, tvb, 20, 1, ENC_NA, &type); |
793 | 0 | proto_tree_add_item_ret_uint(axia_clock_tree, hf_axia_clock_prio, tvb, 27, 1, ENC_NA, &priority); |
794 | 0 | ti = proto_tree_add_item_ret_ether(axia_clock_tree, hf_axia_clock_mac, tvb, 30, 6, ENC_NA, mac_address); |
795 | 0 | alloc_address_wmem(wmem_epan_scope(), ¤t_clock_mac, AT_ETHER, sizeof(mac_address), mac_address); |
796 | 0 | if (cmp_address(¤t_clock_mac, &axia_master_clock.mac_address) || axia_master_clock.priority != priority) |
797 | 0 | { |
798 | | // they do not equal, so the clock has changed... or something |
799 | 0 | axia_master_clock.mac_address = current_clock_mac; |
800 | 0 | axia_master_clock.priority = priority; |
801 | 0 | expert_add_info(pinfo, ti, &ei_axia_clock_changed); |
802 | 0 | } |
803 | 0 | else |
804 | 0 | { |
805 | 0 | free_address_wmem(wmem_epan_scope(), ¤t_clock_mac); |
806 | 0 | } |
807 | |
|
808 | 0 | bool fast_rate = type == 0x0a || type == 0x0b; |
809 | 0 | ti = proto_tree_add_boolean(axia_clock_tree, hf_axia_clock_rate, tvb, 0, 0, fast_rate); |
810 | 0 | proto_item_set_generated(ti); |
811 | 0 | col_append_fstr(pinfo->cinfo, COL_INFO, "%s, Seq=%u, Priority=%u, Time=%u", |
812 | 0 | val_to_str_const(type, clocktypenames, "Unknown clock packet"), seq, priority, rtp_timestamp); |
813 | 0 | return tvb_captured_length(tvb); |
814 | 0 | } |
815 | | static int dissect_intercom(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void *data _U_) |
816 | 0 | { |
817 | 0 | if (json_handle) |
818 | 0 | { |
819 | 0 | int json_length = tvb_captured_length(tvb) - 37; |
820 | 0 | tvbuff_t *json_tvb = tvb_new_subset_length(tvb, 37, tvb_captured_length(tvb) - 37); |
821 | 0 | int json_dissected = call_dissector(json_handle, json_tvb, pinfo, tree); |
822 | 0 | if (json_length == json_dissected) |
823 | 0 | { |
824 | 0 | return tvb_reported_length(tvb); |
825 | 0 | } |
826 | 0 | else |
827 | 0 | { |
828 | 0 | return 0; |
829 | 0 | } |
830 | 0 | } |
831 | 0 | return tvb_reported_length(tvb); |
832 | 0 | } |
833 | | static uint32_t get_lwcp_pdu_len(packet_info *pinfo _U_, tvbuff_t *tvb, int offset, void *data _U_) |
834 | 0 | { |
835 | 0 | int next_offset = offset; |
836 | 0 | int tvb_len = tvb_reported_length(tvb); |
837 | 0 | bool in_encap = false; |
838 | 0 | bool in_quotes = false; |
839 | 0 | while(next_offset < tvb_len) |
840 | 0 | { |
841 | 0 | if (tvb_strneql(tvb, next_offset, "%BeginEncap%", 12) == 0) |
842 | 0 | { |
843 | 0 | in_encap = true; |
844 | 0 | next_offset += 12; |
845 | 0 | continue; |
846 | 0 | } |
847 | 0 | if (tvb_strneql(tvb, next_offset, "%EndEncap%", 10) == 0) |
848 | 0 | { |
849 | 0 | in_encap = false; |
850 | 0 | next_offset += 10; |
851 | 0 | continue; |
852 | 0 | } |
853 | 0 | char c = tvb_get_uint8(tvb, next_offset); |
854 | 0 | if (c == '"') |
855 | 0 | { |
856 | 0 | in_quotes = !in_quotes; |
857 | 0 | } |
858 | 0 | if (!in_encap && !in_quotes) |
859 | 0 | { |
860 | 0 | if (c == '\r') |
861 | 0 | { |
862 | 0 | if ((next_offset + 1 < tvb_len) && (tvb_get_uint8(tvb, next_offset + 1) == '\n')) |
863 | 0 | { |
864 | 0 | return next_offset - offset + 2; |
865 | 0 | } |
866 | 0 | } |
867 | 0 | else if (c == '\n') |
868 | 0 | { |
869 | 0 | return next_offset - offset + 1; |
870 | 0 | } |
871 | 0 | } |
872 | 0 | next_offset++; |
873 | 0 | } |
874 | 0 | return 0; |
875 | 0 | } |
876 | | static int dissect_lwcp(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void *data _U_) |
877 | 0 | { |
878 | 0 | col_set_str(pinfo->cinfo, COL_PROTOCOL, "LWCP"); |
879 | 0 | col_clear(pinfo->cinfo, COL_INFO); |
880 | 0 | proto_item *ti = proto_tree_add_item(tree, proto_axia_lwcp, tvb, 0, -1, ENC_NA); |
881 | 0 | proto_tree *axia_lwcp_tree = proto_item_add_subtree(ti, ett_axia_lwcp); |
882 | 0 | unsigned start = 0; |
883 | 0 | unsigned offset = 0; |
884 | 0 | int field = 0; |
885 | 0 | int encap_depth = 0; |
886 | 0 | bool in_quotes = false; |
887 | 0 | char *op; |
888 | 0 | char *obj; |
889 | 0 | char *prop; |
890 | 0 | while(offset < tvb_reported_length(tvb)) |
891 | 0 | { |
892 | 0 | if (tvb_strneql(tvb, offset, "%BeginEncap%", 12) == 0) |
893 | 0 | { |
894 | 0 | encap_depth++; |
895 | 0 | offset += 12; |
896 | 0 | continue; |
897 | 0 | } |
898 | 0 | if (tvb_strneql(tvb, offset, "%EndEncap%", 10) == 0) |
899 | 0 | { |
900 | 0 | encap_depth--; |
901 | 0 | offset += 10; |
902 | 0 | continue; |
903 | 0 | } |
904 | | |
905 | 0 | char c = tvb_get_uint8(tvb, offset); |
906 | 0 | if (c == '"') |
907 | 0 | { |
908 | 0 | in_quotes = !in_quotes; |
909 | 0 | } |
910 | 0 | else if (c == '[') |
911 | 0 | { |
912 | 0 | encap_depth++; |
913 | 0 | } |
914 | 0 | else if (c == ']') |
915 | 0 | { |
916 | 0 | encap_depth--; |
917 | 0 | } |
918 | 0 | if (encap_depth == 0 && !in_quotes) |
919 | 0 | { |
920 | 0 | int len = offset - start; |
921 | 0 | bool over = false; |
922 | 0 | if (c == '\n' || c == '\r') |
923 | 0 | { |
924 | 0 | over = true; |
925 | 0 | } |
926 | 0 | else if (offset == tvb_reported_length(tvb) - 1) |
927 | 0 | { |
928 | 0 | over = true; |
929 | 0 | len++; |
930 | 0 | } |
931 | 0 | if (over || c == ' ') |
932 | 0 | { |
933 | 0 | switch(field) |
934 | 0 | { |
935 | 0 | case 0: |
936 | 0 | proto_tree_add_item_ret_display_string(axia_lwcp_tree, hf_axia_lwcp_opcode, tvb, start, len, ENC_ASCII, |
937 | 0 | wmem_file_scope(), &op); |
938 | 0 | col_append_fstr(pinfo->cinfo, COL_INFO, "%s ", op); |
939 | 0 | break; |
940 | 0 | case 1: |
941 | 0 | proto_tree_add_item_ret_display_string(axia_lwcp_tree, hf_axia_lwcp_object, tvb, start, len, ENC_ASCII, |
942 | 0 | wmem_file_scope(), &obj); |
943 | 0 | col_append_fstr(pinfo->cinfo, COL_INFO, "%s ", obj); |
944 | 0 | break; |
945 | 0 | default: |
946 | 0 | proto_tree_add_item_ret_display_string(axia_lwcp_tree, hf_axia_lwcp_property, tvb, start, len, ENC_ASCII, |
947 | 0 | wmem_file_scope(), &prop); |
948 | 0 | col_append_str(pinfo->cinfo, COL_INFO, prop); |
949 | 0 | if (!over) |
950 | 0 | { |
951 | 0 | col_append_str(pinfo->cinfo, COL_INFO, ", "); |
952 | 0 | } |
953 | 0 | break; |
954 | 0 | } |
955 | 0 | if (over) |
956 | 0 | { |
957 | 0 | return tvb_reported_length(tvb); |
958 | 0 | } |
959 | 0 | field++; |
960 | 0 | start = offset + 1; |
961 | 0 | } |
962 | 0 | else if (c == ',') |
963 | 0 | { |
964 | 0 | if (tvb_reported_length(tvb) > offset && tvb_get_uint8(tvb, offset + 1) == ' ') |
965 | 0 | { |
966 | 0 | proto_tree_add_item(axia_lwcp_tree, hf_axia_lwcp_property, tvb, start, len, ENC_ASCII); |
967 | 0 | start = offset + 2; |
968 | 0 | offset++; |
969 | 0 | } |
970 | 0 | else |
971 | 0 | { |
972 | 0 | proto_tree_add_item(axia_lwcp_tree, hf_axia_lwcp_property, tvb, start, len, ENC_ASCII); |
973 | 0 | start = offset + 1; |
974 | 0 | } |
975 | 0 | } |
976 | 0 | } |
977 | 0 | offset++; |
978 | 0 | } |
979 | 0 | return tvb_reported_length(tvb); |
980 | 0 | } |
981 | | static int dissect_lwcp_tcp(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void *data) |
982 | 0 | { |
983 | 0 | tcp_dissect_pdus(tvb, pinfo, tree, true, 1, get_lwcp_pdu_len, dissect_lwcp, data); |
984 | 0 | return tvb_reported_length(tvb); |
985 | 0 | } |
986 | | static bool test_lwadv(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void *data) |
987 | 121 | { |
988 | 121 | if (cmp_address(&pinfo->net_dst, &advertisement_address)) |
989 | 121 | return false; |
990 | 0 | if (!validate_header(tvb)) |
991 | 0 | return false; |
992 | 0 | if (pinfo->destport != AXIA_ADV_PORT) |
993 | 0 | return false; |
994 | 0 | if (dissect_lwadv(tvb, pinfo, tree, data)) |
995 | 0 | { |
996 | 0 | conversation_t *conversation = find_or_create_conversation(pinfo); |
997 | 0 | conversation_set_dissector(conversation, axia_adv_handle); |
998 | 0 | return true; |
999 | 0 | } |
1000 | 0 | return false; |
1001 | 0 | } |
1002 | | static bool test_lwgpio(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void *data) |
1003 | 121 | { |
1004 | 121 | if (cmp_address(&pinfo->net_dst, &gpio_address)) |
1005 | 121 | return false; |
1006 | 0 | if (pinfo->destport != AXIA_GPIO_CONSOLE_PORT && pinfo->destport != AXIA_GPIO_NODE_PORT) |
1007 | 0 | return false; |
1008 | 0 | if (!validate_header(tvb)) |
1009 | 0 | return false; |
1010 | 0 | if (dissect_lwgpio(tvb, pinfo, tree, data)) |
1011 | 0 | { |
1012 | 0 | conversation_t *conversation = find_or_create_conversation(pinfo); |
1013 | 0 | conversation_set_dissector(conversation, axia_gpio_handle); |
1014 | 0 | return true; |
1015 | 0 | } |
1016 | 0 | return false; |
1017 | 0 | } |
1018 | | static bool test_lwcp(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void *data) |
1019 | 121 | { |
1020 | 121 | if (cmp_address(&pinfo->net_dst, &gpio_address)) |
1021 | 121 | return false; |
1022 | 0 | if (pinfo->destport != AXIA_LWCP_CONSOLE_PORT && pinfo->destport != AXIA_LWCP_MODULE_PORT) |
1023 | 0 | return false; |
1024 | 0 | if (dissect_lwcp(tvb, pinfo, tree, data)) |
1025 | 0 | { |
1026 | 0 | conversation_t *conversation = find_or_create_conversation(pinfo); |
1027 | 0 | conversation_set_dissector(conversation, lwcp_handle); |
1028 | 0 | return true; |
1029 | 0 | } |
1030 | 0 | return false; |
1031 | 0 | } |
1032 | | static bool test_lwclock(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void *data) |
1033 | 121 | { |
1034 | 121 | if (cmp_address(&pinfo->net_dst, &fast_clock_address) && cmp_address(&pinfo->net_dst, &slow_clock_address)) |
1035 | 121 | return false; |
1036 | 0 | if (!cmp_address(&pinfo->net_dst, &fast_clock_address) && pinfo->destport != AXIA_FAST_CLOCK_PORT) |
1037 | 0 | return false; |
1038 | 0 | if (!cmp_address(&pinfo->net_dst, &slow_clock_address) && pinfo->destport != AXIA_SLOW_CLOCK_PORT) |
1039 | 0 | return false; |
1040 | 0 | if (tvb_captured_length(tvb) != 36) /* RTP Header (12 bytes) + Extension profile and length (4 bytes) + 20 bytes of extension */ |
1041 | 0 | return false; |
1042 | 0 | if (tvb_get_uint8(tvb, 0) != AXIA_CLOCK_RTP_VERSION) /* RTP Version 2, no padding, with Extension and no CSRCs */ |
1043 | 0 | return false; |
1044 | 0 | if (tvb_get_uint8(tvb, 1) != AXIA_CLOCK_RTP_PAYLOAD_TYPE) /* RTP Payload Type = 127 + Mark bit */ |
1045 | 0 | return false; |
1046 | 0 | if (tvb_get_uint16(tvb, 12, ENC_BIG_ENDIAN) != AXIA_CLOCK_RTP_EXTENSION_PROFILE) /* RTP Extension Profile 0xfa1a */ |
1047 | 0 | return false; |
1048 | 0 | if (dissect_lwclock(tvb, pinfo, tree, data)) |
1049 | 0 | { |
1050 | 0 | conversation_t *conversation = find_or_create_conversation(pinfo); |
1051 | 0 | conversation_set_dissector(conversation, axia_clock_handle); |
1052 | 0 | return true; |
1053 | 0 | } |
1054 | 0 | return false; |
1055 | 0 | } |
1056 | | static bool test_lwintercom(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void *data) |
1057 | 121 | { |
1058 | 121 | if (pinfo->destport != AXIA_INTERCOM_PORT || cmp_address(&pinfo->net_dst, &intercom_address)) |
1059 | 121 | return false; |
1060 | 0 | if (dissect_intercom(tvb, pinfo, tree, data)) |
1061 | 0 | { |
1062 | 0 | conversation_t *conversation = find_or_create_conversation(pinfo); |
1063 | 0 | conversation_set_dissector(conversation, axia_intercom_handle); |
1064 | 0 | return true; |
1065 | 0 | } |
1066 | 0 | return false; |
1067 | 0 | } |
1068 | | void proto_register_lwadv(void) |
1069 | 16 | { |
1070 | 16 | expert_module_t* expert_livewire; |
1071 | 16 | static hf_register_info hf_adv[] = { |
1072 | 16 | {&hf_axia_magic_num, {"Axia Magic Number", "axia_adv.magic_number", FT_NONE, BASE_NONE, NULL, 0x0, NULL, HFILL}}, |
1073 | 16 | {&hf_axia_seq, {"Sequence", "axia_adv.seq", FT_UINT32, BASE_DEC, NULL, 0x0, NULL, HFILL}}, |
1074 | 16 | {&hf_axia_msg_count, {"Nested message count", "axia_adv.msgcount", FT_UINT8, BASE_DEC, NULL, 0x0, NULL, HFILL}}, |
1075 | 16 | {&hf_axia_pver, {"Protocol Version", "axia_adv.pver", FT_UINT16, BASE_DEC, NULL, 0x0, NULL, HFILL}}, |
1076 | 16 | {&hf_axia_advt, {"Advertisement type", "axia_adv.advt", FT_UINT8, BASE_HEX, VALS(advtypenames), 0x0, NULL, HFILL}}, |
1077 | 16 | {&hf_axia_unk_u8, {"Unknown Byte", "axia_adv.unknown_u8", FT_UINT8, BASE_HEX, NULL, 0x0, NULL, HFILL}}, |
1078 | 16 | {&hf_axia_unk_u16, {"Unknown Int", "axia_adv.unknown_u16", FT_UINT16, BASE_DEC, NULL, 0x0, NULL, HFILL}}, |
1079 | 16 | {&hf_axia_unk_u32, {"Unknown Int", "axia_adv.unknown_u32", FT_UINT32, BASE_DEC, NULL, 0x0, NULL, HFILL}}, |
1080 | 16 | {&hf_axia_unk_data, {"Unknown Data", "axia_adv.unknown_data", FT_BYTES, SEP_COLON, NULL, 0x0, NULL, HFILL}}, |
1081 | 16 | {&hf_axia_unk_str, {"Unknown String", "axia_adv.unknown_string", FT_STRING, BASE_NONE, NULL, 0x0, NULL, HFILL}}, |
1082 | 16 | {&hf_axia_opcode, {"Operation", "axia_adv.opcode", FT_STRING, BASE_NONE, NULL, 0x0, NULL, HFILL}}, |
1083 | 16 | {&hf_axia_term, {"Terminal Information", "axia_adv.term", FT_NONE, BASE_NONE, NULL, 0x0, NULL, HFILL}}, |
1084 | 16 | {&hf_axia_term_inip, {"IP Address", "axia_adv.term.inip", FT_IPv4, BASE_NONE, NULL, 0x0, NULL, HFILL}}, |
1085 | 16 | {&hf_axia_term_hwid, {"Hardware ID", "axia_adv.term.hwid", FT_UINT16, BASE_HEX, NULL, 0x0, NULL, HFILL}}, |
1086 | 16 | {&hf_axia_term_advv, {"Advertisement Version", "axia_adv.term.advv", FT_UINT32, BASE_DEC, NULL, 0x0, NULL, HFILL}}, |
1087 | 16 | {&hf_axia_term_udpc, {"UDP Port", "axia_adv.term.udpc", FT_UINT16, BASE_PT_UDP,NULL, 0x0, NULL, HFILL}}, |
1088 | 16 | {&hf_axia_term_nums, {"Number of Sources", "axia_adv.term.nums", FT_UINT16, BASE_DEC, NULL, 0x0, NULL, HFILL}}, |
1089 | 16 | {&hf_axia_term_atrn, {"Terminal Name", "axia_adv.term.atrn", FT_STRING, BASE_NONE, NULL, 0x0, NULL, HFILL}}, |
1090 | 16 | {&hf_axia_term_type, {"Type", "axia_adv.term.type", FT_STRING, BASE_NONE, NULL, 0x0, NULL, HFILL}}, |
1091 | | |
1092 | 16 | {&hf_axia_src, {"Source Information", "axia_adv.src", FT_NONE, BASE_NONE, NULL, 0x0, NULL, HFILL}}, |
1093 | 16 | {&hf_axia_src_psid, {"Livewire Source ID", "axia_adv.src.psid", FT_UINT32, BASE_DEC, NULL, 0x0, NULL, HFILL}}, |
1094 | 16 | {&hf_axia_src_shab, {"Sharable", "axia_adv.src.shab", FT_BOOLEAN, BASE_NONE, NULL, 0x0, NULL, HFILL}}, |
1095 | 16 | {&hf_axia_src_fsid, {"Multicast address", "axia_adv.src.fsid", FT_IPv4, BASE_NONE, NULL, 0x0, NULL, HFILL}}, |
1096 | 16 | {&hf_axia_src_bsid, {"Backfeed address", "axia_adv.src.bsid", FT_IPv4, BASE_NONE, NULL, 0x0, NULL, HFILL}}, |
1097 | 16 | {&hf_axia_src_psnm, {"Name", "axia_adv.src.psnm", FT_STRING, BASE_NONE, NULL, 0x0, NULL, HFILL}}, |
1098 | 16 | {&hf_axia_src_labl, {"Label", "axia_adv.src.labl", FT_STRING, BASE_NONE, NULL, 0x0, NULL, HFILL}}, |
1099 | 16 | {&hf_axia_src_lpid, {"Logic Port ID", "axia_adv.src.lpid", FT_UINT32, BASE_DEC, NULL, 0x0, NULL, HFILL}}, |
1100 | 16 | {&hf_axia_src_setup_frame, {"Setup Frame", "axia_adv.src.setup-frame", FT_FRAMENUM, BASE_NONE, NULL, 0x0, "First frame that advertised this source", HFILL}}, |
1101 | 16 | {&hf_axia_src_is_mm, {"Is Backfeed", "axia_adv.src.is-backfeed", FT_BOOLEAN, BASE_NONE, NULL, 0x0, "Is this source a backfeed from a console?", HFILL}}, |
1102 | | |
1103 | 16 | {&hf_axia_busy, {"Source Allocation", "axia_adv.busy", FT_NONE, BASE_NONE, NULL, 0x0, NULL, HFILL}}, |
1104 | 16 | {&hf_axia_busy_hwid, {"Console HWID", "axia_adv.busy.hwid", FT_UINT16, BASE_HEX, NULL, 0x0, NULL, HFILL}}, |
1105 | 16 | {&hf_axia_busy_fader, {"Fader", "axia_adv.busy.fader", FT_UINT8, BASE_DEC, NULL, 0x0, NULL, HFILL}}, |
1106 | 16 | {&hf_axia_busy_ip, {"Console IP Address", "axia_adv.busy.ip", FT_IPv4, BASE_NONE, NULL, 0x0, NULL, HFILL}}, |
1107 | 16 | {&hf_axia_busy_prefix, {"Console IP Prefix", "axia_adv.busy.prefix", FT_UINT16, BASE_HEX, NULL, 0x0, NULL, HFILL}}, |
1108 | 16 | }; |
1109 | 16 | static hf_register_info hf_gpio[] = { |
1110 | 16 | {&hf_axia_gpio_lcid, {"Logic Circuit ID", "axia_gpio.lcid", FT_UINT8, BASE_DEC, NULL, 0x0f, NULL, HFILL}}, |
1111 | 16 | {&hf_axia_gpio_state, {"Logic Circuit State", "axia_gpio.state", FT_UINT8, BASE_DEC, NULL, 0x40, NULL, HFILL}}, |
1112 | 16 | {&hf_axia_gpio_state2, {"Logic Circuit State", "axia_gpio.state", FT_UINT8, BASE_DEC, NULL, 0x01, NULL, HFILL}}, |
1113 | 16 | {&hf_axia_gpio_pmult, {"Pulse length multiplier", "axia_gpio.pulse_len_mult", FT_UINT8, BASE_DEC, NULL, 0x80, NULL, HFILL}}, |
1114 | 16 | {&hf_axia_gpio_plen, {"Pulse length", "axia_gpio.pulse_len", FT_UINT8, BASE_DEC, NULL, 0x3f, NULL, HFILL}}, |
1115 | 16 | }; |
1116 | 16 | static hf_register_info hf_clock[] ={ |
1117 | 16 | {&hf_axia_clock_hwid, {"Clock Hardware ID", "axia_clock.hwid", FT_UINT16, BASE_HEX, NULL, 0x0, NULL, HFILL}}, |
1118 | 16 | {&hf_axia_clock_prio, {"Priority", "axia_clock.priority", FT_UINT8, BASE_DEC, NULL, 0x0, NULL, HFILL}}, |
1119 | 16 | {&hf_axia_clock_mac, {"Clock MAC Address", "axia_clock.mac", FT_ETHER, BASE_NONE, NULL, 0x0, NULL, HFILL}}, |
1120 | 16 | {&hf_axia_clock_samp, {"RTP Timestamp", "axia_clock.rtp_ts", FT_UINT32, BASE_DEC, NULL, 0x0, NULL, HFILL}}, |
1121 | 16 | {&hf_axia_clock_fast, {"Timstamp in live packets", "axia_clock.fast", FT_UINT32, BASE_DEC, NULL, 0x0, NULL, HFILL}}, |
1122 | 16 | {&hf_axia_clock_seq, {"Sequence", "axia_clock.seq", FT_UINT16, BASE_DEC, NULL, 0x0, NULL, HFILL}}, |
1123 | 16 | {&hf_axia_clock_rate, {"Is Fast-Rate Clock", "axia_clock.rate", FT_BOOLEAN, BASE_NONE, NULL, 0x0, NULL, HFILL}}, |
1124 | 16 | {&hf_axia_clock_type, {"Clock message type", "axia_clock.type", FT_UINT8, BASE_HEX, VALS(clocktypenames), 0x0, NULL, HFILL}}, |
1125 | 16 | }; |
1126 | 16 | static hf_register_info hf_lwcp[] ={ |
1127 | 16 | {&hf_axia_lwcp_opcode, {"Operation", "axia_lwcp.opcode", FT_STRING, BASE_NONE, NULL, 0x0, NULL, HFILL}}, |
1128 | 16 | {&hf_axia_lwcp_object, {"Object", "axia_lwcp.object", FT_STRING, BASE_NONE, NULL, 0x0, NULL, HFILL}}, |
1129 | 16 | {&hf_axia_lwcp_property, {"Property", "axia_lwcp.property", FT_STRING, BASE_NONE, NULL, 0x0, NULL, HFILL}}, |
1130 | 16 | }; |
1131 | 16 | static ei_register_info ei[] = { |
1132 | 16 | { &ei_axia_clock_changed, {"axia_clock.masterchanged", PI_PROTOCOL, PI_WARN, "Livewire Master Clock Changed", EXPFILL }}, |
1133 | 16 | }; |
1134 | 16 | static int *ett[] = { |
1135 | 16 | &ett_axia_adv, |
1136 | 16 | &ett_axia_gpio, |
1137 | 16 | &ett_axia_clock, |
1138 | 16 | &ett_axia_lwcp, |
1139 | 16 | }; |
1140 | | |
1141 | 16 | uint32_t ip4_addr; |
1142 | 16 | str_to_ip(AXIA_FAST_CLOCK_ADDR, &ip4_addr); |
1143 | 16 | alloc_address_wmem(wmem_epan_scope(), &fast_clock_address, AT_IPv4, sizeof(uint32_t), &ip4_addr); |
1144 | 16 | str_to_ip(AXIA_SLOW_CLOCK_ADDR, &ip4_addr); |
1145 | 16 | alloc_address_wmem(wmem_epan_scope(), &slow_clock_address, AT_IPv4, sizeof(uint32_t), &ip4_addr); |
1146 | 16 | str_to_ip(AXIA_ADV_ADDR, &ip4_addr); |
1147 | 16 | alloc_address_wmem(wmem_epan_scope(), &advertisement_address, AT_IPv4, sizeof(uint32_t), &ip4_addr); |
1148 | 16 | str_to_ip(AXIA_GPIO_ADDR, &ip4_addr); |
1149 | 16 | alloc_address_wmem(wmem_epan_scope(), &gpio_address, AT_IPv4, sizeof(uint32_t), &ip4_addr); |
1150 | 16 | str_to_ip(AXIA_INTERCOM_ADDR, &ip4_addr); |
1151 | 16 | alloc_address_wmem(wmem_epan_scope(), &intercom_address, AT_IPv4, sizeof(uint32_t), &ip4_addr); |
1152 | | |
1153 | 16 | proto_axia_adv = proto_register_protocol("Livewire Source Advertisement", "LW Advertisement", "axia_adv"); |
1154 | 16 | proto_axia_gpio = proto_register_protocol("Livewire Multicast GPIO", "LW GPIO", "axia_gpio"); |
1155 | 16 | proto_axia_clock = proto_register_protocol("Livewire Clock", "LW Clock", "axia_clock"); |
1156 | 16 | proto_axia_intercom = proto_register_protocol("Telos Infinity Intercom", "Infinity Intercom", "axia_intercom"); |
1157 | 16 | proto_axia_lwcp = proto_register_protocol("Livewire Control Protocol", "LWCP", "axia_lwcp"); |
1158 | 16 | proto_register_field_array(proto_axia_adv, hf_adv, array_length(hf_adv)); |
1159 | 16 | proto_register_field_array(proto_axia_gpio, hf_gpio, array_length(hf_gpio)); |
1160 | 16 | proto_register_field_array(proto_axia_clock, hf_clock, array_length(hf_clock)); |
1161 | 16 | proto_register_field_array(proto_axia_lwcp, hf_lwcp, array_length(hf_lwcp)); |
1162 | 16 | proto_register_subtree_array(ett, array_length(ett)); |
1163 | | |
1164 | 16 | expert_livewire = expert_register_protocol(proto_axia_adv); |
1165 | 16 | expert_register_field_array(expert_livewire, ei, array_length(ei)); |
1166 | | |
1167 | 16 | axia_adv_handle = register_dissector("axia_adv", dissect_lwadv, proto_axia_adv); |
1168 | 16 | axia_gpio_handle = register_dissector("axia_gpio", dissect_lwgpio, proto_axia_gpio); |
1169 | 16 | axia_clock_handle = register_dissector("axia_clock", dissect_lwclock, proto_axia_clock); |
1170 | 16 | axia_intercom_handle = register_dissector("axia_intercom", dissect_intercom, proto_axia_intercom); |
1171 | 16 | lwcp_handle = register_dissector("axia_lwcp", dissect_lwcp, proto_axia_lwcp); |
1172 | 16 | lwcp_tcp_handle = register_dissector("axia_lwcp-tcp", dissect_lwcp_tcp, proto_axia_lwcp); |
1173 | 16 | } |
1174 | | void proto_reg_handoff_lwadv(void) |
1175 | 16 | { |
1176 | 16 | axia_sources = wmem_tree_new_autoreset(wmem_epan_scope(), wmem_file_scope()); |
1177 | 16 | axia_nodes = wmem_tree_new_autoreset(wmem_epan_scope(), wmem_file_scope()); |
1178 | 16 | heur_dissector_add("udp", test_lwadv, "Livewire Source Advertisement Heuristic Dissector", "axia_adv_heur", proto_axia_adv, HEURISTIC_ENABLE); |
1179 | 16 | heur_dissector_add("udp", test_lwgpio, "Livewire GPIO Heuristic Dissector", "axia_gpio_heur", proto_axia_gpio, HEURISTIC_ENABLE); |
1180 | 16 | heur_dissector_add("udp", test_lwclock, "Livewire Clock Heuristic Dissector", "axia_clock_heur", proto_axia_clock, HEURISTIC_ENABLE); |
1181 | 16 | heur_dissector_add("udp", test_lwintercom, "Telos Infinity Heuristic Dissector", "axia_intercom_heur", proto_axia_intercom, HEURISTIC_ENABLE); |
1182 | 16 | heur_dissector_add("udp", test_lwcp, "Livewire Control Protocol Heuristic Dissector", "axia_lwcp_heur", proto_axia_lwcp, HEURISTIC_ENABLE); |
1183 | 16 | dissector_add_for_decode_as("udp.port", axia_adv_handle); |
1184 | 16 | dissector_add_for_decode_as("udp.port", axia_gpio_handle); |
1185 | 16 | dissector_add_for_decode_as("udp.port", axia_clock_handle); |
1186 | 16 | dissector_add_for_decode_as("udp.port", axia_intercom_handle); |
1187 | 16 | dissector_add_for_decode_as("tcp.port", lwcp_tcp_handle); |
1188 | 16 | dissector_add_for_decode_as("udp.port", lwcp_handle); |
1189 | 16 | json_handle = find_dissector("json"); |
1190 | 16 | } |