Coverage Report

Created: 2025-08-04 07:15

/src/wireshark/epan/dissectors/packet-trdp.c
Line
Count
Source (jump to first uncovered line)
1
/* packet-trdp.c
2
 * Routines for TRDP dissection
3
 * Copyright 2020, EKE-Electronics Ltd, Kalle Pokki <kalle.pokki@eke.fi>
4
 *
5
 * Wireshark - Network traffic analyzer
6
 * By Gerald Combs <gerald@wireshark.org>
7
 * Copyright 1998 Gerald Combs
8
 *
9
 * SPDX-License-Identifier: GPL-2.0-or-later
10
 */
11
12
/*
13
 * The Train Real-Time Data Protocol (TRDP) is defined in IEC 61375-2-3. The
14
 * protocol is used to exchange Train Communication Network (TCN) process data
15
 * and message data.
16
 *
17
 * NOTE: Message data support incomplete.
18
 */
19
20
#include <config.h>
21
#include <epan/packet.h>
22
#include <packet-tcp.h>
23
24
void proto_reg_handoff_trdp(void);
25
void proto_register_trdp(void);
26
27
/* Initialize the protocol and registered fields */
28
static int proto_trdp;
29
static int hf_trdp_seq;
30
static int hf_trdp_ver;
31
static int hf_trdp_msgtype;
32
static int hf_trdp_comid;
33
static int hf_trdp_etb_topo;
34
static int hf_trdp_oper_topo;
35
static int hf_trdp_len;
36
static int hf_trdp_res;
37
static int hf_trdp_reply_comid;
38
static int hf_trdp_reply_ipaddr;
39
static int hf_trdp_header_fcs;
40
static int hf_trdp_padding;
41
static int hf_trdp_reply_status;
42
static int hf_trdp_session_id;
43
static int hf_trdp_reply_timeout;
44
static int hf_trdp_source_uri;
45
static int hf_trdp_dest_uri;
46
47
14
#define TRDP_PD_UDP_PORT 17224
48
28
#define TRDP_MD_TCP_UDP_PORT 17225
49
76
#define TRDP_PD_HEADER_LEN 40
50
242
#define TRDP_MD_HEADER_LEN 116
51
52
/* Initialize the subtree pointers */
53
static int ett_trdp;
54
55
/* Initialize dissector table */
56
static dissector_table_t trdp_dissector_table;
57
static dissector_handle_t data_handle;
58
59
/* Message type names */
60
static const value_string msgtype_names[] = {
61
    { 0x4d63, "Message Data Confirm" },
62
    { 0x4d65, "Message Data Error" },
63
    { 0x4d6e, "Message Data Notification (request without reply)" },
64
    { 0x4d70, "Message Data Reply without Confirmation" },
65
    { 0x4d71, "Message Data Reply with Confirmation" },
66
    { 0x4d72, "Message Data Request" },
67
    { 0x5064, "Process Data" },
68
    { 0x5065, "Process Data Error" },
69
    { 0x5070, "Process Data Reply" },
70
    { 0x5072, "Process Data Request" },
71
    { 0, NULL }
72
};
73
static const value_string msgtype_names_short[] = {
74
    { 0x4d63, "Mc" },
75
    { 0x4d65, "Me" },
76
    { 0x4d6e, "Mn" },
77
    { 0x4d70, "Mp" },
78
    { 0x4d71, "Mq" },
79
    { 0x4d72, "Mr" },
80
    { 0x5064, "Pd" },
81
    { 0x5065, "Pe" },
82
    { 0x5070, "Pp" },
83
    { 0x5072, "Pr" },
84
    { 0, NULL }
85
};
86
87
88
/* Communication identifier names */
89
static const value_string comid_names[] = {
90
    { 100, "Operational train directory status" },
91
    { 101, "Operational train directory notification" },
92
    { 106, "Train network directory information request" },
93
    { 107, "Train network directory information reply" },
94
    { 108, "Operational train directory information request" },
95
    { 109, "Operational train directory information reply" },
96
    { 120, "ECSP control telegram" },
97
    { 121, "ECSP status telegram" },
98
    { 132, "ETBN - Train network directory request" },
99
    { 133, "ETBN - Train network directory reply" },
100
    { 2204160, "EKE Modular I/O state" },
101
    { 2204161, "EKE Modular I/O control" },
102
    { 0, NULL }
103
};
104
105
/* Reply status indication names
106
 * Signed int: <0: NOK; 0: OK; >0: user reply status */
