2 * Routines for TRDP dissection
3 * Copyright 2020, EKE-Electronics Ltd, Kalle Pokki <kalle.pokki@eke.fi>
5 * Wireshark - Network traffic analyzer
6 * By Gerald Combs <gerald@wireshark.org>
7 * Copyright 1998 Gerald Combs
9 * SPDX-License-Identifier: GPL-2.0-or-later
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
17 * NOTE: Message data support incomplete.
21 #include <epan/packet.h>
22 #include <packet-tcp.h>
24 void proto_reg_handoff_trdp(void);
25 void proto_register_trdp(void);
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
;
47 #define TRDP_PD_UDP_PORT 17224
48 #define TRDP_MD_TCP_UDP_PORT 17225
49 #define TRDP_PD_HEADER_LEN 40
50 #define TRDP_MD_HEADER_LEN 116
52 /* Initialize the subtree pointers */
55 /* Initialize dissector table */
56 static dissector_table_t trdp_dissector_table
;
57 static dissector_handle_t data_handle
;
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" },
73 static const value_string msgtype_names_short
[] = {
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" },
105 /* Reply status indication names
106 * Signed int: <0: NOK; 0: OK; >0: user reply status */
107 static const value_string reply_status_names
[] = {
109 { -2, "Session abort" },
110 { -3, "No replier instance (at replier side)" },
111 { -4, "No memory (at replier side)" },
112 { -5, "No memory (local)" },
114 { -7, "Not all replies" },
115 { -8, "No confirm" },
117 { -10, "Sending failed" },
122 static inline int is_pd(uint16_t msgtype
)
124 return (msgtype
& 0xff00) == 0x5000; // 'P'
127 static int dissect_trdp(tvbuff_t
*tvb
, packet_info
*pinfo
, proto_tree
*tree
, void *data _U_
)
130 proto_tree
*trdp_tree
;
132 uint32_t remaining
, datalen
, seq
, comid
, etb_topo
, opr_topo
, msgtype
, header_len
;
135 if (tvb_reported_length(tvb
) < TRDP_PD_HEADER_LEN
)
138 col_set_str(pinfo
->cinfo
, COL_PROTOCOL
, "TRDP");
139 col_clear(pinfo
->cinfo
, COL_INFO
);
141 header_len
= is_pd(tvb_get_uint16(tvb
, 6, ENC_BIG_ENDIAN
)) ? TRDP_PD_HEADER_LEN
: TRDP_MD_HEADER_LEN
;
143 /* Create display subtree for the protocol */
144 ti
= proto_tree_add_item(tree
, proto_trdp
, tvb
, 0, header_len
, ENC_NA
);
145 trdp_tree
= proto_item_add_subtree(ti
, ett_trdp
);
147 /* Add items to the subtree */
148 proto_tree_add_item_ret_uint(trdp_tree
, hf_trdp_seq
, tvb
, 0, 4, ENC_BIG_ENDIAN
, &seq
);
149 ver
= tvb_get_uint16(tvb
, 4, ENC_BIG_ENDIAN
);
150 proto_tree_add_uint_format_value(trdp_tree
, hf_trdp_ver
, tvb
, 4, 2, 0, "%d.%d", ver
>> 8, ver
& 0xff);
151 proto_tree_add_item_ret_uint(trdp_tree
, hf_trdp_msgtype
, tvb
, 6, 2, ENC_BIG_ENDIAN
, &msgtype
);
152 proto_tree_add_item_ret_uint(trdp_tree
, hf_trdp_comid
, tvb
, 8, 4, ENC_BIG_ENDIAN
, &comid
);
153 proto_tree_add_item_ret_uint(trdp_tree
, hf_trdp_etb_topo
, tvb
, 12, 4, ENC_BIG_ENDIAN
, &etb_topo
);
154 proto_tree_add_item_ret_uint(trdp_tree
, hf_trdp_oper_topo
, tvb
, 16, 4, ENC_BIG_ENDIAN
, &opr_topo
);
155 proto_tree_add_item_ret_uint(trdp_tree
, hf_trdp_len
, tvb
, 20, 4, ENC_BIG_ENDIAN
, &datalen
);
157 if ( is_pd(msgtype
) ) {
158 proto_tree_add_item(trdp_tree
, hf_trdp_res
, tvb
, 24, 4, ENC_BIG_ENDIAN
);
159 proto_tree_add_item(trdp_tree
, hf_trdp_reply_comid
, tvb
, 28, 4, ENC_BIG_ENDIAN
);
160 proto_tree_add_item(trdp_tree
, hf_trdp_reply_ipaddr
, tvb
, 32, 4, ENC_BIG_ENDIAN
);
161 proto_tree_add_item(trdp_tree
, hf_trdp_header_fcs
, tvb
, 36, 4, ENC_BIG_ENDIAN
);
163 proto_tree_add_item(trdp_tree
, hf_trdp_reply_status
, tvb
, 24, 4, ENC_BIG_ENDIAN
);
164 proto_tree_add_item(trdp_tree
, hf_trdp_session_id
, tvb
, 28, 16, ENC_BIG_ENDIAN
);
165 uint32_t reply_timeout
= tvb_get_uint32(tvb
, 44, ENC_BIG_ENDIAN
);
166 proto_tree_add_uint_format_value(trdp_tree
, hf_trdp_reply_timeout
, tvb
, 44, 4, 0, "%d usec", reply_timeout
);
167 proto_tree_add_item(trdp_tree
, hf_trdp_source_uri
, tvb
, 48, 32, ENC_ASCII
);
168 proto_tree_add_item(trdp_tree
, hf_trdp_dest_uri
, tvb
, 80, 32, ENC_ASCII
);
169 proto_tree_add_item(trdp_tree
, hf_trdp_header_fcs
, tvb
, 112, 4, ENC_BIG_ENDIAN
);
171 /* Append descriptions */
172 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 col_add_fstr(pinfo
->cinfo
, COL_INFO
, "Type=%s Comid=%d Seq=%d", val_to_str(msgtype
, msgtype_names_short
, "0x%x"), comid
, seq
);
175 /* Extract possible padding */
176 remaining
= tvb_captured_length_remaining(tvb
, header_len
);
177 if (remaining
- datalen
> 0)
179 proto_tree_add_item(trdp_tree
, hf_trdp_padding
, tvb
, header_len
+datalen
, -1, ENC_NA
);
180 proto_tree_set_appendix(trdp_tree
, tvb
, header_len
+datalen
, remaining
-datalen
);
183 /* If this protocol has a sub-dissector call it here, see section 1.8 of
184 * README.dissector for more information. */
186 next_tvb
= tvb_new_subset_length(tvb
, header_len
, datalen
);
187 if (!dissector_try_uint(trdp_dissector_table
, comid
, next_tvb
, pinfo
, tree
))
189 call_dissector(data_handle
, next_tvb
, pinfo
, tree
);
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 return tvb_captured_length(tvb
);
197 static unsigned get_trdp_pdu_len(packet_info
*pinfo _U_
, tvbuff_t
*tvb
, int offset _U_
, void *data _U_
)
201 plen
= tvb_get_uint32(tvb
, 20, ENC_BIG_ENDIAN
);
203 return TRDP_MD_HEADER_LEN
+ plen
;
206 static int dissect_trdp_tcp(tvbuff_t
*tvb
, packet_info
*pinfo
, proto_tree
*tree
, void *data
)
208 /* Only Message Data over TCP */
209 if (!tvb_bytes_exist(tvb
, 0, TRDP_MD_HEADER_LEN
))
212 tcp_dissect_pdus(tvb
, pinfo
, tree
, true, TRDP_MD_HEADER_LEN
,
213 get_trdp_pdu_len
, dissect_trdp
, data
);
214 return tvb_reported_length(tvb
);
217 void proto_register_trdp(void)
219 static hf_register_info hf
[] = {
222 { "Sequence Counter", "trdp.seq", FT_UINT32
, BASE_DEC
, NULL
, 0, NULL
, HFILL
}
225 { "Protocol Version", "trdp.ver", FT_UINT16
, BASE_HEX
, NULL
, 0, NULL
, HFILL
}
228 { "Message Type", "trdp.msgtype", FT_UINT16
, BASE_HEX
, VALS(msgtype_names
), 0, NULL
, HFILL
}
231 { "Communication Identifier", "trdp.comid", FT_UINT32
, BASE_DEC
, VALS(comid_names
), 0, NULL
, HFILL
}
234 { "ETB Topography Counter", "trdp.etb_topo", FT_UINT32
, BASE_HEX
, NULL
, 0, NULL
, HFILL
}
236 { &hf_trdp_oper_topo
,
237 { "Operational Topography Counter", "trdp.oper_topo", FT_UINT32
, BASE_HEX
, NULL
, 0, NULL
, HFILL
}
240 { "Dataset Length", "trdp.len", FT_UINT32
, BASE_DEC
, NULL
, 0, NULL
, HFILL
}
243 { "Reserved", "trdp.res", FT_UINT32
, BASE_DEC
, NULL
, 0, NULL
, HFILL
}
245 { &hf_trdp_reply_comid
,
246 { "Reply Communication Identifier", "trdp.reply_comid", FT_UINT32
, BASE_DEC
, VALS(comid_names
), 0, NULL
, HFILL
}
248 { &hf_trdp_reply_ipaddr
,
249 { "Reply IP address", "trdp.reply_ipaddr", FT_IPv4
, BASE_NONE
, NULL
, 0, NULL
, HFILL
}
251 { &hf_trdp_header_fcs
,
252 { "Header FCS", "trdp.fcs", FT_UINT32
, BASE_HEX
, NULL
, 0, NULL
, HFILL
}
255 { "Padding", "trdp.padding", FT_BYTES
, BASE_NONE
, NULL
, 0, NULL
, HFILL
}
259 { &hf_trdp_reply_status
,
260 { "Reply Status Indication", "trdp.reply_status", FT_INT32
, BASE_DEC
, VALS(reply_status_names
), 0, NULL
, HFILL
}
262 { &hf_trdp_session_id
,
263 { "Session UUID", "trdp.session_id", FT_GUID
, BASE_NONE
, NULL
, 0, NULL
, HFILL
}
265 { &hf_trdp_reply_timeout
,
266 { "Reply Timeout", "trdp.reply_timeout", FT_UINT32
, BASE_DEC
, NULL
, 0, NULL
, HFILL
}
268 { &hf_trdp_source_uri
,
269 { "Source URI", "trdp.source_uri", FT_STRINGZ
, BASE_NONE
, NULL
, 0x0, NULL
, HFILL
}
272 { "Destination URI", "trdp.dest_uri", FT_STRINGZ
, BASE_NONE
, NULL
, 0x0, NULL
, HFILL
}
276 /* Setup protocol subtree array */
277 static int *ett
[] = {
281 /* Register the protocol name and description */
282 proto_trdp
= proto_register_protocol("Train Realtime Data Protocol", "TRDP", "trdp");
284 /* Required function calls to register the header fields and subtrees */
285 proto_register_field_array(proto_trdp
, hf
, array_length(hf
));
286 proto_register_subtree_array(ett
, array_length(ett
));
288 /* Register next dissector */
289 trdp_dissector_table
= register_dissector_table("trdp.comid", "comid", proto_trdp
, FT_UINT32
, BASE_DEC
);
292 void proto_reg_handoff_trdp(void)
294 static dissector_handle_t trdp_handle
, trdp_tcp_handle
;
296 trdp_handle
= create_dissector_handle(dissect_trdp
, proto_trdp
);
297 trdp_tcp_handle
= create_dissector_handle(dissect_trdp_tcp
, proto_trdp
);
298 dissector_add_uint("udp.port", TRDP_PD_UDP_PORT
, trdp_handle
);
299 dissector_add_uint("udp.port", TRDP_MD_TCP_UDP_PORT
, trdp_handle
);
300 dissector_add_uint("tcp.port", TRDP_MD_TCP_UDP_PORT
, trdp_tcp_handle
);
302 data_handle
= find_dissector_add_dependency("data", proto_trdp
);
307 * Editor modelines - https://www.wireshark.org/tools/modelines.html
312 * indent-tabs-mode: nil
315 * vi: set shiftwidth=4 tabstop=8 expandtab:
316 * :indentSize=4:tabSize=8:noTabs=true: