Coverage Report

Created: 2026-11-12 07:01

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/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(), &current_clock_mac, AT_ETHER, sizeof(mac_address), mac_address);
796
0
    if (cmp_address(&current_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(), &current_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
}