107
static const value_string reply_status_names[] = {
108
    { -1, "Reserved" },
109
    { -2, "Session abort" },
110
    { -3, "No replier instance (at replier side)" },
111
    { -4, "No memory (at replier side)" },
112
    { -5, "No memory (local)" },
113
    { -6, "No reply" },
114
    { -7, "Not all replies" },
115
    { -8, "No confirm" },
116
    { -9, "Reserved" },
117
    { -10, "Sending failed" },
118
    { 0, "Ok" },
119
    { 0, NULL }
120
};
121
122
static inline int is_pd(uint16_t msgtype)
123
146
{
124
146
    return (msgtype & 0xff00) == 0x5000; // 'P'
125
146
}
126
127
static int dissect_trdp(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void *data _U_)
128
74
{
129
74
    proto_item *ti;
130
74
    proto_tree *trdp_tree;
131
74
    uint16_t ver;
132
74
    uint32_t remaining, datalen, seq, comid, etb_topo, opr_topo, msgtype, header_len;
133
74
    tvbuff_t *next_tvb;
134
135
74
    if (tvb_reported_length(tvb) < TRDP_PD_HEADER_LEN)
136
0
        return 0;
137
138
74
    col_set_str(pinfo->cinfo, COL_PROTOCOL, "TRDP");
139
74
    col_clear(pinfo->cinfo, COL_INFO);
140
141
74
    header_len = is_pd(tvb_get_uint16(tvb, 6, ENC_BIG_ENDIAN)) ? TRDP_PD_HEADER_LEN : TRDP_MD_HEADER_LEN;
142
143
    /* Create display subtree for the protocol */
144
74
    ti = proto_tree_add_item(tree, proto_trdp, tvb, 0, header_len, ENC_NA);
145
74
    trdp_tree = proto_item_add_subtree(ti, ett_trdp);
146
147
    /* Add items to the subtree */
148
74
    proto_tree_add_item_ret_uint(trdp_tree, hf_trdp_seq, tvb, 0, 4, ENC_BIG_ENDIAN, &seq);
149
74
    ver = tvb_get_uint16(tvb, 4, ENC_BIG_ENDIAN);
150
74
    proto_tree_add_uint_format_value(trdp_tree, hf_trdp_ver, tvb, 4, 2, 0, "%d.%d", ver >> 8, ver & 0xff);
151
74
    proto_tree_add_item_ret_uint(trdp_tree, hf_trdp_msgtype, tvb, 6, 2, ENC_BIG_ENDIAN, &msgtype);
152
74
    proto_tree_add_item_ret_uint(trdp_tree, hf_trdp_comid, tvb, 8, 4, ENC_BIG_ENDIAN, &comid);
153
74
    proto_tree_add_item_ret_uint(trdp_tree, hf_trdp_etb_topo, tvb, 12, 4, ENC_BIG_ENDIAN, &etb_topo);
154
74
    proto_tree_add_item_ret_uint(trdp_tree, hf_trdp_oper_topo, tvb, 16, 4, ENC_BIG_ENDIAN, &opr_topo);
155
74
    proto_tree_add_item_ret_uint(trdp_tree, hf_trdp_len, tvb, 20, 4, ENC_BIG_ENDIAN, &datalen);
156
157
74
    if ( is_pd(msgtype) ) {
158
2
        proto_tree_add_item(trdp_tree, hf_trdp_res, tvb, 24, 4, ENC_BIG_ENDIAN);
159
2
        proto_tree_add_item(trdp_tree, hf_trdp_reply_comid, tvb, 28, 4, ENC_BIG_ENDIAN);
160
2
        proto_tree_add_item(trdp_tree, hf_trdp_reply_ipaddr, tvb, 32, 4, ENC_BIG_ENDIAN);
161
2
        proto_tree_add_item(trdp_tree, hf_trdp_header_fcs, tvb, 36, 4, ENC_BIG_ENDIAN);
162
72
    } else {
163
72
        proto_tree_add_item(trdp_tree, hf_trdp_reply_status, tvb, 24, 4, ENC_BIG_ENDIAN);
164
72
        proto_tree_add_item(trdp_tree, hf_trdp_session_id, tvb, 28, 16, ENC_BIG_ENDIAN);
165
72
        uint32_t reply_timeout = tvb_get_uint32(tvb, 44, ENC_BIG_ENDIAN);
166
72
        proto_tree_add_uint_format_value(trdp_tree, hf_trdp_reply_timeout, tvb, 44, 4, 0, "%d usec", reply_timeout);
167
72
        proto_tree_add_item(trdp_tree, hf_trdp_source_uri, tvb, 48, 32, ENC_ASCII);
168
72
        proto_tree_add_item(trdp_tree, hf_trdp_dest_uri, tvb, 80, 32, ENC_ASCII);
169
72
        proto_tree_add_item(trdp_tree, hf_trdp_header_fcs, tvb, 112, 4, ENC_BIG_ENDIAN);
170
72
    }
171
    /* Append descriptions */
172
74
    proto_item_append_text(ti, ", Type: %s, Comid: %d, Seq: %d, ETB Topo: 0x%08x, Opr Topo: 0x%08x", val_to_str(msgtype, msgtype_names_short, "0x%x"), comid, seq, etb_topo, opr_topo);
173
74
    col_add_fstr(pinfo->cinfo, COL_INFO, "Type=%s Comid=%d Seq=%d", val_to_str(msgtype, msgtype_names_short, "0x%x"), comid, seq);
174
175
    /* Extract possible padding */
176
74
    remaining = tvb_captured_length_remaining(tvb, header_len);
177
74
    if (remaining - datalen > 0)
178
43
    {
179
43
        proto_tree_add_item(trdp_tree, hf_trdp_padding, tvb, header_len+datalen, -1, ENC_NA);
180
43
        proto_tree_set_appendix(trdp_tree, tvb, header_len+datalen, remaining-datalen);
181
43
    }
182
183
    /* If this protocol has a sub-dissector call it here, see section 1.8 of
184
     * README.dissector for more information. */
185
186
74
    next_tvb = tvb_new_subset_length(tvb, header_len, datalen);
187
74
    if (!dissector_try_uint(trdp_dissector_table, comid, next_tvb, pinfo, tree))
188
20
    {
189
20
        call_dissector(data_handle, next_tvb, pinfo, tree);
190
20
    }
191
192
    /* Return the amount of data this dissector was able to dissect (which may
193
     * or may not be the total captured packet as we return here). */
194
74
    return tvb_captured_length(tvb);
195
74
}
196
197
static unsigned get_trdp_pdu_len(packet_info *pinfo _U_, tvbuff_t *tvb, int offset _U_, void *data _U_)
198
67
{
199
67
    uint32_t plen;
200
201
67
    plen = tvb_get_uint32(tvb, 20, ENC_BIG_ENDIAN);
202
203
67
    return TRDP_MD_HEADER_LEN + plen;
204
67
}
205
206
static int dissect_trdp_tcp(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void *data)
207
16
{
208
    /* Only Message Data over TCP */
209
16
    if (!tvb_bytes_exist(tvb, 0, TRDP_MD_HEADER_LEN))
210
3
        return 0;
211
212
13
    tcp_dissect_pdus(tvb, pinfo, tree, true, TRDP_MD_HEADER_LEN,
213
13
                     get_trdp_pdu_len, dissect_trdp, data);
214
13
    return tvb_reported_length(tvb);
215
16
}
216
217
void proto_register_trdp(void)
218
14
{
219
14
    static hf_register_info hf[] = {
220
        /* PD header */
221
14
        { &hf_trdp_seq,
222
14
          { "Sequence Counter", "trdp.seq", FT_UINT32, BASE_DEC, NULL, 0, NULL, HFILL }
223
14
        },
224
14
        { &hf_trdp_ver,
225
14
          { "Protocol Version", "trdp.ver", FT_UINT16, BASE_HEX, NULL, 0, NULL, HFILL }
226
14
        },
227
14
        { &hf_trdp_msgtype,
228
14
          { "Message Type", "trdp.msgtype", FT_UINT16, BASE_HEX, VALS(msgtype_names), 0, NULL, HFILL }
229
14
        },
230
14
        { &hf_trdp_comid,
231
14
          { "Communication Identifier", "trdp.comid", FT_UINT32, BASE_DEC, VALS(comid_names), 0, NULL, HFILL }
232
14
        },
233
14
        { &hf_trdp_etb_topo,
234
14
          { "ETB Topography Counter", "trdp.etb_topo", FT_UINT32, BASE_HEX, NULL, 0, NULL, HFILL }
235
14
        },
236
14
        { &hf_trdp_oper_topo,
237
14
          { "Operational Topography Counter", "trdp.oper_topo", FT_UINT32, BASE_HEX, NULL, 0, NULL, HFILL }
238
14
        },
239
14
        { &hf_trdp_len,
240
14
          { "Dataset Length", "trdp.len", FT_UINT32, BASE_DEC, NULL, 0, NULL, HFILL }
241
14
        },
242
14
        { &hf_trdp_res,
243
14
          { "Reserved", "trdp.res", FT_UINT32, BASE_DEC, NULL, 0, NULL, HFILL }
244
14
        },
245
14
        { &hf_trdp_reply_comid,
246
14
          { "Reply Communication Identifier", "trdp.reply_comid", FT_UINT32, BASE_DEC, VALS(comid_names), 0, NULL, HFILL }
247
14
        },
248
14
        { &hf_trdp_reply_ipaddr,
249
14
          { "Reply IP address", "trdp.reply_ipaddr", FT_IPv4, BASE_NONE, NULL, 0, NULL, HFILL }
250
14
        },
251
14
        { &hf_trdp_header_fcs,
252
14
          { "Header FCS", "trdp.fcs", FT_UINT32, BASE_HEX, NULL, 0, NULL, HFILL }
253
14
        },
254
14
        { &hf_trdp_padding,
255
14
          { "Padding", "trdp.padding", FT_BYTES, BASE_NONE, NULL, 0, NULL, HFILL }
256
14
        },
257
258
        /* MD Header */
259
14
        { &hf_trdp_reply_status,
260
14
          { "Reply Status Indication", "trdp.reply_status", FT_INT32, BASE_DEC, VALS(reply_status_names), 0, NULL, HFILL }
261
14
        },
262
14
        { &hf_trdp_session_id,
263
14
          { "Session UUID", "trdp.session_id", FT_GUID, BASE_NONE, NULL, 0, NULL, HFILL }
264
14
        },
265
14
        { &hf_trdp_reply_timeout,
266
14
          { "Reply Timeout", "trdp.reply_timeout", FT_UINT32, BASE_DEC, NULL, 0, NULL, HFILL }
267
14
        },
268
14
        { &hf_trdp_source_uri,
269
14
          { "Source URI", "trdp.source_uri", FT_STRINGZ, BASE_NONE, NULL, 0x0, NULL, HFILL }
270
14
        },
271
14
        { &hf_trdp_dest_uri,
272
14
          { "Destination URI", "trdp.dest_uri", FT_STRINGZ, BASE_NONE, NULL, 0x0, NULL, HFILL }
273
14
        }
274
14
    };
275
276
    /* Setup protocol subtree array */
277
14
    static int *ett[] = {
278
14
        &ett_trdp
279
14
    };
280
281
    /* Register the protocol name and description */
282
14
    proto_trdp = proto_register_protocol("Train Realtime Data Protocol", "TRDP", "trdp");
283
284
    /* Required function calls to register the header fields and subtrees */
285
14
    proto_register_field_array(proto_trdp, hf, array_length(hf));
286
14
    proto_register_subtree_array(ett, array_length(ett));
287
288
    /* Register next dissector */
289
14
    trdp_dissector_table = register_dissector_table("trdp.comid", "comid", proto_trdp, FT_UINT32, BASE_DEC);
290
14
}
291
292
void proto_reg_handoff_trdp(void)
293
14
{
294
14
    static dissector_handle_t trdp_handle, trdp_tcp_handle;
295
296
14
    trdp_handle = create_dissector_handle(dissect_trdp, proto_trdp);
297
14
    trdp_tcp_handle = create_dissector_handle(dissect_trdp_tcp, proto_trdp);
298
14
    dissector_add_uint("udp.port", TRDP_PD_UDP_PORT, trdp_handle);
299
14
    dissector_add_uint("udp.port", TRDP_MD_TCP_UDP_PORT, trdp_handle);
300
14
    dissector_add_uint("tcp.port", TRDP_MD_TCP_UDP_PORT, trdp_tcp_handle);
301
302
14
    data_handle = find_dissector_add_dependency("data", proto_trdp);
303
14
}
304
305
306
/*
307
 * Editor modelines  -  https://www.wireshark.org/tools/modelines.html
308
 *
309
 * Local variables:
310
 * c-basic-offset: 4
311
 * tab-width: 8
312
 * indent-tabs-mode: nil
313
 * End:
314
 *
315
 * vi: set shiftwidth=4 tabstop=8 expandtab:
316
 * :indentSize=4:tabSize=8:noTabs=true:
317
 */