/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 | | */ |