1 /* packet-opensafety.c
2 *
3 * openSAFETY is a machine-safety protocol, encapsulated in modern fieldbus
4 * and industrial ethernet solutions.
5 *
6 * For more information see http://www.open-safety.org
7 *
8 * This dissector currently supports the following transport protocols
9 *
10 * - openSAFETY using POWERLINK
11 * - openSAFETY using SercosIII
12 * - openSAFETY using Generic UDP
13 * - openSAFETY using Modbus/TCP
14 * - openSAFETY using (openSAFETY over UDP) transport
15 * - openSAFETY using ProfiNet IO
16 *
17 * By Roland Knall <roland.knall@br-automation.com>
18 * Copyright 2011-2012 Bernecker + Rainer Industrie-Elektronik Ges.m.b.H.
19 *
20 * Wireshark - Network traffic analyzer
21 * By Gerald Combs <gerald@wireshark.org>
22 * Copyright 1998 Gerald Combs
23 *
24 * SPDX-License-Identifier: GPL-2.0-or-later
25 */
26
27 #include "config.h"
28
29 #include <epan/packet.h>
30 #include <epan/prefs.h>
31 #include <epan/etypes.h>
32 #include <epan/expert.h>
33 #include <epan/reassemble.h>
34 #include <epan/strutil.h>
35 #include <epan/tap.h>
36
37 #include <wsutil/crc8.h>
38 #include <wsutil/crc16.h>
39
40 #include "packet-frame.h"
41 #include "packet-opensafety.h"
42
43 /* General definitions */
44
45 /* Used to clasify incoming traffic and presort the heuristic */
46 #define OPENSAFETY_ANY_TRANSPORT 0x00
47 #define OPENSAFETY_CYCLIC_DATA 0x01
48 #define OPENSAFETY_ACYCLIC_DATA 0x02
49
50 #ifndef OPENSAFETY_PINFO_CONST_DATA
51 #define OPENSAFETY_PINFO_CONST_DATA 0xAABBCCDD
52 #endif
53
54 #define OPENSAFETY_REQUEST TRUE
55 #define OPENSAFETY_RESPONSE FALSE
56
57 /* SPDO Feature Flags
58 * Because featureflags are part of the TR field (which is only 6 bit), the field get's shifted */
59 #define OPENSAFETY_SPDO_FEAT_40BIT_AVAIL 0x20
60 #define OPENSAFETY_SPDO_FEAT_40BIT_USED 0x10
61 #define OPENSAFETY_SPDO_FEATURE_FLAGS (OPENSAFETY_SPDO_FEAT_40BIT_USED | OPENSAFETY_SPDO_FEAT_40BIT_AVAIL)
62
63 #define OSS_FRAME_POS_ADDR 0
64 #define OSS_FRAME_POS_ID 1
65 #define OSS_FRAME_POS_LEN 2
66 #define OSS_FRAME_POS_CT 3
67 #define OSS_FRAME_POS_DATA 4
68
69 #define OSS_PAYLOAD_MAXSIZE_FOR_CRC8 0x08
70 #define OSS_SLIM_FRAME_WITH_CRC8_MAXSIZE 0x13 /* 19 */
71 #define OSS_SLIM_FRAME2_WITH_CRC8 0x06 /* 6 */
72 #define OSS_SLIM_FRAME2_WITH_CRC16 0x07 /* 7 */
73 #define OSS_MINIMUM_LENGTH 0x0b /* 11 */
74
75 #define OPENSAFETY_SPDO_CONNECTION_VALID 0x04
76
77 #define OPENSAFETY_SOD_DVI 0x1018
78 #define OPENSAFETY_SOD_RXMAP 0x1800
79 #define OPENSAFETY_SOD_TXMAP 0xC000
80
81 #define OSS_FRAME_ADDR(f, offset) (f[OSS_FRAME_POS_ADDR + offset] + ((guint8)((f[OSS_FRAME_POS_ADDR + offset + 1]) << 6) << 2))
82 #define OSS_FRAME_ID(f, offset) (f[OSS_FRAME_POS_ID + offset] & 0xFC )
83 #define OSS_FRAME_LENGTH(f, offset) (f[OSS_FRAME_POS_LEN + offset])
84 #define OSS_FRAME_FIELD(f, position) (f[position])
85
86 #define OSS_FRAME_ADDR_T(f, offset) (tvb_get_guint8(f, OSS_FRAME_POS_ADDR + offset) + ((guint8)((tvb_get_guint8( f, OSS_FRAME_POS_ADDR + offset + 1)) << 6) << 2))
87 #define OSS_FRAME_ADDR_T2(f, offset, su1, su2) (( tvb_get_guint8(f, OSS_FRAME_POS_ADDR + offset) ^ su1) + ((guint8)(((tvb_get_guint8( f, OSS_FRAME_POS_ADDR + offset + 1) ^ su2)) << 6) << 2))
88 #define OSS_FRAME_ID_T(f, offset) (tvb_get_guint8(f, OSS_FRAME_POS_ID + offset) & 0xFC)
89 #define OSS_FRAME_LENGTH_T(f, offset) (tvb_get_guint8(f, OSS_FRAME_POS_LEN + offset))
90
91 static int proto_opensafety = -1;
92
93 static gint ett_opensafety = -1;
94 static gint ett_opensafety_checksum = -1;
95 static gint ett_opensafety_snmt = -1;
96 static gint ett_opensafety_ssdo = -1;
97 static gint ett_opensafety_spdo = -1;
98 static gint ett_opensafety_spdo_flags = -1;
99 static gint ett_opensafety_ssdo_sacmd = -1;
100 static gint ett_opensafety_ssdo_payload = -1;
101 static gint ett_opensafety_ssdo_sodentry = -1;
102 static gint ett_opensafety_ssdo_extpar = -1;
103 static gint ett_opensafety_sod_mapping = -1;
104 static gint ett_opensafety_node = -1;
105
106 static expert_field ei_payload_length_not_positive = EI_INIT;
107 static expert_field ei_payload_unknown_format = EI_INIT;
108 static expert_field ei_crc_slimssdo_instead_of_spdo = EI_INIT;
109 static expert_field ei_crc_frame_1_invalid = EI_INIT;
110 static expert_field ei_crc_frame_1_valid_frame2_invalid = EI_INIT;
111 static expert_field ei_crc_frame_2_invalid = EI_INIT;
112 static expert_field ei_crc_frame_2_unknown_scm_udid = EI_INIT;
113 static expert_field ei_crc_frame_2_scm_udid_encoded = EI_INIT;
114 static expert_field ei_message_unknown_type = EI_INIT;
115 static expert_field ei_message_reassembly_size_differs_from_header = EI_INIT;
116 static expert_field ei_message_spdo_address_invalid = EI_INIT;
117 static expert_field ei_message_id_field_mismatch = EI_INIT;
118 static expert_field ei_scmudid_autodetected = EI_INIT;
119 static expert_field ei_scmudid_invalid_preference = EI_INIT;
120 static expert_field ei_scmudid_unknown = EI_INIT;
121 static expert_field ei_40bit_default_domain = EI_INIT;
122
123 static int hf_oss_msg = -1;
124 static int hf_oss_msg_direction = -1;
125 static int hf_oss_msg_category = -1;
126 static int hf_oss_msg_node = -1;
127 static int hf_oss_msg_network = -1;
128 static int hf_oss_msg_sender = -1;
129 static int hf_oss_msg_receiver = -1;
130 static int hf_oss_length= -1;
131 static int hf_oss_crc = -1;
132 static int hf_oss_byte_offset = -1;
133
134 static int hf_oss_crc_valid = -1;
135 static int hf_oss_crc2_valid = -1;
136 static int hf_oss_crc_type = -1;
137
138 static int hf_oss_snmt_slave = -1;
139 static int hf_oss_snmt_master = -1;
140 static int hf_oss_snmt_udid = -1;
141 static int hf_oss_snmt_scm = -1;
142 static int hf_oss_snmt_tool = -1;
143 static int hf_oss_snmt_service_id = -1;
144 static int hf_oss_snmt_error_group = -1;
145 static int hf_oss_snmt_error_code = -1;
146 static int hf_oss_snmt_param_type = -1;
147 static int hf_oss_snmt_ext_addsaddr = -1;
148 static int hf_oss_snmt_ext_addtxspdo = -1;
149 static int hf_oss_snmt_ext_initct = -1;
150
151 static int hf_oss_ssdo_server = -1;
152 static int hf_oss_ssdo_client = -1;
153 static int hf_oss_ssdo_sano = -1;
154 static int hf_oss_ssdo_sacmd = -1;
155 static int hf_oss_ssdo_sod_index = -1;
156 static int hf_oss_ssdo_sod_subindex = -1;
157 static int hf_oss_ssdo_payload = -1;
158 static int hf_oss_ssdo_payload_size = -1;
159 static int hf_oss_ssdo_sodentry_size = -1;
160 static int hf_oss_ssdo_sodentry_data = -1;
161 static int hf_oss_ssdo_abort_code = -1;
162 static int hf_oss_ssdo_preload_queue = -1;
163 static int hf_oss_ssdo_preload_error = -1;
164
165 static int hf_oss_sod_par_timestamp = -1;
166 static int hf_oss_sod_par_checksum = -1;
167 static int hf_oss_ssdo_sodmapping = -1;
168 static int hf_oss_ssdo_sodmapping_bits = -1;
169
170 static int hf_oss_ssdo_sacmd_access_type = -1;
171 static int hf_oss_ssdo_sacmd_preload = -1;
172 static int hf_oss_ssdo_sacmd_abort_transfer = -1;
173 static int hf_oss_ssdo_sacmd_segmentation = -1;
174 static int hf_oss_ssdo_sacmd_toggle = -1;
175 static int hf_oss_ssdo_sacmd_initiate = -1;
176 static int hf_oss_ssdo_sacmd_end_segment = -1;
177 #if 0
178 static int hf_oss_ssdo_sacmd_reserved = -1;
179 #endif
180
181 static int hf_oss_ssdo_extpar_parset = -1;
182 static int hf_oss_ssdo_extpar_version = -1;
183 static int hf_oss_ssdo_extpar_saddr = -1;
184 static int hf_oss_ssdo_extpar_length = -1;
185 static int hf_oss_ssdo_extpar_crc = -1;
186 static int hf_oss_ssdo_extpar_tstamp = -1;
187 static int hf_oss_ssdo_extpar_data = -1;
188 static int hf_oss_ssdo_extpar = -1;
189
190 static int hf_oss_scm_udid = -1;
191 static int hf_oss_scm_udid_auto = -1;
192 static int hf_oss_scm_udid_valid = -1;
193
194 static int hf_oss_spdo_direction = -1;
195 static int hf_oss_spdo_connection_valid = -1;
196 static int hf_oss_spdo_ct = -1;
197 static int hf_oss_spdo_ct_40bit = -1;
198 static int hf_oss_spdo_time_request = -1;
199 static int hf_oss_spdo_time_request_to = -1;
200 static int hf_oss_spdo_time_request_from = -1;
201 static int hf_oss_spdo_feature_flags = -1;
202 static int hf_oss_spdo_feature_flag_40bit_available = -1;
203 static int hf_oss_spdo_feature_flag_40bit_used = -1;
204
205 static int hf_oss_fragments = -1;
206 static int hf_oss_fragment = -1;
207 static int hf_oss_fragment_overlap = -1;
208 static int hf_oss_fragment_overlap_conflicts = -1;
209 static int hf_oss_fragment_multiple_tails = -1;
210 static int hf_oss_fragment_too_long_fragment = -1;
211 static int hf_oss_fragment_error = -1;
212 static int hf_oss_fragment_count = -1;
213 static int hf_oss_reassembled_in = -1;
214 static int hf_oss_reassembled_length = -1;
215 static int hf_oss_reassembled_data = -1;
216
217 static gint ett_opensafety_ssdo_fragment = -1;
218 static gint ett_opensafety_ssdo_fragments = -1;
219
220 /* Definitions for the openSAFETY ov. UDP transport protocol */
221 static dissector_handle_t opensafety_udptransport_handle = NULL;
222
223 static int proto_oss_udp_transport = -1;
224
225 static int hf_oss_udp_transport_version = -1;
226 static int hf_oss_udp_transport_flags_type = -1;
227 static int hf_oss_udp_transport_counter = -1;
228 static int hf_oss_udp_transport_sender = -1;
229 static int hf_oss_udp_transport_datapoint = -1;
230 static int hf_oss_udp_transport_length= -1;
231
232 static gint ett_oss_udp_transport = -1;
233
234 static const true_false_string tfs_udp_transport_cyclic_acyclic = { "Cyclic", "ACyclic" };
235 static guint global_network_oss_udp_port = OPENSAFETY_UDP_PORT;
236
237 static int opensafety_tap = -1;
238
239 static const fragment_items oss_frag_items = {
240 /* Fragment subtrees */
241 &ett_opensafety_ssdo_fragment,
242 &ett_opensafety_ssdo_fragments,
243 /* Fragment fields */
244 &hf_oss_fragments,
245 &hf_oss_fragment,
246 &hf_oss_fragment_overlap,
247 &hf_oss_fragment_overlap_conflicts,
248 &hf_oss_fragment_multiple_tails,
249 &hf_oss_fragment_too_long_fragment,
250 &hf_oss_fragment_error,
251 &hf_oss_fragment_count,
252 /* Reassembled in field */
253 &hf_oss_reassembled_in,
254 /* Reassembled length field */
255 &hf_oss_reassembled_length,
256 /* Reassembled data */
257 &hf_oss_reassembled_data,
258 /* Tag */
259 "Message fragments"
260 };
261
262 static const char *global_scm_udid = "00:00:00:00:00:00";
263
264 static dissector_handle_t data_dissector = NULL;
265 static dissector_handle_t opensafety_udpdata_handle = NULL;
266 static dissector_handle_t opensafety_mbtcp_handle = NULL;
267 static dissector_handle_t opensafety_pnio_handle = NULL;
268
269 static gboolean global_display_intergap_data = FALSE;
270 static gboolean global_scm_udid_autoset = TRUE;
271 static gboolean global_udp_frame2_first = FALSE;
272 static gboolean global_siii_udp_frame2_first = FALSE;
273 static gboolean global_mbtcp_big_endian = FALSE;
274 static guint global_network_udp_port = OPENSAFETY_UDP_PORT;
275 static guint global_network_udp_port_sercosiii = OPENSAFETY_UDP_PORT_SIII;
276 static gboolean global_classify_transport = TRUE;
277
278 static gboolean global_enable_udp = TRUE;
279 static gboolean global_enable_mbtcp = TRUE;
280
281 static gboolean global_opensafety_debug_verbose = FALSE;
282
283 static const char * global_filter_nodes = "";
284 static gboolean global_show_only_node_in_filter = TRUE;
285 static wmem_list_t * global_filter_list = NULL;
286
287 static gboolean heuristic_siii_dissection_enabled = TRUE;
288
289 static heur_dissector_list_t heur_opensafety_spdo_subdissector_list;
290
291 static gboolean bDissector_Called_Once_Before = FALSE;
292 /* Using local_scm_udid as read variable for global_scm_udid, to
293 * enable automatic detection of scm udid */
294 static char *local_scm_udid = NULL;
295
296 static reassembly_table os_reassembly_table;
297
298 /* Resets the dissector in case the dissection is malformed and the dissector crashes */
299 static void
reset_dissector(void)300 reset_dissector(void)
301 {
302 bDissector_Called_Once_Before = FALSE;
303 }
304
305 static void
setup_dissector(void)306 setup_dissector(void)
307 {
308 heur_dtbl_entry_t * heur_entry = NULL;
309
310 /* create list if it does not exist, but clean existing elements anyway,
311 * as options might have changed */
312 global_filter_list = wmem_list_new(wmem_file_scope());
313
314 gchar ** vector = wmem_strsplit(wmem_file_scope(), global_filter_nodes, ",", -1);
315 for (; NULL != *vector; vector++ )
316 {
317 if ( *vector && g_ascii_strtoll(*vector, NULL, 10) > 0 )
318 wmem_list_append(global_filter_list, GINT_TO_POINTER(g_ascii_strtoll(*vector, NULL, 10)));
319 }
320
321 heur_entry = find_heur_dissector_by_unique_short_name("opensafety_sercosiii");
322 if ( heur_entry != NULL )
323 heuristic_siii_dissection_enabled = heur_entry->enabled;
324 }
325
326 static void
cleanup_dissector(void)327 cleanup_dissector(void)
328 {
329 local_scm_udid = NULL;
330
331 if ( global_filter_list )
332 {
333 wmem_destroy_list(global_filter_list);
334 global_filter_list = NULL;
335 }
336 }
337
338 void proto_register_opensafety(void);
339 void proto_reg_handoff_opensafety(void);
340
341 /* Conversation functions */
342
343 /* This is defined by the specification. The Address field is 10 bits long, and the node with the number
344 * 1 is always the SCM, therefore ( 2 ^ 10 ) - 1 nodes can be addressed. We use 2 ^ 10 here, because the
345 * SCM can talk to himself (Assign SADR for instance ) */
346 /* #define MAX_NUMBER_OF_SAFETY_NODES ( 1 << 10 ) */
347
348 /* Tracks the information that the packet pinfo has been received by receiver, and adds that information to the tree, using pos, as
349 * byte position in the PDU */
350 static void
opensafety_packet_node(tvbuff_t * message_tvb,packet_info * pinfo,proto_tree * tree,gint hf_field,guint16 saddr,guint16 posInFrame,guint16 posSdnInFrame,guint16 sdn)351 opensafety_packet_node(tvbuff_t * message_tvb, packet_info *pinfo, proto_tree *tree,
352 gint hf_field, guint16 saddr, guint16 posInFrame, guint16 posSdnInFrame, guint16 sdn )
353 {
354 proto_item *psf_item = NULL;
355 proto_tree *psf_tree = NULL;
356
357 psf_item = proto_tree_add_uint(tree, hf_field, message_tvb, posInFrame, 2, saddr);
358 psf_tree = proto_item_add_subtree(psf_item, ett_opensafety_node);
359 psf_item = proto_tree_add_uint(psf_tree, hf_oss_msg_node, message_tvb, posInFrame, 2, saddr);
360 proto_item_set_generated(psf_item);
361
362 if ( sdn > 0 )
363 {
364 psf_item = proto_tree_add_uint(psf_tree, hf_oss_msg_network, message_tvb,
365 posSdnInFrame, 2, sdn);
366 } else if ( sdn <= 0 ) {
367 psf_item = proto_tree_add_uint(psf_tree, hf_oss_msg_network, message_tvb,
368 posSdnInFrame, 2, sdn * -1);
369 expert_add_info(pinfo, psf_item, &ei_scmudid_unknown );
370 }
371 proto_item_set_generated(psf_item);
372 }
373
374 static void
opensafety_packet_receiver(tvbuff_t * message_tvb,packet_info * pinfo,proto_tree * tree,proto_item * opensafety_item,opensafety_packet_info * packet,guint16 recv,guint16 posInFrame,guint16 posSdnInFrame,guint16 sdn)375 opensafety_packet_receiver(tvbuff_t * message_tvb, packet_info *pinfo, proto_tree *tree, proto_item *opensafety_item,
376 opensafety_packet_info *packet, guint16 recv,
377 guint16 posInFrame, guint16 posSdnInFrame, guint16 sdn )
378 {
379 packet->receiver = recv;
380 if ( sdn > 0 )
381 packet->sdn = sdn;
382
383 opensafety_packet_node (message_tvb, pinfo, tree, hf_oss_msg_receiver, recv, posInFrame, posSdnInFrame, sdn );
384 proto_item_append_text(opensafety_item, ", Dst: 0x%03X (%d)", recv, recv);
385 }
386
387 /* Tracks the information that the packet pinfo has been sent by sender, and received by everyone else, and adds that information to
388 * the tree, using pos, as byte position in the PDU */
389 static void
opensafety_packet_sender(tvbuff_t * message_tvb,packet_info * pinfo,proto_tree * tree,proto_item * opensafety_item,opensafety_packet_info * packet,guint16 sender,guint16 posInFrame,guint16 posSdnInFrame,guint16 sdn)390 opensafety_packet_sender(tvbuff_t * message_tvb, packet_info *pinfo, proto_tree *tree, proto_item *opensafety_item,
391 opensafety_packet_info *packet, guint16 sender,
392 guint16 posInFrame, guint16 posSdnInFrame, guint16 sdn )
393 {
394 packet->sender = sender;
395 if ( sdn > 0 )
396 packet->sdn = sdn;
397
398 opensafety_packet_node (message_tvb, pinfo, tree, hf_oss_msg_sender, sender, posInFrame, posSdnInFrame, sdn );
399 proto_item_append_text(opensafety_item, ", Src: 0x%03X (%d)", sender, sender);
400 }
401
402 /* Tracks the information that the packet pinfo has been sent by sender, and received by receiver, and adds that information to
403 * the tree, using pos for the sender and pos2 for the receiver, as byte position in the PDU */
404 static void
opensafety_packet_sendreceiv(tvbuff_t * message_tvb,packet_info * pinfo,proto_tree * tree,proto_item * opensafety_item,opensafety_packet_info * packet,guint16 send,guint16 pos,guint16 recv,guint16 pos2,guint16 posnet,guint16 sdn)405 opensafety_packet_sendreceiv(tvbuff_t * message_tvb, packet_info *pinfo, proto_tree *tree, proto_item *opensafety_item,
406 opensafety_packet_info *packet, guint16 send, guint16 pos,
407 guint16 recv, guint16 pos2, guint16 posnet, guint16 sdn)
408 {
409 opensafety_packet_receiver(message_tvb, pinfo, tree, opensafety_item, packet, recv, pos2, posnet, sdn);
410 opensafety_packet_sender(message_tvb, pinfo, tree, opensafety_item, packet, send, pos, posnet, sdn);
411 }
412
413 static proto_item *
opensafety_packet_response(tvbuff_t * message_tvb,proto_tree * sub_tree,opensafety_packet_info * packet,gboolean isResponse)414 opensafety_packet_response(tvbuff_t *message_tvb, proto_tree *sub_tree, opensafety_packet_info *packet, gboolean isResponse)
415 {
416 proto_item *item = NULL;
417 guint8 b_id = 0;
418
419 if ( packet->msg_type != OPENSAFETY_SPDO_MESSAGE_TYPE )
420 {
421 proto_tree_add_item(sub_tree, hf_oss_msg, message_tvb,
422 OSS_FRAME_POS_ID + packet->frame.subframe1, 1, ENC_NA );
423 }
424 else
425 {
426 /* SPDOs code the connection valid bit on offset 0x04. SSDO and SNMT frames use this
427 * bit for messages. Therefore setting a bitmask on the hf-field would not work. */
428 b_id = OSS_FRAME_ID_T(message_tvb, packet->frame.subframe1) & 0xF8;
429 proto_tree_add_uint(sub_tree, hf_oss_msg, message_tvb, OSS_FRAME_POS_ID + packet->frame.subframe1, 1, b_id);
430 }
431
432 item = proto_tree_add_item(sub_tree, packet->msg_type != OPENSAFETY_SPDO_MESSAGE_TYPE ? hf_oss_msg_direction : hf_oss_spdo_direction,
433 message_tvb, OSS_FRAME_POS_ID + packet->frame.subframe1, 1, ENC_NA);
434 if ( ! isResponse )
435 packet->is_request = TRUE;
436
437 return item;
438 }
439
440 static proto_tree *
opensafety_packet_payloadtree(packet_info * pinfo,tvbuff_t * message_tvb,proto_tree * opensafety_tree,opensafety_packet_info * packet,gint ett_tree)441 opensafety_packet_payloadtree(packet_info *pinfo, tvbuff_t *message_tvb, proto_tree *opensafety_tree,
442 opensafety_packet_info *packet, gint ett_tree)
443 {
444 proto_item *item = NULL;
445
446 item = proto_tree_add_item(opensafety_tree, hf_oss_msg_category, message_tvb, OSS_FRAME_POS_ID + packet->frame.subframe1, 1, ENC_NA );
447 proto_item_set_generated(item);
448
449 if ( packet->msg_type == OPENSAFETY_SNMT_MESSAGE_TYPE)
450 packet->payload.snmt = wmem_new0(pinfo->pool, opensafety_packet_snmt);
451 else if ( packet->msg_type == OPENSAFETY_SSDO_MESSAGE_TYPE || packet->msg_type == OPENSAFETY_SLIM_SSDO_MESSAGE_TYPE )
452 {
453 packet->payload.ssdo = wmem_new0(pinfo->pool, opensafety_packet_ssdo);
454 if ( packet->msg_type == OPENSAFETY_SLIM_SSDO_MESSAGE_TYPE )
455 packet->payload.ssdo->is_slim = TRUE;
456 }
457 else if ( packet->msg_type == OPENSAFETY_SPDO_MESSAGE_TYPE )
458 packet->payload.spdo = wmem_new0(pinfo->pool, opensafety_packet_spdo);
459
460 return proto_item_add_subtree(item, ett_tree);
461 }
462
463 static guint16
findFrame1Position(packet_info * pinfo,tvbuff_t * message_tvb,guint16 byte_offset,guint8 dataLength,gboolean checkIfSlimMistake)464 findFrame1Position ( packet_info *pinfo, tvbuff_t *message_tvb, guint16 byte_offset, guint8 dataLength, gboolean checkIfSlimMistake )
465 {
466 guint16 i_wFrame1Position = 0;
467 guint16 i_payloadLength, i_calculatedLength = 0;
468 guint16 i_offset = 0, calcCRC = 0, frameCRC = 0;
469 guint8 b_tempByte = 0;
470 guint8 *bytes = NULL;
471
472 /*
473 * First, a normal package is assumed. Calculation of frame 1 position is
474 * pretty easy, because, the length of the whole package is 11 + 2*n + 2*o, which
475 * results in frame 1 start at (6 + n + o), which is length / 2 + 1
476 */
477 i_wFrame1Position = dataLength / 2 + 1;
478 i_payloadLength = tvb_get_guint8(message_tvb, byte_offset + i_wFrame1Position + 2 );
479 /* Calculating the assumed frame length, taking CRC8/CRC16 into account */
480 i_calculatedLength = i_payloadLength * 2 + 11 + 2 * (i_payloadLength > OSS_PAYLOAD_MAXSIZE_FOR_CRC8 ? 1 : 0);
481
482 /* To prevent miscalculations, where by chance the byte at [length / 2] + 3 is a value matching a possible payload length,
483 * but in reality the frame is a slim ssdo, the CRC of frame 1 gets checked additionally. This check
484 * is somewhat time consuming, so it will only run if the normal check led to a mistake detected along the line */
485 if ( checkIfSlimMistake && i_calculatedLength == dataLength )
486 {
487 if (dataLength > OSS_PAYLOAD_MAXSIZE_FOR_CRC8)
488 frameCRC = tvb_get_letohs(message_tvb, byte_offset + i_wFrame1Position + dataLength + OSS_FRAME_POS_DATA);
489 else
490 frameCRC = tvb_get_guint8(message_tvb, byte_offset + i_wFrame1Position + dataLength + OSS_FRAME_POS_DATA);
491
492 bytes = (guint8*)tvb_memdup(pinfo->pool, message_tvb, byte_offset + i_wFrame1Position, dataLength + 4);
493 if ( dataLength > OSS_PAYLOAD_MAXSIZE_FOR_CRC8 )
494 {
495 calcCRC = crc16_0x755B(bytes, dataLength + 4, 0);
496 if ( frameCRC != calcCRC )
497 calcCRC = crc16_0x5935(bytes, dataLength + 4, 0);
498 }
499 else
500 calcCRC = crc8_0x2F(bytes, dataLength + 4, 0);
501
502 /* if the calculated crc does not match the detected, the package is not a normal openSAFETY package */
503 if ( frameCRC != calcCRC )
504 dataLength = 0;
505 }
506
507 /* If the calculated length differs from the given length, a slim package is assumed. */
508 if ( i_calculatedLength != dataLength )
509 {
510 /* possible slim package */
511 i_wFrame1Position = 0;
512 /*
513 * Slim packages have a fixed sublength of either 6 bytes for frame 2 in
514 * case of crc8 and 7 bytes in case of crc16
515 */
516 i_offset = OSS_SLIM_FRAME2_WITH_CRC8 + ( dataLength < (OSS_SLIM_FRAME_WITH_CRC8_MAXSIZE + 1) ? 0 : 1 );
517 /* Last 2 digits belong to addr, therefore have to be cleared */
518 b_tempByte = ( tvb_get_guint8 ( message_tvb, byte_offset + i_offset + 1 ) ) & 0xFC;
519
520 /* If the id byte xor 0xE8 is 0, we have a slim package */
521 if ( ( ( b_tempByte ^ OPENSAFETY_MSG_SSDO_SLIM_SERVICE_REQUEST ) == 0 ) ||
522 ( ( b_tempByte ^ OPENSAFETY_MSG_SSDO_SLIM_SERVICE_RESPONSE ) == 0 ) )
523 {
524 /* Slim package found */
525 i_wFrame1Position = i_offset;
526 }
527 }
528
529 return i_wFrame1Position;
530 }
531
findSafetyFrame(packet_info * pinfo,tvbuff_t * message_tvb,guint u_Offset,gboolean b_frame2first,guint * u_frameOffset,guint * u_frameLength,opensafety_packet_info * packet)532 static gboolean findSafetyFrame ( packet_info *pinfo, tvbuff_t *message_tvb, guint u_Offset, gboolean b_frame2first,
533 guint *u_frameOffset, guint *u_frameLength, opensafety_packet_info *packet )
534 {
535 guint ctr, rem_length;
536 guint16 crc, f2crc, calcCrc = 0;
537 guint8 b_Length = 0, b_CTl = 0, crcOffset = 0, crc1Type = 0;
538 guint8 *bytes;
539 guint b_ID = 0;
540 gboolean found;
541
542 found = FALSE;
543 ctr = u_Offset;
544 rem_length = tvb_reported_length_remaining (message_tvb, ctr);
545
546 /* Search will allways start at the second byte of the frame ( cause it determines )
547 * the type of package and therefore everything else. Therefore the mininmum length - 1
548 * is the correct minimum length */
549 while ( rem_length >= ( OSS_MINIMUM_LENGTH - 1 ) )
550 {
551 /* The ID byte must ALWAYS be the second byte, therefore 0 is invalid,
552 * also, the byte we want to access, must at least exist, otherwise,
553 * the frame is not detectable as an openSAFETY frame.
554 * We check for ID and length */
555 if ( ctr != 0 && tvb_bytes_exist(message_tvb, ctr, 2) )
556 {
557 *u_frameLength = 0;
558 *u_frameOffset = 0;
559
560 crcOffset = 0;
561 b_ID = tvb_get_guint8(message_tvb, ctr );
562
563 if ( b_ID != 0x0 )
564 {
565 b_Length = tvb_get_guint8(message_tvb, ctr + 1 );
566
567 /* 0xFF is often used, but always false, otherwise start detection, if the highest
568 * bit is set */
569 if ( ( b_ID != 0xFF ) && ( b_ID & 0x80 ) )
570 {
571 /* The rem_length value might be poluted, due to the else statement of
572 * above if-decision (frame at end position detection). Therefore we
573 * calculate it here again, to have a sane value */
574 rem_length = tvb_reported_length_remaining(message_tvb, ctr);
575
576 /* Plausability check on length */
577 if ( (guint)( b_Length * 2 ) < ( rem_length + OSS_MINIMUM_LENGTH ) )
578 {
579
580 /* The calculated length must fit, but for the CRC16 check, also the calculated length
581 * plus the CRC16 end position must fit in the remaining length */
582 if ( ( b_Length <= (guint) 8 && ( b_Length <= rem_length ) ) ||
583 ( b_Length > (guint) 8 && ( ( b_Length + (guint) 5 ) <= rem_length ) ) )
584 {
585 /* Ensure, that the correct length for CRC calculation
586 * still exists in byte stream, so that we can calculate the crc */
587 if ( tvb_bytes_exist(message_tvb, ctr - 1, b_Length + 5) )
588 {
589 /* An openSAFETY command has to have a high-byte range between 0x0A and 0x0E
590 * b_ID & 0x80 took care of everything underneath, we check for 0x09 and 0x0F,
591 * as they remain the only values left, which are not valid */
592 if ( ( ( b_ID >> 4 ) != 0x09 ) && ( ( b_ID >> 4 ) != 0x0F ) )
593 {
594 /* Read CRC from position */
595 crc = tvb_get_guint8(message_tvb, ctr + 3 + b_Length );
596
597 /* There exists some false positives, where the only possible
598 * data information in the frame is the ID and the ADDR fields.
599 * The rest of the fields in frame 1 are zeroed out. The packet
600 * must be filtered out and may not be used. To detect it, we
601 * check for the CT value, which, if zero indicates strongly
602 * that this is false-positive. */
603 b_CTl = tvb_get_guint8(message_tvb, ctr + 2 );
604
605 /* If either length, crc or CTl is not zero, this may be a
606 * correct package. If all three are 0, this is most certainly
607 * an incorrect package, because the possibility of a valid
608 * package with all three values being zero is next to impossible */
609 if ( b_Length != 0x00 || crc != 0x00 || b_CTl != 0x00 )
610 {
611 /* calculate checksum */
612 bytes = (guint8 *)tvb_memdup(pinfo->pool, message_tvb, ctr - 1, b_Length + 5 );
613 if ( b_Length > 8 )
614 {
615 crc = tvb_get_letohs ( message_tvb, ctr + 3 + b_Length );
616 crcOffset = 1;
617
618 crc1Type = OPENSAFETY_CHECKSUM_CRC16;
619 calcCrc = crc16_0x755B( bytes, b_Length + 4, 0 );
620 if ( ( crc ^ calcCrc ) != 0 )
621 {
622 calcCrc = crc16_0x5935( bytes, b_Length + 4, 0 );
623 if ( ( crc ^ calcCrc ) == 0 )
624 crc1Type = OPENSAFETY_CHECKSUM_CRC16SLIM;
625 else
626 crc1Type = OPENSAFETY_CHECKSUM_INVALID;
627 }
628 } else {
629 crc1Type = OPENSAFETY_CHECKSUM_CRC8;
630 calcCrc = crc8_0x2F ( bytes, b_Length + 4, 0 );
631 }
632
633 if ( ( crc ^ calcCrc ) == 0 )
634 {
635 /* Check if this is a Slim SSDO message */
636 if ( ( b_ID >> 3 ) == ( OPENSAFETY_SLIM_SSDO_MESSAGE_TYPE >> 3 ) )
637 {
638 /* Slim SSDO messages must have a length != 0, as the first byte
639 * in the payload contains the SOD access command */
640 if ( b_Length > 0 )
641 {
642 *u_frameOffset = ( ctr - 1 );
643 *u_frameLength = b_Length + 2 * crcOffset + 11;
644
645 /* It is highly unlikely, that both frame 1 and frame 2 generate
646 * a crc == 0 or equal crc's. Therefore we check, if both crc's are
647 * equal. If so, it is a falsely detected frame. */
648 f2crc = tvb_get_guint8 ( message_tvb, ctr + 3 + 5 + b_Length );
649 if ( b_Length > 8 )
650 f2crc = tvb_get_letohs ( message_tvb, ctr + 3 + 5 + b_Length );
651 if ( crc != f2crc )
652 {
653 found = TRUE;
654 break;
655 }
656 }
657 }
658 else
659 {
660 *u_frameLength = 2 * b_Length + 2 * crcOffset + 11;
661 *u_frameOffset = ( ctr - 1 );
662
663 /* If the first crc is zero, the second one must not be 0. The header
664 * for each subfields differ, therefore it is impossible, that both
665 * crcs are zero */
666 if ( crc == 0 )
667 {
668 f2crc = tvb_get_guint8 ( message_tvb, ( ctr - 1 ) + 10 + ( 2 * b_Length ) );
669 if ( b_Length > 8 )
670 f2crc = tvb_get_letohs ( message_tvb, ( ctr - 1 ) + 11 + ( 2 * b_Length ) );
671
672 /* The crc's differ, everything is ok */
673 if ( crc != f2crc )
674 {
675 found = TRUE;
676 break;
677 }
678 }
679 else
680 {
681 /* At this point frames had been checked for SoC and SoA types of
682 * EPL. This is no longer necessary and leads to false-negatives.
683 * SoC and SoA frames get filtered out at the EPL entry point, cause
684 * EPL only provides payload, no longer complete frames. */
685 found = TRUE;
686 break;
687 }
688 }
689 }
690 }
691 }
692 }
693 }
694 }
695 }
696 else
697 {
698 /* There exist frames, where the last openSAFETY frame is sitting in the
699 * very last bytes of the frame, and the complete frame itself contains
700 * more than one openSAFETY frame. It so happens that in such a case, the
701 * last openSAFETY frame will miss detection.
702 *
703 * If so we look at the transported length, calculate the frame length,
704 * and take a look if the calculated frame length, might be a fit for the
705 * remaining length. If such is the case, we increment ctr and increment
706 * rem_length (to hit the while loop one more time) and the frame will be
707 * detected correctly. */
708 if ( rem_length == OSS_MINIMUM_LENGTH )
709 {
710 b_ID = tvb_get_guint8(message_tvb, ctr );
711 b_Length = tvb_get_guint8(message_tvb, ctr + 2 );
712 if ( ( b_ID >> 3 ) == ( OPENSAFETY_SLIM_SSDO_MESSAGE_TYPE >> 3 ) )
713 b_Length = ( 11 + ( b_Length > 8 ? 2 : 0 ) + b_Length );
714 else
715 b_Length = ( 11 + ( b_Length > 8 ? 2 : 0 ) + 2 * b_Length );
716
717 if ( rem_length == b_Length )
718 {
719 ctr++;
720 rem_length++;
721 continue;
722 }
723 }
724 }
725 }
726 }
727
728 ctr++;
729 rem_length = tvb_reported_length_remaining(message_tvb, ctr);
730
731 }
732
733 /* Store packet information in packet_info */
734 if ( found && packet )
735 {
736 packet->msg_id = b_ID;
737 packet->msg_len = b_Length;
738 packet->frame_len = *u_frameLength;
739
740 /* Should be the calculated crc, which is the same as the frame crc */
741 packet->crc.frame1 = calcCrc;
742 packet->crc.type = crc1Type;
743 if ( packet->crc.type != OPENSAFETY_CHECKSUM_INVALID )
744 packet->crc.valid1 = TRUE;
745 else
746 packet->crc.valid1 = FALSE;
747 }
748
749 /* Seem redundant if b_frame2First is false. But in this case, the function is needed for the
750 * simple detection of a possible openSAFETY frame. */
751 if ( b_frame2first && found )
752 *u_frameOffset = u_Offset;
753
754 return found;
755 }
756
757 static gint
dissect_data_payload(proto_tree * epl_tree,tvbuff_t * tvb,packet_info * pinfo,gint offset,gint len,guint8 msgType)758 dissect_data_payload ( proto_tree *epl_tree, tvbuff_t *tvb, packet_info *pinfo, gint offset, gint len, guint8 msgType )
759 {
760 gint off = 0;
761 tvbuff_t * payload_tvb = NULL;
762 heur_dtbl_entry_t *hdtbl_entry = NULL;
763
764 off = offset;
765
766 if (len > 0)
767 {
768 payload_tvb = tvb_new_subset_length_caplen(tvb, off, len, tvb_reported_length_remaining(tvb, offset) );
769 if ( ! dissector_try_heuristic(heur_opensafety_spdo_subdissector_list, payload_tvb, pinfo, epl_tree, &hdtbl_entry, &msgType))
770 call_dissector(data_dissector, payload_tvb, pinfo, epl_tree);
771
772 off += len;
773 }
774
775 return off;
776 }
777
778 static void
dissect_opensafety_spdo_message(tvbuff_t * message_tvb,packet_info * pinfo,proto_tree * opensafety_tree,opensafety_packet_info * packet,proto_item * opensafety_item)779 dissect_opensafety_spdo_message(tvbuff_t *message_tvb, packet_info *pinfo, proto_tree *opensafety_tree,
780 opensafety_packet_info * packet, proto_item * opensafety_item )
781 {
782 proto_item *item, *diritem;
783 proto_tree *spdo_tree, *spdo_flags_tree;
784 guint16 ct, addr;
785 guint64 ct40bit;
786 gint16 taddr, sdn;
787 guint dataLength;
788 guint8 tr, b_ID, spdoFlags;
789
790 dataLength = tvb_get_guint8(message_tvb, OSS_FRAME_POS_LEN + packet->frame.subframe1);
791 b_ID = tvb_get_guint8(message_tvb, packet->frame.subframe1 + 1) & 0xF8;
792
793 /* Network address is xor'ed into the start of the second frame, but only legible, if the scm given is valid */
794 sdn = ( ( OSS_FRAME_ADDR_T(message_tvb, packet->frame.subframe1) ) ^
795 ( OSS_FRAME_ADDR_T2(message_tvb, packet->frame.subframe2, packet->scm_udid[0], packet->scm_udid[1]) ) );
796 if ( ! packet->scm_udid_valid )
797 sdn = ( -1 * sdn );
798
799 /* taddr is the 4th octet in the second frame */
800 tr = ( tvb_get_guint8(message_tvb, packet->frame.subframe2 + 4) ^ packet->scm_udid[4] ) & 0xFC;
801
802 /* allow only valid SPDO flags */
803 spdoFlags = ( ( tr >> 2 ) & OPENSAFETY_SPDO_FEATURE_FLAGS );
804
805 /* An SPDO is always sent by the producer, to everybody else .
806 * For a 40bit connection OPENSAFETY_DEFAULT_DOMAIN is assumed as sdn value for now */
807 if ( (OPENSAFETY_SPDO_FEAT_40BIT_USED & spdoFlags ) == OPENSAFETY_SPDO_FEAT_40BIT_USED )
808 sdn = OPENSAFETY_DEFAULT_DOMAIN;
809
810 /* Determine the producer and set it, as opensafety_packet_node does not */
811 addr = OSS_FRAME_ADDR_T(message_tvb, packet->frame.subframe1);
812 packet->sender = addr;
813
814 opensafety_packet_node ( message_tvb, pinfo, opensafety_tree, hf_oss_msg_sender,
815 addr, OSS_FRAME_POS_ADDR + packet->frame.subframe1, packet->frame.subframe2, sdn );
816 proto_item_append_text(opensafety_item, "; Producer: 0x%03X (%d)", addr, addr);
817
818 spdo_tree = opensafety_packet_payloadtree ( pinfo, message_tvb, opensafety_tree, packet, ett_opensafety_spdo );
819
820 /* Determine SPDO Flags. Attention packet->payload.spdo exists ONLY AFTER opensafety_packet_payloadtree */
821 packet->payload.spdo->flags.enabled40bit = FALSE;
822 packet->payload.spdo->flags.requested40bit = FALSE;
823
824 if ( (OPENSAFETY_SPDO_FEAT_40BIT_AVAIL & spdoFlags ) == OPENSAFETY_SPDO_FEAT_40BIT_AVAIL )
825 packet->payload.spdo->flags.requested40bit = TRUE;
826 if ( packet->payload.spdo->flags.requested40bit && ( (OPENSAFETY_SPDO_FEAT_40BIT_USED & spdoFlags ) == OPENSAFETY_SPDO_FEAT_40BIT_USED ) )
827 packet->payload.spdo->flags.enabled40bit = TRUE;
828
829 diritem = opensafety_packet_response(message_tvb, spdo_tree, packet, b_ID == OPENSAFETY_MSG_SPDO_DATA_WITH_TIME_RESPONSE );
830 proto_tree_add_item(spdo_tree, hf_oss_spdo_connection_valid, message_tvb, OSS_FRAME_POS_ID + packet->frame.subframe1, 1, ENC_NA);
831
832 packet->payload.spdo->conn_valid = (tvb_get_guint8(message_tvb, OSS_FRAME_POS_ID + packet->frame.subframe1) & 0x04) == 0x04;
833
834 /* taddr is the 4th octet in the second frame */
835 taddr = OSS_FRAME_ADDR_T2(message_tvb, packet->frame.subframe2 + 3, packet->scm_udid[3], packet->scm_udid[4]);
836 tr = ( tvb_get_guint8(message_tvb, packet->frame.subframe2 + 4) ^ packet->scm_udid[4] ) & 0xFC;
837
838 /* determine the ct value. if complete it can be used for analysis of the package */
839 ct = tvb_get_guint8(message_tvb, packet->frame.subframe1 + 3);
840 if ( packet->scm_udid_valid )
841 {
842 ct = (guint16)((tvb_get_guint8(message_tvb, packet->frame.subframe2 + 2) ^ packet->scm_udid[2]) << 8) +
843 (tvb_get_guint8(message_tvb, packet->frame.subframe1 + 3));
844 }
845
846 if ( b_ID == OPENSAFETY_MSG_SPDO_DATA_WITH_TIME_REQUEST )
847 {
848 proto_item_append_text(diritem, " (Safety Node: %03d)", taddr);
849 item = proto_tree_add_uint_format_value(spdo_tree, hf_oss_spdo_ct, message_tvb, 0, 0, ct,
850 "0x%04X [%d] (%s)", ct, ct,
851 (packet->scm_udid_valid ? "Complete" : "Low byte only"));
852 proto_item_set_generated(item);
853 packet->payload.spdo->counter.b16 = ct;
854
855 packet->payload.spdo->timerequest = taddr;
856 proto_tree_add_uint(spdo_tree, hf_oss_spdo_time_request, message_tvb,
857 OSS_FRAME_POS_ADDR + packet->frame.subframe2 + 4, 1, tr);
858 opensafety_packet_node ( message_tvb, pinfo, spdo_tree, hf_oss_spdo_time_request_from, taddr,
859 OSS_FRAME_POS_ADDR + packet->frame.subframe2 + 3, packet->frame.subframe2, sdn );
860 }
861 else
862 {
863 if ( ! (b_ID == OPENSAFETY_MSG_SPDO_DATA_ONLY) || !(packet->payload.spdo->flags.enabled40bit) )
864 {
865 item = proto_tree_add_uint_format_value(spdo_tree, hf_oss_spdo_ct, message_tvb, 0, 0, ct,
866 "0x%04X [%d] (%s)", ct, ct, (packet->scm_udid_valid ? "Complete" : "Low byte only"));
867 proto_item_set_generated(item);
868 packet->payload.spdo->counter.b16 = ct;
869 }
870 else
871 {
872 /* 40bit counter is calculated from various fields. Therefore it cannot be read
873 * directly from the frame. All fields starting after or with packet->frame.subframe2 have to
874 * be decoded using the scm udid */
875 ct40bit = (tvb_get_guint8(message_tvb, packet->frame.subframe2 + 3) ^ packet->scm_udid[3]);
876 ct40bit <<= 8;
877 ct40bit += ((guint64)(tvb_get_guint8(message_tvb, packet->frame.subframe2 + 1) ^ packet->scm_udid[1]) ^ tvb_get_guint8(message_tvb, packet->frame.subframe1 + 1));
878 ct40bit <<= 8;
879 ct40bit += (tvb_get_guint8(message_tvb, packet->frame.subframe2 + 0) ^ packet->scm_udid[0]) ^ OPENSAFETY_DEFAULT_DOMAIN ^ tvb_get_guint8(message_tvb, packet->frame.subframe1 + 0);
880 ct40bit <<= 8;
881 ct40bit += (tvb_get_guint8(message_tvb, packet->frame.subframe2 + 2) ^ packet->scm_udid[2]);
882 ct40bit <<= 8;
883 ct40bit += tvb_get_guint8(message_tvb, packet->frame.subframe1 + 3);
884
885 item = proto_tree_add_uint64(spdo_tree, hf_oss_spdo_ct_40bit, message_tvb, 0, 0, ct40bit);
886 proto_item_set_generated(item);
887
888 packet->payload.spdo->counter.b40 = ct40bit;
889 if ( global_opensafety_debug_verbose )
890 expert_add_info ( pinfo, item, &ei_40bit_default_domain );
891 }
892 proto_item_set_generated(item);
893
894 if ( b_ID == OPENSAFETY_MSG_SPDO_DATA_WITH_TIME_RESPONSE )
895 {
896 proto_item_append_text(diritem, " (Safety Node: %03d)", taddr);
897 proto_tree_add_uint(spdo_tree, hf_oss_spdo_time_request, message_tvb,
898 OSS_FRAME_POS_ADDR + packet->frame.subframe2 + 4, 1, tr);
899 packet->payload.spdo->timerequest = taddr;
900
901 opensafety_packet_node ( message_tvb, pinfo, spdo_tree, hf_oss_spdo_time_request_to, taddr,
902 OSS_FRAME_POS_ADDR + packet->frame.subframe2 + 3, packet->frame.subframe2, sdn );
903 }
904 else
905 {
906 item = proto_tree_add_uint(spdo_tree, hf_oss_spdo_feature_flags,
907 message_tvb, OSS_FRAME_POS_ADDR + packet->frame.subframe2 + 4, 1, spdoFlags << 2);
908
909 spdo_flags_tree = proto_item_add_subtree(item, ett_opensafety_spdo_flags);
910
911 proto_tree_add_boolean(spdo_flags_tree, hf_oss_spdo_feature_flag_40bit_available, message_tvb,
912 OSS_FRAME_POS_ADDR + packet->frame.subframe2 + 4, 1,
913 packet->payload.spdo->flags.requested40bit ? OPENSAFETY_SPDO_FEAT_40BIT_AVAIL << 2 : 0 );
914 proto_tree_add_boolean(spdo_flags_tree, hf_oss_spdo_feature_flag_40bit_used, message_tvb,
915 OSS_FRAME_POS_ADDR + packet->frame.subframe2 + 4, 1,
916 packet->payload.spdo->flags.enabled40bit ? OPENSAFETY_SPDO_FEAT_40BIT_USED << 2 : 0 );
917 }
918 }
919
920 if ( dataLength > 0 )
921 {
922 dissect_data_payload(spdo_tree, message_tvb, pinfo, OSS_FRAME_POS_ID + 3, dataLength, OPENSAFETY_SPDO_MESSAGE_TYPE);
923 }
924 }
925
dissect_opensafety_ssdo_payload(packet_info * pinfo,tvbuff_t * new_tvb,proto_tree * ssdo_payload,guint8 sacmd)926 static void dissect_opensafety_ssdo_payload ( packet_info *pinfo, tvbuff_t *new_tvb, proto_tree *ssdo_payload, guint8 sacmd )
927 {
928 guint dataLength = 0, ctr = 0, n = 0, nCRCs = 0;
929 guint8 ssdoSubIndex = 0;
930 guint16 ssdoIndex = 0, dispSSDOIndex = 0;
931 guint32 sodLength = 0, entry = 0;
932 proto_item *item;
933 proto_tree *sod_tree, *ext_tree;
934
935 dataLength = tvb_captured_length(new_tvb);
936
937 ssdoIndex = tvb_get_letohs(new_tvb, 0);
938
939 sodLength = tvb_get_letohl(new_tvb, 4);
940
941 /* first check for extended parameter */
942 if ( dataLength == 16 || sodLength == ( dataLength - 16 ) || ssdoIndex == 0x0101 )
943 {
944 /* extended parameter header & data */
945 item = proto_tree_add_string_format(ssdo_payload, hf_oss_ssdo_extpar,
946 new_tvb, 0, dataLength, "", "Extended Parameter Set: %s",
947 (dataLength == 16 ? "Header only" : "Header & Data") );
948 ext_tree = proto_item_add_subtree(item, ett_opensafety_ssdo_extpar);
949
950 proto_tree_add_item(ext_tree, hf_oss_ssdo_extpar_parset, new_tvb, 0, 1, ENC_BIG_ENDIAN );
951 proto_tree_add_item(ext_tree, hf_oss_ssdo_extpar_version, new_tvb, 1, 1, ENC_BIG_ENDIAN );
952 proto_tree_add_item(ext_tree, hf_oss_ssdo_extpar_saddr, new_tvb, 2, 2, ENC_LITTLE_ENDIAN );
953
954 proto_tree_add_uint_format_value(ext_tree, hf_oss_ssdo_extpar_length,
955 new_tvb, 4, 4, sodLength, "0x%04X (%d octets)",
956 sodLength, sodLength );
957
958 proto_tree_add_item(ext_tree, hf_oss_ssdo_extpar_crc, new_tvb, 8, 4, ENC_LITTLE_ENDIAN );
959 proto_tree_add_item(ext_tree, hf_oss_ssdo_extpar_tstamp, new_tvb, 12, 4, ENC_LITTLE_ENDIAN );
960
961 if ( dataLength != 16 )
962 {
963 item = proto_tree_add_item(ext_tree, hf_oss_ssdo_extpar_data, new_tvb, 16, dataLength - 16, ENC_NA );
964
965 if ( ( dataLength - sodLength ) != 16 )
966 expert_add_info ( pinfo, item, &ei_message_reassembly_size_differs_from_header );
967 }
968 }
969 else
970 {
971 /* If == upload, it is most likely a par upload */
972 if ( sacmd == OPENSAFETY_MSG_SSDO_UPLOAD_SEGMENT_END && ( dataLength % 4 == 0 ) )
973 {
974
975 item = proto_tree_add_uint_format_value(ssdo_payload, hf_oss_ssdo_sod_index, new_tvb,
976 0, 0, 0x1018, "0x%04X (%s)", 0x1018,
977 val_to_str_ext_const( ((guint32) (0x1018 << 16) ),
978 &opensafety_sod_idx_names_ext, "Unknown") );
979 sod_tree = proto_item_add_subtree(item, ett_opensafety_ssdo_sodentry);
980 proto_item_set_generated(item);
981
982 item = proto_tree_add_uint_format_value(sod_tree, hf_oss_ssdo_sod_subindex, new_tvb, 0, 0,
983 0x06, "0x%02X (%s)", 0x06,
984 val_to_str_ext_const(((guint32) (0x1018 << 16) + 0x06),
985 &opensafety_sod_idx_names_ext, "Unknown") );
986 proto_item_set_generated(item);
987
988 proto_tree_add_item( sod_tree, hf_oss_sod_par_timestamp, new_tvb, 0, 4, ENC_LITTLE_ENDIAN );
989
990 /* This is to avoid a compiler loop optimization warning */
991 nCRCs = dataLength / 4;
992 for ( ctr = 1; ctr < nCRCs; ctr++ )
993 {
994 entry = tvb_get_letohl ( new_tvb, ctr * 4 );
995 proto_tree_add_uint_format_value ( sod_tree, hf_oss_sod_par_checksum, new_tvb, ctr * 4,
996 4, entry, "[#%d] 0x%08X", ctr, entry );
997 }
998 }
999 /* If != upload, it is most likely a 101A download */
1000 else
1001 {
1002
1003 /* normal parameter set */
1004 for ( ctr = 0; ctr < dataLength; ctr++ )
1005 {
1006 ssdoIndex = tvb_get_letohs(new_tvb, ctr);
1007 ssdoSubIndex = tvb_get_guint8(new_tvb, ctr + 2);
1008 dispSSDOIndex = ssdoIndex;
1009
1010 if ( ssdoIndex >= 0x1400 && ssdoIndex <= 0x17FE )
1011 dispSSDOIndex = 0x1400;
1012 else if ( ssdoIndex >= 0x1800 && ssdoIndex <= 0x1BFE )
1013 dispSSDOIndex = 0x1800;
1014 else if ( ssdoIndex >= 0x1C00 && ssdoIndex <= 0x1FFE )
1015 dispSSDOIndex = 0x1C00;
1016 else if ( ssdoIndex >= 0xC000 && ssdoIndex <= 0xC3FE )
1017 dispSSDOIndex = 0xC000;
1018
1019 item = proto_tree_add_uint_format_value(ssdo_payload, hf_oss_ssdo_sod_index, new_tvb,
1020 ctr, 2, ssdoIndex, "0x%04X (%s)", ssdoIndex,
1021 val_to_str_ext_const( ((guint32) (dispSSDOIndex << 16) ),
1022 &opensafety_sod_idx_names_ext, "Unknown") );
1023 if ( ssdoIndex != dispSSDOIndex )
1024 proto_item_set_generated ( item );
1025
1026 if ( ssdoIndex < 0x1000 || ssdoIndex > 0xE7FF )
1027 expert_add_info ( pinfo, item, &ei_payload_unknown_format );
1028
1029 sod_tree = proto_item_add_subtree(item, ett_opensafety_ssdo_sodentry);
1030
1031 if ( ssdoSubIndex != 0 )
1032 {
1033 proto_tree_add_uint_format_value(sod_tree, hf_oss_ssdo_sod_subindex, new_tvb, ctr + 2, 1,
1034 ssdoSubIndex, "0x%02X (%s)", ssdoSubIndex,
1035 val_to_str_ext_const(((guint32) (ssdoIndex << 16) + ssdoSubIndex),
1036 &opensafety_sod_idx_names_ext, "Unknown") );
1037 }
1038 else
1039 proto_tree_add_uint_format_value(sod_tree, hf_oss_ssdo_sod_subindex, new_tvb, ctr + 2, 1,
1040 ssdoSubIndex, "0x%02X", ssdoSubIndex );
1041 ctr += 2;
1042
1043 /* reading real size */
1044 sodLength = tvb_get_letohl ( new_tvb, ctr + 1 );
1045 if ( sodLength > (dataLength - ctr) )
1046 sodLength = 0;
1047
1048 if ( ( sodLength + 4 + ctr ) > dataLength )
1049 break;
1050
1051 if ( ssdoIndex == OPENSAFETY_SOD_DVI && ssdoSubIndex == 0x06 )
1052 {
1053 proto_tree_add_item( sod_tree, hf_oss_sod_par_timestamp, new_tvb, ctr + 5, 4, ENC_LITTLE_ENDIAN );
1054
1055 /* This is to avoid a compiler loop optimization warning */
1056 nCRCs = sodLength / 4;
1057 for ( n = 1; n < nCRCs; n++ )
1058 {
1059 entry = tvb_get_letohl ( new_tvb, ctr + 5 + ( n * 4 ) );
1060 proto_tree_add_uint_format_value ( sod_tree, hf_oss_sod_par_checksum, new_tvb,
1061 (ctr + 5 + ( n * 4 ) ), 4, entry, "[#%d] 0x%08X", n, entry );
1062 }
1063 } else if ( ssdoIndex == OPENSAFETY_SOD_DVI && ssdoSubIndex == 0x07 ) {
1064 proto_tree_add_item( sod_tree, hf_oss_sod_par_timestamp, new_tvb, ctr + 5, 4, ENC_LITTLE_ENDIAN );
1065 } else if ( ( dispSSDOIndex == OPENSAFETY_SOD_RXMAP || dispSSDOIndex == OPENSAFETY_SOD_TXMAP ) && ssdoSubIndex != 0x0 ) {
1066 proto_tree_add_uint(sod_tree, hf_oss_ssdo_sodentry_size, new_tvb, ctr + 1, 4, sodLength );
1067 item = proto_tree_add_item(sod_tree, hf_oss_ssdo_sodmapping, new_tvb, ctr + 5, sodLength, ENC_NA );
1068 ext_tree = proto_item_add_subtree(item, ett_opensafety_sod_mapping);
1069
1070 proto_tree_add_item(ext_tree, hf_oss_ssdo_sodmapping_bits, new_tvb, ctr + 5, 1, ENC_NA);
1071
1072 proto_tree_add_item(ext_tree, hf_oss_ssdo_sod_index, new_tvb, ctr + 7, 2, ENC_LITTLE_ENDIAN);
1073 proto_tree_add_item(ext_tree, hf_oss_ssdo_sod_subindex, new_tvb, ctr + 6, 1, ENC_NA);
1074
1075 } else {
1076 proto_tree_add_uint(sod_tree, hf_oss_ssdo_sodentry_size, new_tvb, ctr + 1, 4, sodLength );
1077 if ( sodLength > 0 )
1078 proto_tree_add_item(sod_tree, hf_oss_ssdo_sodentry_data, new_tvb, ctr + 5, sodLength, ENC_NA );
1079 }
1080 ctr += sodLength + 4;
1081 }
1082 }
1083 }
1084
1085
1086 }
1087
1088 static void
dissect_opensafety_ssdo_message(tvbuff_t * message_tvb,packet_info * pinfo,proto_tree * opensafety_tree,opensafety_packet_info * packet,proto_item * opensafety_item)1089 dissect_opensafety_ssdo_message(tvbuff_t *message_tvb, packet_info *pinfo, proto_tree *opensafety_tree,
1090 opensafety_packet_info * packet, proto_item * opensafety_item )
1091 {
1092 proto_item *item;
1093 proto_tree *ssdo_tree, *ssdo_payload;
1094 guint16 taddr = 0, sdn = 0, server = 0, client = 0, n = 0, ct = 0;
1095 guint32 abortcode, ssdoIndex = 0, ssdoSubIndex = 0, payloadSize, fragmentId = 0, entry = 0;
1096 guint8 db0Offset, db0, payloadOffset, preload;
1097 guint dataLength;
1098 gint calcDataLength;
1099 gboolean isResponse, saveFragmented;
1100 tvbuff_t *new_tvb = NULL;
1101 fragment_head *frag_msg = NULL;
1102
1103 static int * const ssdo_sacmd_flags[] = {
1104 &hf_oss_ssdo_sacmd_end_segment,
1105 &hf_oss_ssdo_sacmd_initiate,
1106 &hf_oss_ssdo_sacmd_toggle,
1107 &hf_oss_ssdo_sacmd_segmentation,
1108 &hf_oss_ssdo_sacmd_abort_transfer,
1109 &hf_oss_ssdo_sacmd_preload,
1110 &hf_oss_ssdo_sacmd_access_type,
1111 NULL
1112 };
1113
1114 dataLength = tvb_get_guint8(message_tvb, OSS_FRAME_POS_LEN + packet->frame.subframe1);
1115
1116 db0Offset = packet->frame.subframe1 + OSS_FRAME_POS_DATA;
1117 db0 = tvb_get_guint8(message_tvb, db0Offset);
1118 ssdoIndex = 0;
1119 ssdoSubIndex = 0;
1120
1121 /* Response is determined by the openSAFETY message field */
1122 isResponse = ( ( OSS_FRAME_ID_T(message_tvb, packet->frame.subframe1) & 0x04 ) == 0x04 );
1123
1124 if ( packet->scm_udid_valid )
1125 {
1126 /* taddr is the 4th octet in the second frame */
1127 taddr = OSS_FRAME_ADDR_T2(message_tvb, packet->frame.subframe2 + 3, packet->scm_udid[3], packet->scm_udid[4]);
1128 sdn = ( OSS_FRAME_ADDR_T(message_tvb, packet->frame.subframe1) ^
1129 ( OSS_FRAME_ADDR_T2(message_tvb, packet->frame.subframe2, packet->scm_udid[0], packet->scm_udid[1]) ) );
1130
1131 opensafety_packet_sendreceiv ( message_tvb, pinfo, opensafety_tree, opensafety_item, packet, taddr,
1132 packet->frame.subframe2 + 3, OSS_FRAME_ADDR_T(message_tvb, packet->frame.subframe1),
1133 packet->frame.subframe1, packet->frame.subframe2, sdn );
1134 }
1135 else if ( ! isResponse )
1136 {
1137 opensafety_packet_sender ( message_tvb, pinfo, opensafety_tree, opensafety_item, packet,
1138 OSS_FRAME_ADDR_T(message_tvb, packet->frame.subframe1), packet->frame.subframe1,
1139 packet->frame.subframe2, -1 * ( ( OSS_FRAME_ADDR_T(message_tvb, packet->frame.subframe1) ) ^
1140 ( OSS_FRAME_ADDR_T2(message_tvb, packet->frame.subframe2, packet->scm_udid[0], packet->scm_udid[1]) ) ) );
1141 }
1142 else if ( isResponse )
1143 {
1144 opensafety_packet_receiver ( message_tvb, pinfo, opensafety_tree, opensafety_item, packet,
1145 OSS_FRAME_ADDR_T(message_tvb, packet->frame.subframe1), packet->frame.subframe1,
1146 packet->frame.subframe2, -1 * ( ( OSS_FRAME_ADDR_T(message_tvb, packet->frame.subframe1) ) ^
1147 ( OSS_FRAME_ADDR_T2(message_tvb, packet->frame.subframe2, packet->scm_udid[0], packet->scm_udid[1]) ) ) );
1148 }
1149
1150 ssdo_tree = opensafety_packet_payloadtree ( pinfo, message_tvb, opensafety_tree, packet, ett_opensafety_ssdo );
1151
1152 opensafety_packet_response ( message_tvb, ssdo_tree, packet, isResponse );
1153
1154 packet->payload.ssdo->sacmd.toggle = ( db0 & OPENSAFETY_SSDO_SACMD_TGL ) == OPENSAFETY_SSDO_SACMD_TGL;
1155 packet->payload.ssdo->sacmd.abort_transfer = ( db0 & OPENSAFETY_SSDO_SACMD_ABRT ) == OPENSAFETY_SSDO_SACMD_ABRT;
1156 packet->payload.ssdo->sacmd.preload = ( db0 & OPENSAFETY_SSDO_SACMD_PRLD ) == OPENSAFETY_SSDO_SACMD_PRLD;
1157 packet->payload.ssdo->sacmd.read_access = ( db0 & OPENSAFETY_SSDO_DOWNLOAD ) == OPENSAFETY_SSDO_DOWNLOAD;
1158 packet->payload.ssdo->sacmd.initiate = ( db0 & OPENSAFETY_SSDO_SACMD_INI ) == OPENSAFETY_SSDO_SACMD_INI;
1159 packet->payload.ssdo->sacmd.segmented = ( db0 & OPENSAFETY_SSDO_SACMD_SEG ) == OPENSAFETY_SSDO_SACMD_SEG;
1160 packet->payload.ssdo->sacmd.end_segment = ( db0 & OPENSAFETY_SSDO_SACMD_ENSG ) == OPENSAFETY_SSDO_SACMD_ENSG;
1161
1162 if ( isResponse )
1163 {
1164 opensafety_packet_node ( message_tvb, pinfo, ssdo_tree, hf_oss_ssdo_client,
1165 OSS_FRAME_ADDR_T(message_tvb, packet->frame.subframe1),
1166 packet->frame.subframe1, packet->frame.subframe2, sdn );
1167 client = OSS_FRAME_ADDR_T(message_tvb, packet->frame.subframe1);
1168
1169 if ( packet->scm_udid_valid )
1170 {
1171 proto_tree_add_uint(ssdo_tree, hf_oss_ssdo_server, message_tvb, packet->frame.subframe2 + 3, 2, taddr);
1172 server = taddr;
1173 }
1174 }
1175 else if ( ! isResponse )
1176 {
1177 proto_tree_add_uint(ssdo_tree, hf_oss_ssdo_server, message_tvb, packet->frame.subframe1, 2, OSS_FRAME_ADDR_T(message_tvb, packet->frame.subframe1));
1178 server = OSS_FRAME_ADDR_T(message_tvb, packet->frame.subframe1);
1179 if ( packet->scm_udid_valid )
1180 {
1181 opensafety_packet_node ( message_tvb, pinfo, ssdo_tree, hf_oss_ssdo_client,
1182 taddr, packet->frame.subframe2 + 3, packet->frame.subframe2, sdn );
1183 client = taddr;
1184 }
1185 }
1186
1187 /* Toggle bit must be removed, otherwise the values cannot be displayed correctly */
1188 if ( packet->payload.ssdo->sacmd.toggle )
1189 db0 &= (~OPENSAFETY_SSDO_SACMD_TGL);
1190 proto_tree_add_bitmask(ssdo_tree, message_tvb, db0Offset, hf_oss_ssdo_sacmd,
1191 ett_opensafety_ssdo_sacmd, ssdo_sacmd_flags, ENC_NA);
1192
1193 col_append_fstr(pinfo->cinfo, COL_INFO, ", SACMD: %s", val_to_str_const(db0, opensafety_ssdo_sacmd_values, " "));
1194
1195 payloadOffset = db0Offset + 1;
1196
1197 ct = tvb_get_guint8(message_tvb, packet->frame.subframe1 + 3);
1198 if ( packet->scm_udid_valid )
1199 {
1200 ct = (guint16)((tvb_get_guint8(message_tvb, packet->frame.subframe2 + 2) ^ packet->scm_udid[2]) << 8);
1201 ct += (tvb_get_guint8(message_tvb, packet->frame.subframe1 + 3));
1202 }
1203
1204 proto_tree_add_uint(ssdo_tree, hf_oss_ssdo_sano, message_tvb, packet->frame.subframe1 + 3, 1, ct );
1205
1206 /* Evaluate preload field [field TR] */
1207 if ( packet->scm_udid_valid && packet->payload.ssdo->sacmd.preload && isResponse )
1208 {
1209 /* Preload info are the higher 6 bit of the TR field */
1210 preload = ( (tvb_get_guint8(message_tvb, packet->frame.subframe2 + 4) ^ packet->scm_udid[4]) & 0xFC ) >> 2;
1211
1212 if ( packet->payload.ssdo->sacmd.initiate )
1213 {
1214 /* Use the lower 4 bits from the preload as size */
1215 proto_tree_add_uint_format_value(ssdo_tree, hf_oss_ssdo_preload_queue, message_tvb, packet->frame.subframe2 + 4, 1,
1216 preload & 0x0F, "%d", preload & 0x0F );
1217 }
1218 else
1219 {
1220 /* The highest 2 bits of information contain an error flag */
1221 item = proto_tree_add_item(ssdo_tree, hf_oss_ssdo_preload_error, message_tvb, packet->frame.subframe2 + 4, 1, ENC_NA );
1222 if ( (preload & 0x30) == 0x30 )
1223 proto_item_append_text(item, " (SOD Access Request Number is last successful)" );
1224 }
1225 }
1226
1227 /* When the following clause is met, DB1,2 contain the SOD index, and DB3 the SOD subindex */
1228 if ( packet->payload.ssdo->sacmd.initiate && !packet->payload.ssdo->sacmd.abort_transfer )
1229 {
1230 ssdoIndex = tvb_get_letohs(message_tvb, db0Offset + 1);
1231 ssdoSubIndex = tvb_get_guint8(message_tvb, db0Offset + 3);
1232
1233 proto_tree_add_uint_format_value(ssdo_tree, hf_oss_ssdo_sod_index, message_tvb, db0Offset + 1, 2,
1234 ssdoIndex, "0x%04X (%s)", ssdoIndex,
1235 val_to_str_ext_const(((guint32) (ssdoIndex << 16)), &opensafety_sod_idx_names_ext, "Unknown") );
1236 col_append_fstr(pinfo->cinfo, COL_INFO, " [%s", val_to_str_ext_const(((guint32) (ssdoIndex << 16)), &opensafety_sod_idx_names_ext, "Unknown"));
1237
1238 /* Some SOD downloads (0x101A for instance) don't have sub-indeces */
1239 if ( ssdoSubIndex != 0x0 )
1240 {
1241 proto_tree_add_uint_format_value(ssdo_tree, hf_oss_ssdo_sod_subindex, message_tvb, db0Offset + 3, 1,
1242 ssdoSubIndex, "0x%02X (%s)", ssdoSubIndex,
1243 val_to_str_ext_const(((guint32) (ssdoIndex << 16) + ssdoSubIndex), &opensafety_sod_idx_names_ext, "Unknown") );
1244 col_append_fstr(pinfo->cinfo, COL_INFO, " - %s",
1245 val_to_str_ext_const(((guint32) (ssdoIndex << 16) + ssdoSubIndex), &opensafety_sod_idx_names_ext, "Unknown"));
1246 }
1247 col_append_str(pinfo->cinfo, COL_INFO, "]");
1248 payloadOffset += 3;
1249 }
1250
1251 if ( packet->payload.ssdo->sacmd.abort_transfer )
1252 {
1253 abortcode = tvb_get_letohl(message_tvb, packet->frame.subframe1 + OSS_FRAME_POS_DATA + 4);
1254
1255 proto_tree_add_uint_format_value(ssdo_tree, hf_oss_ssdo_abort_code, message_tvb, packet->frame.subframe1 + OSS_FRAME_POS_DATA + 4, 4, abortcode,
1256 "0x%04X %04X - %s", (guint16)(abortcode >> 16), (guint16)(abortcode),
1257 val_to_str_ext_const(abortcode, &opensafety_abort_codes_ext, "Unknown"));
1258 col_append_fstr(pinfo->cinfo, COL_INFO, " - %s", val_to_str_ext_const(abortcode, &opensafety_abort_codes_ext, "Unknown"));
1259
1260
1261 } else {
1262 /* Either the SSDO msg is a response, then data is sent by the server and only in uploads,
1263 * or the message is a request, then data is coming from the client and payload data is
1264 * sent in downloads. Data is only sent in initiate, segmented or end-segment messages */
1265 if ( ( packet->payload.ssdo->sacmd.initiate || packet->payload.ssdo->sacmd.segmented || packet->payload.ssdo->sacmd.end_segment ) &&
1266 ( ( isResponse && !packet->payload.ssdo->sacmd.read_access ) ||
1267 ( !isResponse && packet->payload.ssdo->sacmd.read_access ) ) )
1268 {
1269 saveFragmented = pinfo->fragmented;
1270 if ( server != 0 && client != 0 )
1271 fragmentId = (guint32)((((guint32)client) << 16 ) + server );
1272
1273 /* If payload data has to be calculated, either a total size is given, or not */
1274 if ( packet->payload.ssdo->sacmd.segmented && packet->payload.ssdo->sacmd.initiate )
1275 {
1276
1277 payloadOffset += 4;
1278
1279 /* reading real size */
1280 payloadSize = tvb_get_letohl(message_tvb, payloadOffset - 4);
1281
1282 calcDataLength = dataLength - (payloadOffset - db0Offset);
1283
1284 item = proto_tree_add_uint_format_value(ssdo_tree, hf_oss_ssdo_payload_size, message_tvb, payloadOffset - 4, 4,
1285 payloadSize, "%d octets total (%d octets in this frame)", payloadSize, calcDataLength);
1286
1287 if ( calcDataLength >= 0 )
1288 {
1289 if ( fragmentId != 0 && packet->payload.ssdo->sacmd.segmented )
1290 {
1291 pinfo->fragmented = TRUE;
1292 frag_msg = fragment_add_seq_check(&os_reassembly_table, message_tvb, payloadOffset, pinfo,
1293 fragmentId, NULL, 0, calcDataLength, TRUE );
1294 fragment_add_seq_offset ( &os_reassembly_table, pinfo, fragmentId, NULL, ct );
1295
1296 if ( frag_msg != NULL )
1297 {
1298 item = proto_tree_add_bytes_format_value(ssdo_tree, hf_oss_ssdo_payload, message_tvb, 0, 0, NULL, "Reassembled" );
1299 proto_item_set_generated(item);
1300
1301 ssdo_payload = proto_item_add_subtree(item, ett_opensafety_ssdo_payload);
1302 process_reassembled_data(message_tvb, 0, pinfo, "Reassembled Message", frag_msg, &oss_frag_items, NULL, ssdo_payload );
1303 }
1304 }
1305
1306 proto_tree_add_item(ssdo_tree, hf_oss_ssdo_payload, message_tvb, payloadOffset, calcDataLength, ENC_NA );
1307 } else {
1308 if ( global_opensafety_debug_verbose )
1309 expert_add_info_format(pinfo, item, &ei_payload_length_not_positive,
1310 "Calculation for payload length yielded non-positive result [%d]", (guint) calcDataLength );
1311 }
1312 }
1313 else
1314 {
1315 payloadSize = dataLength - (payloadOffset - db0Offset);
1316 if ((gint)dataLength < (payloadOffset - db0Offset))
1317 {
1318 if ( global_opensafety_debug_verbose )
1319 expert_add_info_format(pinfo, opensafety_item, &ei_payload_length_not_positive,
1320 "Calculation for payload length yielded non-positive result [%d]", (gint)payloadSize );
1321 return;
1322 }
1323
1324 if ( fragmentId != 0 && packet->payload.ssdo->sacmd.segmented )
1325 {
1326 pinfo->fragmented = TRUE;
1327
1328 frag_msg = fragment_add_seq_check(&os_reassembly_table, message_tvb, payloadOffset, pinfo,
1329 fragmentId, NULL, ct, payloadSize,
1330 packet->payload.ssdo->sacmd.end_segment ? FALSE : TRUE );
1331 }
1332
1333 if ( frag_msg )
1334 {
1335 item = proto_tree_add_bytes_format_value(ssdo_tree, hf_oss_ssdo_payload, message_tvb,
1336 0, 0, NULL, "Reassembled" );
1337 proto_item_set_generated(item);
1338 ssdo_payload = proto_item_add_subtree(item, ett_opensafety_ssdo_payload);
1339
1340 new_tvb = process_reassembled_data(message_tvb, 0, pinfo, "Reassembled Message", frag_msg,
1341 &oss_frag_items, NULL, ssdo_payload );
1342 if ( packet->payload.ssdo->sacmd.end_segment && new_tvb )
1343 {
1344 item = proto_tree_add_uint_format_value(ssdo_payload, hf_oss_ssdo_payload_size, message_tvb, 0, 0,
1345 payloadSize, "%d octets (over all fragments)", frag_msg->len);
1346 proto_item_set_generated(item);
1347
1348 col_append_str(pinfo->cinfo, COL_INFO, " (Message Reassembled)" );
1349 dissect_opensafety_ssdo_payload ( pinfo, new_tvb, ssdo_payload, db0 );
1350 }
1351 }
1352 else
1353 {
1354 item = proto_tree_add_uint_format_value(ssdo_tree, hf_oss_ssdo_payload_size, message_tvb, 0, 0, payloadSize,
1355 "%d octets", payloadSize);
1356 proto_item_set_generated(item);
1357
1358 if ( ssdoIndex == OPENSAFETY_SOD_DVI && ssdoSubIndex == 0x06 )
1359 {
1360 proto_tree_add_item( ssdo_tree, hf_oss_sod_par_timestamp, message_tvb, payloadOffset, 4, ENC_LITTLE_ENDIAN );
1361 for ( n = 4; n < payloadSize; n+=4 )
1362 {
1363 entry = tvb_get_letohl ( message_tvb, payloadOffset + n );
1364 proto_tree_add_uint_format_value ( ssdo_tree, hf_oss_sod_par_checksum, message_tvb, (payloadOffset + n ),
1365 4, entry, "[#%d] 0x%08X", ( n / 4 ), entry );
1366 }
1367 } else if ( ssdoIndex == OPENSAFETY_SOD_DVI && ssdoSubIndex == 0x07 ) {
1368 proto_tree_add_item ( ssdo_tree, hf_oss_sod_par_timestamp, message_tvb, payloadOffset, 4, ENC_LITTLE_ENDIAN );
1369 } else
1370 proto_tree_add_item(ssdo_tree, hf_oss_ssdo_payload, message_tvb, payloadOffset, payloadSize, ENC_NA );
1371 }
1372 }
1373
1374 pinfo->fragmented = saveFragmented;
1375 }
1376 }
1377 }
1378
1379 static void
opensafety_parse_scm_udid(tvbuff_t * tvb,packet_info * pinfo,proto_tree * tree,opensafety_packet_info * packet,guint offset)1380 opensafety_parse_scm_udid ( tvbuff_t* tvb, packet_info *pinfo, proto_tree *tree,
1381 opensafety_packet_info *packet, guint offset )
1382 {
1383 proto_item * item = NULL;
1384 gchar *scm_udid_test = NULL;
1385
1386 item = proto_tree_add_item(tree, hf_oss_snmt_udid, tvb, offset, 6, ENC_NA);
1387
1388 scm_udid_test = tvb_bytes_to_str_punct(pinfo->pool, tvb, offset, 6, ':' );
1389
1390 if ( scm_udid_test != NULL && strlen( scm_udid_test ) == 17 )
1391 {
1392 if ( g_strcmp0("00:00:00:00:00:00", scm_udid_test ) != 0 )
1393 {
1394 packet->payload.snmt->scm_udid = scm_udid_test;
1395
1396 if ( ( global_scm_udid_autoset == TRUE ) && ( memcmp ( global_scm_udid, scm_udid_test, 17 ) != 0 ) )
1397 {
1398 if ( local_scm_udid == NULL || memcmp ( local_scm_udid, scm_udid_test, 17 ) != 0 )
1399 {
1400 local_scm_udid = wmem_strdup(wmem_file_scope(), scm_udid_test );
1401 if ( global_opensafety_debug_verbose )
1402 expert_add_info_format(pinfo, item, &ei_scmudid_autodetected,
1403 "Auto detected payload as SCM UDID [%s].", local_scm_udid);
1404 }
1405 }
1406 }
1407 }
1408 }
1409
1410 static void
dissect_opensafety_snmt_message(tvbuff_t * message_tvb,packet_info * pinfo,proto_tree * opensafety_tree,opensafety_packet_info * packet,proto_item * opensafety_item)1411 dissect_opensafety_snmt_message(tvbuff_t *message_tvb, packet_info *pinfo, proto_tree *opensafety_tree,
1412 opensafety_packet_info *packet, proto_item * opensafety_item )
1413 {
1414 proto_tree *snmt_tree;
1415 guint16 addr, taddr, sdn;
1416 guint8 db0, byte, errcode;
1417 guint dataLength;
1418
1419 dataLength = OSS_FRAME_LENGTH_T(message_tvb, packet->frame.subframe1);
1420
1421 /* addr is the first field, as well as the recipient of the message */
1422 addr = packet->saddr;
1423
1424 /* taddr is the 4th octet in the second frame */
1425 taddr = OSS_FRAME_ADDR_T(message_tvb, packet->frame.subframe2 + 3);
1426 /* domain is xor'ed on the first field in the second frame. As this is also addr, it is easy to obtain */
1427 sdn = OSS_FRAME_ADDR_T(message_tvb, packet->frame.subframe2) ^ addr;
1428 packet->sdn = sdn;
1429
1430 db0 = -1;
1431 if (dataLength > 0)
1432 db0 = tvb_get_guint8(message_tvb, packet->frame.subframe1 + OSS_FRAME_POS_DATA);
1433
1434 packet->msg_id = OSS_FRAME_ID_T(message_tvb, packet->frame.subframe1);
1435
1436 if ( ( packet->msg_id == OPENSAFETY_MSG_SNMT_SERVICE_RESPONSE ) &&
1437 ( (db0 ^ OPENSAFETY_MSG_SNMT_EXT_SCM_SET_TO_STOP) == 0 ||
1438 (db0 ^ OPENSAFETY_MSG_SNMT_EXT_SCM_SET_TO_OP) == 0 ) )
1439 {
1440 opensafety_packet_receiver( message_tvb, pinfo, opensafety_tree, opensafety_item, packet, addr,
1441 OSS_FRAME_POS_ADDR + packet->frame.subframe1, packet->frame.subframe2, sdn );
1442 }
1443 else
1444 {
1445 opensafety_packet_sendreceiv ( message_tvb, pinfo, opensafety_tree, opensafety_item, packet, taddr,
1446 packet->frame.subframe2 + 3, addr, OSS_FRAME_POS_ADDR + packet->frame.subframe1,
1447 packet->frame.subframe2, sdn );
1448 }
1449
1450 snmt_tree = opensafety_packet_payloadtree ( pinfo, message_tvb, opensafety_tree, packet, ett_opensafety_snmt );
1451 /* Just a precaution, cause payloadtree actually sets the snmt pointer */
1452 if ( packet->payload.snmt == NULL )
1453 return;
1454
1455 if ( ( packet->msg_id == OPENSAFETY_MSG_SNMT_SERVICE_RESPONSE ) ||
1456 ( packet->msg_id == OPENSAFETY_MSG_SNMT_SERVICE_REQUEST ) )
1457 packet->payload.snmt->ext_msg_id = db0;
1458
1459 opensafety_packet_response(message_tvb, snmt_tree, packet, ( packet->msg_id & 0x04 ) == 0x04 );
1460
1461 if ( packet->is_request )
1462 {
1463 proto_tree_add_uint(snmt_tree, hf_oss_snmt_master, message_tvb, packet->frame.subframe2 + 3, 2, taddr);
1464 proto_tree_add_uint(snmt_tree, hf_oss_snmt_slave, message_tvb, OSS_FRAME_POS_ADDR + packet->frame.subframe1, 2, addr);
1465 }
1466 else
1467 {
1468 proto_tree_add_uint(snmt_tree, hf_oss_snmt_master, message_tvb, OSS_FRAME_POS_ADDR + packet->frame.subframe1, 2, addr);
1469 proto_tree_add_uint(snmt_tree, hf_oss_snmt_slave, message_tvb, packet->frame.subframe2 + 3, 2, taddr);
1470 }
1471
1472 /* Handle Acknowledge and Fail specifically */
1473 if ( ( ( db0 ^ OPENSAFETY_MSG_SNMT_EXT_SN_ACKNOWLEDGE) == 0 ) || ( db0 ^ OPENSAFETY_MSG_SNMT_EXT_SN_FAIL) == 0 )
1474 {
1475 byte = tvb_get_guint8(message_tvb, OSS_FRAME_POS_DATA + packet->frame.subframe1 + 1);
1476
1477 /* Handle a normal SN Fail */
1478 if ( byte != OPENSAFETY_ERROR_GROUP_ADD_PARAMETER )
1479 {
1480 if ( (db0 ^ OPENSAFETY_MSG_SNMT_EXT_SN_FAIL) == 0 )
1481 {
1482 proto_tree_add_uint(snmt_tree, hf_oss_snmt_service_id, message_tvb,
1483 OSS_FRAME_POS_DATA + packet->frame.subframe1, 1, packet->payload.snmt->ext_msg_id);
1484 col_append_fstr(pinfo->cinfo, COL_INFO, ", %s",
1485 val_to_str_const(packet->payload.snmt->ext_msg_id, opensafety_message_service_type, "Unknown"));
1486 }
1487 else if ( (db0 ^ OPENSAFETY_MSG_SNMT_EXT_SN_ACKNOWLEDGE) == 0 )
1488 {
1489 proto_tree_add_uint(snmt_tree, hf_oss_snmt_service_id, message_tvb, OSS_FRAME_POS_DATA + packet->frame.subframe1, 1, db0);
1490 col_append_fstr(pinfo->cinfo, COL_INFO, ", %s", val_to_str_const(db0, opensafety_message_service_type, "Unknown"));
1491 }
1492
1493 proto_tree_add_uint_format_value(snmt_tree, hf_oss_snmt_error_group, message_tvb, OSS_FRAME_POS_DATA + packet->frame.subframe1 + 1, 1,
1494 byte, "%s", ( byte == 0 ? "Device" : val_to_str(byte, opensafety_sn_fail_error_group, "Reserved [%d]" ) ) );
1495
1496 errcode = tvb_get_guint8(message_tvb, OSS_FRAME_POS_DATA + packet->frame.subframe1 + 2);
1497 proto_tree_add_uint_format_value(snmt_tree, hf_oss_snmt_error_code, message_tvb, OSS_FRAME_POS_DATA + packet->frame.subframe1 + 2, 1,
1498 errcode, "%s [%d]", ( errcode == 0 ? "Default" : "Vendor Specific" ), errcode );
1499
1500 col_append_fstr(pinfo->cinfo, COL_INFO, " - Group: %s; Code: %s",
1501 ( byte == 0 ? "Device" : val_to_str(byte, opensafety_sn_fail_error_group, "Reserved [%d]" ) ),
1502 ( errcode == 0 ? "Default" : "Vendor Specific" )
1503 );
1504
1505 packet->payload.snmt->add_param.exists = FALSE;
1506 packet->payload.snmt->error_code = errcode;
1507 }
1508 else
1509 {
1510 if ( (db0 ^ OPENSAFETY_MSG_SNMT_EXT_SN_FAIL) == 0 )
1511 {
1512 proto_tree_add_uint_format_value(snmt_tree, hf_oss_snmt_service_id, message_tvb, OSS_FRAME_POS_DATA + packet->frame.subframe1, 1,
1513 packet->payload.snmt->ext_msg_id, "%s [Request via SN Fail] (0x%02X)",
1514 val_to_str_const(byte, opensafety_sn_fail_error_group, "Unknown"), packet->payload.snmt->ext_msg_id);
1515 col_append_fstr(pinfo->cinfo, COL_INFO, ", %s", val_to_str_const(byte, opensafety_sn_fail_error_group, "Unknown"));
1516 } else if ( (db0 ^ OPENSAFETY_MSG_SNMT_EXT_SN_ACKNOWLEDGE) == 0 )
1517 {
1518 proto_tree_add_uint_format_value(snmt_tree, hf_oss_snmt_service_id, message_tvb, OSS_FRAME_POS_DATA + packet->frame.subframe1, 1,
1519 packet->payload.snmt->ext_msg_id, "Additional parameter missing [Response via SN Acknowledge] (0x%02X)", packet->payload.snmt->ext_msg_id);
1520 col_append_str(pinfo->cinfo, COL_INFO, ", Additional parameter missing");
1521 }
1522
1523 errcode = tvb_get_guint8(message_tvb, OSS_FRAME_POS_DATA + packet->frame.subframe1 + 2);
1524 packet->payload.snmt->add_param.exists = TRUE;
1525 packet->payload.snmt->add_param.id = errcode;
1526 packet->payload.snmt->add_param.set = ( errcode & 0x0F ) + 1;
1527 packet->payload.snmt->add_param.full = ( ( errcode & 0xF0 ) == 0xF0 );
1528
1529 /* Handle an additional parameter request */
1530 proto_tree_add_uint(snmt_tree, hf_oss_ssdo_extpar_parset, message_tvb,
1531 OSS_FRAME_POS_DATA + packet->frame.subframe1 + 2, 1, ( errcode & 0x0F ) + 1 );
1532
1533 proto_tree_add_boolean(snmt_tree, hf_oss_snmt_param_type, message_tvb,
1534 OSS_FRAME_POS_DATA + packet->frame.subframe1 + 2, 1, ( ( errcode & 0xF0 ) != 0xF0 ) );
1535 }
1536 }
1537 else if ( (OSS_FRAME_ID_T(message_tvb, packet->frame.subframe1) ^ OPENSAFETY_MSG_SNMT_SERVICE_RESPONSE) == 0 )
1538 {
1539 proto_tree_add_uint(snmt_tree, hf_oss_snmt_service_id, message_tvb,
1540 OSS_FRAME_POS_DATA + packet->frame.subframe1, 1, packet->payload.snmt->ext_msg_id);
1541 col_append_fstr(pinfo->cinfo, COL_INFO, ", %s",
1542 val_to_str_const(packet->payload.snmt->ext_msg_id, opensafety_message_service_type, "Unknown"));
1543
1544 if ( (db0 ^ OPENSAFETY_MSG_SNMT_EXT_SN_ASSIGNED_UDID_SCM) == 0 )
1545 {
1546 opensafety_parse_scm_udid ( message_tvb, pinfo, snmt_tree, packet, OSS_FRAME_POS_DATA + packet->frame.subframe1 + 1 );
1547 }
1548 else if ( ( db0 ^ OPENSAFETY_MSG_SNMT_EXT_SN_ASSIGNED_ADDITIONAL_SADR) == 0 )
1549 {
1550 packet->payload.snmt->add_saddr.actual = OSS_FRAME_ADDR_T(message_tvb, packet->frame.subframe1 + OSS_FRAME_POS_DATA + 1);
1551 proto_tree_add_uint(snmt_tree, hf_oss_snmt_ext_addsaddr, message_tvb, OSS_FRAME_POS_DATA + packet->frame.subframe1 + 1, 2,
1552 packet->payload.snmt->add_saddr.actual );
1553
1554 packet->payload.snmt->add_saddr.additional = OSS_FRAME_ADDR_T(message_tvb, packet->frame.subframe1 + OSS_FRAME_POS_DATA + 3);
1555 proto_tree_add_uint(snmt_tree, hf_oss_snmt_ext_addtxspdo, message_tvb, OSS_FRAME_POS_DATA + packet->frame.subframe1 + 3, 2,
1556 packet->payload.snmt->add_saddr.additional);
1557
1558 col_append_fstr(pinfo->cinfo, COL_INFO, " [0x%04X => 0x%04X]",
1559 OSS_FRAME_ADDR_T(message_tvb, packet->frame.subframe1 + OSS_FRAME_POS_DATA + 1),
1560 OSS_FRAME_ADDR_T(message_tvb, packet->frame.subframe1 + OSS_FRAME_POS_DATA + 3));
1561 }
1562 else if ( ( db0 ^ OPENSAFETY_MSG_SNMT_EXT_ASSIGNED_INIT_CT) == 0 )
1563 {
1564 packet->payload.snmt->init_ct =
1565 tvb_get_guint40(message_tvb, packet->frame.subframe1 + OSS_FRAME_POS_DATA + 1, ENC_BIG_ENDIAN);
1566 proto_tree_add_item(snmt_tree, hf_oss_snmt_ext_initct, message_tvb,
1567 packet->frame.subframe1 + OSS_FRAME_POS_DATA + 1, 5, ENC_BIG_ENDIAN );
1568 }
1569 }
1570 else if ( (OSS_FRAME_ID_T(message_tvb, packet->frame.subframe1) ^ OPENSAFETY_MSG_SNMT_SERVICE_REQUEST) == 0 )
1571 {
1572 proto_tree_add_uint(snmt_tree, hf_oss_snmt_service_id, message_tvb, OSS_FRAME_POS_DATA + packet->frame.subframe1, 1, db0);
1573 col_append_fstr(pinfo->cinfo, COL_INFO, ", %s", val_to_str_const(db0, opensafety_message_service_type, "Unknown"));
1574
1575 if ( (db0 ^ OPENSAFETY_MSG_SNMT_EXT_SCM_SET_TO_STOP) == 0 || (db0 ^ OPENSAFETY_MSG_SNMT_EXT_SCM_SET_TO_OP) == 0 )
1576 {
1577 proto_tree_add_uint(snmt_tree, hf_oss_snmt_scm, message_tvb, OSS_FRAME_POS_ADDR + packet->frame.subframe1, 2, addr);
1578 proto_tree_add_uint(snmt_tree, hf_oss_snmt_tool, message_tvb, packet->frame.subframe2 + 3, 2, taddr);
1579 }
1580 else if ( (db0 ^ OPENSAFETY_MSG_SNMT_EXT_SN_ASSIGN_UDID_SCM) == 0 )
1581 {
1582 opensafety_parse_scm_udid ( message_tvb, pinfo, snmt_tree, packet, OSS_FRAME_POS_DATA + packet->frame.subframe1 + 1 );
1583 }
1584 else if ( ( db0 ^ OPENSAFETY_MSG_SNMT_EXT_ASSIGN_INIT_CT) == 0 )
1585 {
1586 packet->payload.snmt->init_ct =
1587 tvb_get_guint40(message_tvb, packet->frame.subframe1 + OSS_FRAME_POS_DATA + 1, ENC_BIG_ENDIAN);
1588 proto_tree_add_item(snmt_tree, hf_oss_snmt_ext_initct, message_tvb,
1589 packet->frame.subframe1 + OSS_FRAME_POS_DATA + 1, 5, ENC_BIG_ENDIAN );
1590 }
1591 else
1592 {
1593 if ( (db0 ^ OPENSAFETY_MSG_SNMT_EXT_SN_SET_TO_OP) == 0 )
1594 {
1595 proto_tree_add_item ( snmt_tree, hf_oss_sod_par_timestamp, message_tvb,
1596 OSS_FRAME_POS_DATA + packet->frame.subframe1 + 1, 4, ENC_LITTLE_ENDIAN );
1597 }
1598 else if ( ( db0 ^ OPENSAFETY_MSG_SNMT_EXT_ASSIGN_ADDITIONAL_SADR) == 0 )
1599 {
1600 packet->payload.snmt->add_saddr.actual = OSS_FRAME_ADDR_T(message_tvb, packet->frame.subframe1 + OSS_FRAME_POS_DATA + 1);
1601 proto_tree_add_uint(snmt_tree, hf_oss_snmt_ext_addsaddr, message_tvb, OSS_FRAME_POS_DATA + packet->frame.subframe1 + 1, 2,
1602 packet->payload.snmt->add_saddr.actual );
1603
1604 packet->payload.snmt->add_saddr.additional = OSS_FRAME_ADDR_T(message_tvb, packet->frame.subframe1 + OSS_FRAME_POS_DATA + 3);
1605 proto_tree_add_uint(snmt_tree, hf_oss_snmt_ext_addtxspdo, message_tvb, OSS_FRAME_POS_DATA + packet->frame.subframe1 + 3, 2,
1606 packet->payload.snmt->add_saddr.additional);
1607
1608 col_append_fstr(pinfo->cinfo, COL_INFO, " [0x%04X => 0x%04X]",
1609 OSS_FRAME_ADDR_T(message_tvb, packet->frame.subframe1 + OSS_FRAME_POS_DATA + 1),
1610 OSS_FRAME_ADDR_T(message_tvb, packet->frame.subframe1 + OSS_FRAME_POS_DATA + 3));
1611 }
1612
1613 }
1614 }
1615 else if ( (OSS_FRAME_ID_T(message_tvb, packet->frame.subframe1) ^ OPENSAFETY_MSG_SNMT_SADR_ASSIGNED) == 0 ||
1616 (OSS_FRAME_ID_T(message_tvb, packet->frame.subframe1) ^ OPENSAFETY_MSG_SNMT_ASSIGN_SADR) == 0 ||
1617 (OSS_FRAME_ID_T(message_tvb, packet->frame.subframe1) ^ OPENSAFETY_MSG_SNMT_RESPONSE_UDID) == 0 )
1618 {
1619 if (dataLength > 0)
1620 {
1621 packet->payload.snmt->sn_udid = wmem_strdup(pinfo->pool,
1622 tvb_bytes_to_str_punct(pinfo->pool, message_tvb, OSS_FRAME_POS_DATA + packet->frame.subframe1 + 1, 6, ':' ) );
1623 proto_tree_add_item(snmt_tree, hf_oss_snmt_udid, message_tvb, OSS_FRAME_POS_DATA + packet->frame.subframe1, 6, ENC_NA);
1624 }
1625 }
1626 }
1627
1628 static gboolean
dissect_opensafety_checksum(tvbuff_t * message_tvb,packet_info * pinfo,proto_tree * opensafety_tree,opensafety_packet_info * packet)1629 dissect_opensafety_checksum(tvbuff_t *message_tvb, packet_info *pinfo, proto_tree *opensafety_tree,
1630 opensafety_packet_info *packet )
1631 {
1632 guint16 frame1_crc, frame2_crc;
1633 guint16 calc1_crc, calc2_crc;
1634 guint dataLength, frame2Length;
1635 guint8 *bytesf2, *bytesf1, ctr = 0, crcType = OPENSAFETY_CHECKSUM_CRC8;
1636 proto_item *item;
1637 proto_tree *checksum_tree;
1638 gint start;
1639 gint length;
1640 gboolean isSlim = FALSE;
1641 gboolean isSNMT = FALSE;
1642 gboolean isSPDO = FALSE;
1643 GByteArray *scmUDID = NULL;
1644
1645 dataLength = OSS_FRAME_LENGTH_T(message_tvb, packet->frame.subframe1);
1646 start = OSS_FRAME_POS_DATA + dataLength + packet->frame.subframe1;
1647
1648 if (OSS_FRAME_LENGTH_T(message_tvb, packet->frame.subframe1) > OSS_PAYLOAD_MAXSIZE_FOR_CRC8)
1649 frame1_crc = tvb_get_letohs(message_tvb, start);
1650 else
1651 frame1_crc = tvb_get_guint8(message_tvb, start);
1652
1653 if ( packet->msg_type == OPENSAFETY_SLIM_SSDO_MESSAGE_TYPE )
1654 isSlim = TRUE;
1655 if ( packet->msg_type == OPENSAFETY_SNMT_MESSAGE_TYPE )
1656 isSNMT = TRUE;
1657 if ( packet->msg_type == OPENSAFETY_SPDO_MESSAGE_TYPE )
1658 isSPDO = TRUE;
1659
1660 frame2Length = (isSlim ? 0 : dataLength) + 5;
1661
1662 length = (dataLength > OSS_PAYLOAD_MAXSIZE_FOR_CRC8 ? OPENSAFETY_CHECKSUM_CRC16 : OPENSAFETY_CHECKSUM_CRC8);
1663 item = proto_tree_add_uint_format(opensafety_tree, hf_oss_crc, message_tvb, start, length, frame1_crc,
1664 "CRC for subframe #1: 0x%04X", frame1_crc);
1665
1666 checksum_tree = proto_item_add_subtree(item, ett_opensafety_checksum);
1667
1668 bytesf1 = (guint8*)tvb_memdup(pinfo->pool, message_tvb, packet->frame.subframe1, dataLength + 4);
1669
1670 crcType = packet->crc.type;
1671 calc1_crc = packet->crc.frame1;
1672
1673 if ( ! isSlim && crcType == OPENSAFETY_CHECKSUM_CRC16SLIM )
1674 expert_add_info(pinfo, item, &ei_crc_slimssdo_instead_of_spdo );
1675
1676 item = proto_tree_add_boolean(checksum_tree, hf_oss_crc_valid, message_tvb,
1677 packet->frame.subframe1, dataLength + 4, (frame1_crc == calc1_crc));
1678 proto_item_set_generated(item);
1679 if ( crcType == OPENSAFETY_CHECKSUM_INVALID || frame1_crc != calc1_crc )
1680 expert_add_info(pinfo, item, &ei_crc_frame_1_invalid );
1681
1682 /* using the defines, as the values can change */
1683 proto_tree_add_uint(checksum_tree, hf_oss_crc_type, message_tvb, start, length, crcType );
1684
1685 start = packet->frame.subframe2 + (isSlim ? 5 : dataLength + OSS_FRAME_POS_DATA + 1 );
1686 if (OSS_FRAME_LENGTH_T(message_tvb, packet->frame.subframe1) > OSS_PAYLOAD_MAXSIZE_FOR_CRC8)
1687 frame2_crc = tvb_get_letohs(message_tvb, start);
1688 else
1689 frame2_crc = tvb_get_guint8(message_tvb, start);
1690
1691 /* 0xFFFF is an invalid CRC16 value, therefore valid for initialization. Needed, because
1692 * otherwise this function may return without setting calc2_crc, and this does not go well
1693 * with the compiler */
1694 calc2_crc = 0xFFFF;
1695
1696 /* Currently SPDO 40 Bit CRC2 support is broken. Will be implemented at a later state, after
1697 * the first generation of openSAFETY devices using 40 bit counter are available */
1698 if ( isSPDO && packet->payload.spdo->flags.enabled40bit == TRUE )
1699 packet->scm_udid_valid = FALSE;
1700
1701 /* This used to be an option. But only, because otherwise there would be three different
1702 * crc calculations taking place within dissection. As we could reduce this by one, the
1703 * global option has been changed to the simple validity question, if we have enough information
1704 * to calculate the second crc, meaning, if the SCM udid is known, or if we have an SNMT msg */
1705 if ( isSNMT || packet->scm_udid_valid )
1706 {
1707 bytesf2 = (guint8*)tvb_memdup(pinfo->pool, message_tvb, packet->frame.subframe2, frame2Length + length);
1708
1709 /* SLIM SSDO messages, do not contain a payload in frame2 */
1710 if ( isSlim == TRUE )
1711 dataLength = 0;
1712
1713 scmUDID = g_byte_array_new();
1714 packet->crc.valid2 = FALSE;
1715 if ( isSNMT || ( hex_str_to_bytes((local_scm_udid != NULL ? local_scm_udid : global_scm_udid), scmUDID, TRUE) && scmUDID->len == 6 ) )
1716 {
1717 if ( !isSNMT )
1718 {
1719 for ( ctr = 0; ctr < 6; ctr++ )
1720 bytesf2[ctr] = bytesf2[ctr] ^ (guint8)(scmUDID->data[ctr]);
1721 if ( isSPDO )
1722 {
1723
1724 /* allow only valid SPDO flags */
1725 if ( packet->msg_id == OPENSAFETY_MSG_SPDO_DATA_ONLY )
1726 {
1727 if ( packet->payload.spdo->flags.enabled40bit == TRUE )
1728 {
1729 /* we assume the OPENSAFETY_DEFAULT_DOMAIN (0x01) for 40 bit for now */
1730 bytesf2[0] = bytesf2[0] ^ (bytesf2[0] ^ OPENSAFETY_DEFAULT_DOMAIN ^ bytesf1[0]);
1731 bytesf2[1] = bytesf2[1] ^ (bytesf2[1] ^ bytesf1[1]);
1732 bytesf2[3] = 0;
1733 }
1734 }
1735 }
1736
1737 if ( isSlim || packet->frame.length == 11 )
1738 frame2_crc ^= ((guint8)scmUDID->data[5]);
1739
1740 /*
1741 * If the second frame is 6 or 7 (slim) bytes in length, we have to decode the found
1742 * frame crc again. This must be done using the byte array, as the unxor operation
1743 * had to take place.
1744 */
1745 if ( dataLength == 0 )
1746 {
1747 if ( isSlim && length == 2 )
1748 frame2_crc = ( bytesf2[6] << 8 ) + bytesf2[5];
1749 }
1750
1751 }
1752
1753 item = proto_tree_add_uint_format(opensafety_tree, hf_oss_crc, message_tvb, start, length, frame2_crc,
1754 "CRC for subframe #2: 0x%04X", frame2_crc);
1755
1756 checksum_tree = proto_item_add_subtree(item, ett_opensafety_checksum);
1757
1758 if ( OSS_FRAME_LENGTH_T(message_tvb, packet->frame.subframe1) > OSS_PAYLOAD_MAXSIZE_FOR_CRC8 )
1759 {
1760 calc2_crc = crc16_0x755B(bytesf2, frame2Length, 0);
1761 if ( frame2_crc != calc2_crc )
1762 calc2_crc = crc16_0x5935(bytesf2, frame2Length, 0);
1763 }
1764 else
1765 calc2_crc = crc8_0x2F(bytesf2, frame2Length, 0);
1766
1767 item = proto_tree_add_boolean(checksum_tree, hf_oss_crc2_valid, message_tvb,
1768 packet->frame.subframe2, frame2Length, (frame2_crc == calc2_crc));
1769 proto_item_set_generated(item);
1770
1771 if ( frame2_crc != calc2_crc )
1772 {
1773 item = proto_tree_add_uint_format(checksum_tree, hf_oss_crc, message_tvb,
1774 packet->frame.subframe2, frame2Length, calc2_crc, "Calculated CRC: 0x%04X", calc2_crc);
1775 proto_item_set_generated(item);
1776 expert_add_info(pinfo, item, &ei_crc_frame_2_invalid );
1777 }
1778 else
1779 {
1780 if ( global_opensafety_debug_verbose && ( isSlim || ( !isSNMT && packet->frame.length == 11 ) ) )
1781 expert_add_info(pinfo, item, &ei_crc_frame_2_scm_udid_encoded );
1782
1783 packet->crc.valid2 = TRUE;
1784 }
1785 }
1786 else
1787 expert_add_info(pinfo, item, &ei_crc_frame_2_unknown_scm_udid );
1788
1789 g_byte_array_free(scmUDID, TRUE);
1790 }
1791
1792 /* For a correct calculation of the second crc we need to know the scm udid.
1793 * If the dissection of the second frame has been triggered, we integrate the
1794 * crc for frame2 into the result */
1795 return (gboolean) (frame1_crc == calc1_crc) &&
1796 ( ( isSNMT || packet->scm_udid_valid ) == TRUE ? (frame2_crc == calc2_crc) : TRUE);
1797 }
1798
1799 static gint
check_scmudid_validity(opensafety_packet_info * packet,tvbuff_t * message_tvb)1800 check_scmudid_validity(opensafety_packet_info *packet, tvbuff_t *message_tvb)
1801 {
1802 guint8 b_ID, spdoFlags, udidLen;
1803 GByteArray *scmUDID = NULL;
1804
1805 packet->scm_udid_valid = FALSE;
1806 scmUDID = g_byte_array_new();
1807
1808 if ( hex_str_to_bytes((local_scm_udid != NULL ? local_scm_udid : global_scm_udid), scmUDID, TRUE) && scmUDID->len == 6 )
1809 {
1810 packet->scm_udid_valid = TRUE;
1811
1812 /* Now confirm, that the xor operation was successful. The ID fields of both frames have to be the same */
1813 b_ID = tvb_get_guint8(message_tvb, packet->frame.subframe2 + 1) ^ (guint8)(scmUDID->data[OSS_FRAME_POS_ID]);;
1814 if ( ( OSS_FRAME_ID_T(message_tvb, packet->frame.subframe1) ^ b_ID ) != 0 )
1815 packet->scm_udid_valid = FALSE;
1816
1817 /* The IDs do not match, but the SCM UDID could still be ok. This happens, if this packet
1818 * utilizes the 40 bit counter. Therefore we reduce the check here only to the feature
1819 * flags, but only if the package is a SPDO Data Only (because everything else uses 16 bit. */
1820 if ( packet->msg_id == OPENSAFETY_MSG_SPDO_DATA_ONLY )
1821 {
1822 spdoFlags = ( tvb_get_guint8(message_tvb, packet->frame.subframe2 + 4 ) ^ scmUDID->data[4] ) ;
1823 spdoFlags = ( spdoFlags >> 2 ) & OPENSAFETY_SPDO_FEATURE_FLAGS;
1824 if ( ( spdoFlags & OPENSAFETY_SPDO_FEAT_40BIT_USED ) == OPENSAFETY_SPDO_FEAT_40BIT_USED )
1825 packet->scm_udid_valid = TRUE;
1826 }
1827
1828 if ( packet->scm_udid_valid == TRUE )
1829 memcpy(packet->scm_udid, scmUDID->data, 6);
1830 }
1831
1832 udidLen = scmUDID->len;
1833
1834 g_byte_array_free( scmUDID, TRUE);
1835
1836 return udidLen;
1837 }
1838
1839 static gboolean
dissect_opensafety_message(opensafety_packet_info * packet,tvbuff_t * message_tvb,packet_info * pinfo,proto_item * opensafety_item,proto_tree * opensafety_tree,guint8 u_nrInPackage,guint8 previous_msg_id)1840 dissect_opensafety_message(opensafety_packet_info *packet,
1841 tvbuff_t *message_tvb, packet_info *pinfo,
1842 proto_item *opensafety_item, proto_tree *opensafety_tree,
1843 guint8 u_nrInPackage, guint8 previous_msg_id)
1844 {
1845 guint8 ctr, udidLen;
1846 proto_item *item;
1847 gboolean messageTypeUnknown, crcValid;
1848
1849 messageTypeUnknown = FALSE;
1850
1851 for ( ctr = 0; ctr < 6; ctr++ )
1852 packet->scm_udid[ctr] = 0;
1853
1854 packet->saddr = OSS_FRAME_ADDR_T(message_tvb, packet->frame.subframe1);
1855 /* Sender / Receiver is determined by message type */
1856 packet->sender = 0;
1857 packet->receiver = 0;
1858
1859 /* SPDO is handled below */
1860 if ( packet->msg_type != OPENSAFETY_SPDO_MESSAGE_TYPE )
1861 {
1862 col_append_fstr(pinfo->cinfo, COL_INFO, (u_nrInPackage > 1 ? " | %s" : "%s" ),
1863 val_to_str(packet->msg_id, opensafety_message_type_values, "Unknown Message (0x%02X) "));
1864 }
1865
1866 item = proto_tree_add_uint(opensafety_tree, hf_oss_byte_offset, packet->frame.frame_tvb, 0, 1, packet->frame.byte_offset);
1867 proto_item_set_generated(item);
1868
1869 if ( packet->msg_type == OPENSAFETY_SNMT_MESSAGE_TYPE )
1870 {
1871 proto_item_append_text(opensafety_item, ", SNMT");
1872 dissect_opensafety_snmt_message ( message_tvb, pinfo, opensafety_tree, packet, opensafety_item );
1873 }
1874 else
1875 {
1876 udidLen = check_scmudid_validity(packet, message_tvb);
1877
1878 if ( strlen( (local_scm_udid != NULL ? local_scm_udid : global_scm_udid) ) > 0 && udidLen == 6 )
1879 {
1880 if ( local_scm_udid != NULL )
1881 {
1882 item = proto_tree_add_string(opensafety_tree, hf_oss_scm_udid_auto, message_tvb, 0, 0, local_scm_udid);
1883 if ( ! packet->scm_udid_valid )
1884 expert_add_info(pinfo, item, &ei_message_id_field_mismatch );
1885 }
1886 else
1887 item = proto_tree_add_string(opensafety_tree, hf_oss_scm_udid, message_tvb, 0, 0, global_scm_udid);
1888 proto_item_set_generated(item);
1889 }
1890
1891 item = proto_tree_add_boolean(opensafety_tree, hf_oss_scm_udid_valid, message_tvb, 0, 0, packet->scm_udid_valid);
1892 if ( udidLen != 6 )
1893 expert_add_info(pinfo, item, &ei_scmudid_invalid_preference );
1894 proto_item_set_generated(item);
1895
1896 if ( packet->msg_type == OPENSAFETY_SSDO_MESSAGE_TYPE || packet->msg_type == OPENSAFETY_SLIM_SSDO_MESSAGE_TYPE )
1897 {
1898 proto_item_append_text(opensafety_item,
1899 (packet->msg_type == OPENSAFETY_SLIM_SSDO_MESSAGE_TYPE) ? ", Slim SSDO" : ", SSDO");
1900 dissect_opensafety_ssdo_message ( message_tvb, pinfo, opensafety_tree, packet, opensafety_item );
1901 }
1902 else if ( packet->msg_type == OPENSAFETY_SPDO_MESSAGE_TYPE )
1903 {
1904 proto_item_append_text(opensafety_item, ", SPDO" );
1905 dissect_opensafety_spdo_message ( message_tvb, pinfo, opensafety_tree, packet, opensafety_item );
1906
1907 /* Now we know packet->sender, therefore we can add the info text */
1908 if ( previous_msg_id != packet->msg_id )
1909 {
1910 col_append_fstr(pinfo->cinfo, COL_INFO, (u_nrInPackage > 1 ? " | %s - 0x%03X" : "%s - 0x%03X" ),
1911 val_to_str(packet->msg_id, opensafety_message_type_values, "Unknown Message (0x%02X) "),
1912 packet->sender );
1913 } else {
1914 col_append_fstr(pinfo->cinfo, COL_INFO, ", 0x%03X", packet->sender );
1915 }
1916 }
1917 else
1918 {
1919 messageTypeUnknown = TRUE;
1920 proto_item_append_text(opensafety_item, ", Unknown" );
1921 }
1922 }
1923
1924 crcValid = FALSE;
1925 item = proto_tree_add_uint(opensafety_tree, hf_oss_length,
1926 message_tvb, OSS_FRAME_POS_LEN + packet->frame.subframe1, 1,
1927 OSS_FRAME_LENGTH_T(message_tvb, packet->frame.subframe1));
1928 if ( messageTypeUnknown )
1929 {
1930 expert_add_info(pinfo, item, &ei_message_unknown_type );
1931 }
1932 else
1933 {
1934 crcValid = dissect_opensafety_checksum ( message_tvb, pinfo, opensafety_tree, packet );
1935 }
1936
1937 /* with SNMT's we can check if the ID's for the frames match. Rare randomized packages do have
1938 * an issue, where an frame 1 can be valid. The id's for both frames must differ, as well as
1939 * the addresses, but addresses won't be checked yet, as there are issues with SDN xored on it. */
1940 if ( crcValid && packet->msg_type == OPENSAFETY_SNMT_MESSAGE_TYPE )
1941 {
1942 if ( OSS_FRAME_ID_T(message_tvb, packet->frame.subframe1) != OSS_FRAME_ID_T(message_tvb, packet->frame.subframe2) )
1943 expert_add_info(pinfo, opensafety_item, &ei_crc_frame_1_valid_frame2_invalid );
1944 }
1945
1946 return TRUE;
1947 }
1948
1949 static gboolean
opensafety_package_dissector(const gchar * protocolName,const gchar * sub_diss_handle,gboolean b_frame2First,gboolean do_byte_swap,guint8 force_nr_in_package,tvbuff_t * given_tvb,packet_info * pinfo,proto_tree * tree,guint8 transporttype)1950 opensafety_package_dissector(const gchar *protocolName, const gchar *sub_diss_handle,
1951 gboolean b_frame2First, gboolean do_byte_swap, guint8 force_nr_in_package,
1952 tvbuff_t *given_tvb, packet_info *pinfo, proto_tree *tree, guint8 transporttype )
1953 {
1954 tvbuff_t *next_tvb = NULL, *gap_tvb = NULL, *message_tvb = NULL;
1955 guint length, len, frameOffset, frameLength, nodeAddress, gapStart;
1956 guint8 *swbytes;
1957 gboolean handled, dissectorCalled, call_sub_dissector, markAsMalformed;
1958 guint8 type, found, i, tempByte, previous_msg_id;
1959 guint16 frameStart1, frameStart2, byte_offset;
1960 gint reported_len;
1961 dissector_handle_t protocol_dissector = NULL;
1962 proto_item *opensafety_item;
1963 proto_tree *opensafety_tree;
1964
1965 opensafety_packet_info *packet = NULL;
1966
1967 handled = FALSE;
1968 dissectorCalled = FALSE;
1969 call_sub_dissector = FALSE;
1970 markAsMalformed = FALSE;
1971 previous_msg_id = 0;
1972
1973 /* registering frame end routine, to prevent a malformed dissection preventing
1974 * further dissector calls (see bug #6950) */
1975 register_frame_end_routine(pinfo, reset_dissector);
1976
1977 length = tvb_reported_length(given_tvb);
1978 /* Minimum package length is 11 */
1979 if ( length < OSS_MINIMUM_LENGTH )
1980 return FALSE;
1981
1982 /* Determine dissector handle for sub-dissection */
1983 if ( strlen( sub_diss_handle ) > 0 )
1984 {
1985 call_sub_dissector = TRUE;
1986 protocol_dissector = find_dissector ( sub_diss_handle );
1987 if ( protocol_dissector == NULL )
1988 protocol_dissector = data_dissector;
1989 }
1990
1991 reported_len = tvb_reported_length_remaining(given_tvb, 0);
1992
1993 /* This will swap the bytes according to MBTCP encoding */
1994 if ( do_byte_swap == TRUE && global_mbtcp_big_endian == TRUE )
1995 {
1996 /* Because of padding bytes at the end of the frame, tvb_memdup could lead
1997 * to a "openSAFETY truncated" message. By ensuring, that we have enough
1998 * bytes to copy, this will be prevented. */
1999 if ( ! tvb_bytes_exist ( given_tvb, 0, length ) )
2000 return FALSE;
2001
2002 swbytes = (guint8 *) tvb_memdup( pinfo->pool, given_tvb, 0, length);
2003
2004 /* Wordswapping for modbus detection */
2005 /* Only a even number of bytes can be swapped */
2006 len = (length / 2);
2007 for ( i = 0; i < len; i++ )
2008 {
2009 tempByte = swbytes [ 2 * i ]; swbytes [ 2 * i ] = swbytes [ 2 * i + 1 ]; swbytes [ 2 * i + 1 ] = tempByte;
2010 }
2011
2012 message_tvb = tvb_new_real_data(swbytes, length, reported_len);
2013 } else {
2014 message_tvb = given_tvb;
2015 }
2016
2017 frameOffset = 0;
2018 frameLength = 0;
2019 found = 0;
2020
2021 /* Counter to determine gaps between openSAFETY packages */
2022 gapStart = 0;
2023
2024 while ( frameOffset < length )
2025 {
2026 /* Reset the next_tvb buffer */
2027 next_tvb = NULL;
2028
2029 /* Smallest possible frame size is 11, but this check must ensure, that even the last frame
2030 * will get considered, which leads us with 10, as the first byte checked is the second one */
2031 if ( tvb_captured_length_remaining(message_tvb, frameOffset ) < ( OSS_MINIMUM_LENGTH - 1 ) )
2032 break;
2033
2034 /* Resetting packet, to ensure, that findSafetyFrame starts with a fresh frame.
2035 * As only packet_scope is used, this will not polute memory too much and get's
2036 * cleared with the next packet anyway */
2037 packet = wmem_new0(pinfo->pool, opensafety_packet_info);
2038
2039 /* Finding the start of the first possible safety frame */
2040 if ( findSafetyFrame(pinfo, message_tvb, frameOffset, b_frame2First, &frameOffset, &frameLength, packet) )
2041 {
2042 /* if packet msg_id is not null, it still might be an incorrect frame, as there is no validity
2043 * check in findSafetyFrame for the msg id (this happens later in this routine)
2044 * frameLength is calculated/read directly from the dissected data. If frameLength and frameOffset together
2045 * are bigger than the reported length, the package is not really an openSAFETY package */
2046 if ( packet->msg_id == 0 || ( frameOffset + frameLength ) > (guint)reported_len )
2047 break;
2048
2049 found++;
2050
2051 byte_offset = ( b_frame2First ? 0 : frameOffset );
2052 /* We determine a possible position for frame 1 and frame 2 */
2053 if ( b_frame2First )
2054 {
2055 frameStart1 = findFrame1Position (pinfo, message_tvb, byte_offset, frameLength, FALSE );
2056 frameStart2 = 0;
2057 }
2058 else
2059 {
2060 frameStart1 = 0;
2061 frameStart2 = ((OSS_FRAME_LENGTH_T(message_tvb, byte_offset + frameStart1) - 1) +
2062 (OSS_FRAME_LENGTH_T(message_tvb, byte_offset + frameStart1) > OSS_PAYLOAD_MAXSIZE_FOR_CRC8 ? OSS_SLIM_FRAME2_WITH_CRC16 : OSS_SLIM_FRAME2_WITH_CRC8));
2063 }
2064
2065 /* If both frame starts are equal, something went wrong. In which case, we retract the found entry, and
2066 * also increase the search offset, just doing a continue will result in an infinite loop. */
2067 if (frameStart1 == frameStart2)
2068 {
2069 found--;
2070 frameOffset += frameLength;
2071 continue;
2072 }
2073
2074 /* We determine the possible type, and return false, if there could not be one */
2075 packet->msg_id = OSS_FRAME_ID_T(message_tvb, byte_offset + frameStart1);
2076 if ( ( packet->msg_id & OPENSAFETY_SLIM_SSDO_MESSAGE_TYPE ) == OPENSAFETY_SLIM_SSDO_MESSAGE_TYPE )
2077 type = OPENSAFETY_SLIM_SSDO_MESSAGE_TYPE;
2078 else if ( ( packet->msg_id & OPENSAFETY_SSDO_MESSAGE_TYPE ) == OPENSAFETY_SSDO_MESSAGE_TYPE )
2079 type = OPENSAFETY_SSDO_MESSAGE_TYPE;
2080 else if ( ( packet->msg_id & OPENSAFETY_SPDO_MESSAGE_TYPE ) == OPENSAFETY_SPDO_MESSAGE_TYPE )
2081 type = OPENSAFETY_SPDO_MESSAGE_TYPE;
2082 else if ( ( packet->msg_id & OPENSAFETY_SNMT_MESSAGE_TYPE ) == OPENSAFETY_SNMT_MESSAGE_TYPE )
2083 type = OPENSAFETY_SNMT_MESSAGE_TYPE;
2084 else
2085 {
2086 /* This is an invalid openSAFETY package, but it could be an undetected slim ssdo message. This specific error
2087 * will only occur, if findFrame1Position is in play. So we search once more, but this time calculating the CRC.
2088 * The reason for the second run is, that calculating the CRC is time consuming. */
2089 if ( b_frame2First )
2090 {
2091 /* Now let's check again, but this time calculate the CRC */
2092 frameStart1 = findFrame1Position(pinfo, message_tvb, ( b_frame2First ? 0 : frameOffset ), frameLength, TRUE );
2093 frameStart2 = 0;
2094
2095 packet->msg_id = OSS_FRAME_ID_T(message_tvb, byte_offset + frameStart1);
2096 if ( ( packet->msg_id & OPENSAFETY_SLIM_SSDO_MESSAGE_TYPE ) == OPENSAFETY_SLIM_SSDO_MESSAGE_TYPE )
2097 type = OPENSAFETY_SLIM_SSDO_MESSAGE_TYPE;
2098 else if ( ( packet->msg_id & OPENSAFETY_SSDO_MESSAGE_TYPE ) == OPENSAFETY_SSDO_MESSAGE_TYPE )
2099 type = OPENSAFETY_SSDO_MESSAGE_TYPE;
2100 else if ( ( packet->msg_id & OPENSAFETY_SPDO_MESSAGE_TYPE ) == OPENSAFETY_SPDO_MESSAGE_TYPE )
2101 type = OPENSAFETY_SPDO_MESSAGE_TYPE;
2102 else if ( ( packet->msg_id & OPENSAFETY_SNMT_MESSAGE_TYPE ) == OPENSAFETY_SNMT_MESSAGE_TYPE )
2103 type = OPENSAFETY_SNMT_MESSAGE_TYPE;
2104 else {
2105 /* Skip this frame. We cannot continue without
2106 advancing frameOffset - just doing a continue
2107 will result in an infinite loop. Advancing with 1 will
2108 lead to infinite loop, advancing with frameLength might miss
2109 some packages*/
2110 frameOffset += 2;
2111 found--;
2112 continue;
2113 }
2114 } else {
2115 /* As stated above, you cannot just continue
2116 without advancing frameOffset. Advancing with 1 will
2117 lead to infinite loop, advancing with frameLength might miss
2118 some packages*/
2119 frameOffset += 2;
2120 found--;
2121 continue;
2122 }
2123 }
2124
2125 /* Sorting messages for transporttype */
2126 if ( global_classify_transport && transporttype != OPENSAFETY_ANY_TRANSPORT )
2127 {
2128 /* Cyclic data is transported via SPDOs and acyclic is transported via SNMT, SSDO. Everything
2129 * else is misclassification */
2130 if ( ( transporttype == OPENSAFETY_ACYCLIC_DATA && type == OPENSAFETY_SPDO_MESSAGE_TYPE ) ||
2131 ( transporttype == OPENSAFETY_CYCLIC_DATA && type != OPENSAFETY_SPDO_MESSAGE_TYPE ) )
2132 {
2133 frameOffset += 2;
2134 found--;
2135 continue;
2136 }
2137 }
2138
2139 /* Some faulty packages do indeed have a valid first frame, but the second is
2140 * invalid. These checks should prevent most faulty detections */
2141 if ( type != OPENSAFETY_SPDO_MESSAGE_TYPE )
2142 {
2143 /* Is the given type at least known? */
2144 gint idx = -1;
2145 try_val_to_str_idx(OSS_FRAME_ID_T(message_tvb, byte_offset + frameStart1), opensafety_message_type_values, &idx );
2146 /* Unknown Frame Type */
2147 if ( idx < 0 )
2148 {
2149 frameOffset += 2;
2150 found--;
2151 continue;
2152 }
2153 /* Frame IDs do not match */
2154 else if ( type == OPENSAFETY_SNMT_MESSAGE_TYPE &&
2155 (OSS_FRAME_ID_T(message_tvb, byte_offset + frameStart1) != OSS_FRAME_ID_T(message_tvb, byte_offset + frameStart2)) )
2156 {
2157 frameOffset += 2;
2158 found--;
2159 continue;
2160 }
2161 }
2162
2163 /* If this package is not valid, the next step, which normally occurs in unxorFrame will lead to a
2164 * frameLength bigger than the maximum data size. This is an indicator, that the package in general
2165 * is fault, and therefore we return false. Increasing the frameOffset will lead to out-of-bounds
2166 * for tvb_* functions. And frameLength errors are misidentified packages most of the times anyway */
2167 if ( ( (gint)frameLength - (gint)( frameStart2 > frameStart1 ? frameStart2 : frameLength - frameStart1 ) ) < 0 )
2168 return FALSE;
2169
2170 /* Some SPDO based sanity checks, still a lot of faulty SPDOs remain, because they
2171 * cannot be filtered, without throwing out too many positives. */
2172 if ( type == OPENSAFETY_SPDO_MESSAGE_TYPE )
2173 {
2174 /* Checking if there is a node address set, or the package is invalid. Some PRes
2175 * messages in EPL may double as valid subframes 1. If the nodeAddress is out of
2176 * range, the package is marked as malformed */
2177 nodeAddress = OSS_FRAME_ADDR_T(message_tvb, byte_offset + frameStart1);
2178 if ( nodeAddress == 0 || nodeAddress > 1024 ) {
2179 markAsMalformed = TRUE;
2180 }
2181
2182 /* SPDO Reserved is invalid, therefore all packages using this ID can be discarded */
2183 if ( OSS_FRAME_ID_T(message_tvb, byte_offset + frameStart1) == OPENSAFETY_MSG_SPDO_RESERVED )
2184 {
2185 frameOffset += 2;
2186 found--;
2187 continue;
2188 }
2189 }
2190
2191 /* Filter node list */
2192 gint addr = OSS_FRAME_ADDR_T(message_tvb, byte_offset + frameStart1);
2193 if ( global_filter_list && wmem_list_count ( global_filter_list ) > 0 )
2194 {
2195 gboolean found_in_list = wmem_list_find(global_filter_list, GINT_TO_POINTER( addr )) ? TRUE : FALSE;
2196
2197 if ( ( ! global_show_only_node_in_filter && found_in_list ) ||
2198 ( global_show_only_node_in_filter && ! found_in_list ) )
2199 {
2200 opensafety_item = proto_tree_add_item(tree, proto_opensafety, message_tvb, frameOffset, frameLength, ENC_NA);
2201 proto_item_append_text(opensafety_item, ", Filtered Node: 0x%03X (%d)", addr, addr);
2202 frameOffset += 2;
2203 found--;
2204 continue;
2205 }
2206 }
2207
2208 /* From here on, the package should be correct. Even if it is not correct, it will be dissected
2209 * anyway and marked as malformed. Therefore it can be assumed, that a gap will end here.
2210 */
2211 if ( global_display_intergap_data == TRUE && gapStart != frameOffset )
2212 {
2213 /* Storing the gap data in subset, and calling the data dissector to display it */
2214 gap_tvb = tvb_new_subset_length_caplen(message_tvb, gapStart, (frameOffset - gapStart), reported_len);
2215 call_dissector(data_dissector, gap_tvb, pinfo, tree);
2216 }
2217 /* Setting the gap to the next offset */
2218 gapStart = frameOffset + frameLength;
2219
2220 /* Adding second data source */
2221 next_tvb = tvb_new_subset_length_caplen ( message_tvb, frameOffset, frameLength, reported_len );
2222
2223 /* Adding a visual aid to the dissector tree */
2224 add_new_data_source(pinfo, next_tvb, "openSAFETY Frame");
2225
2226 /* A new subtype for package dissection will need to set the actual nr. for the whole dissected package */
2227 if ( force_nr_in_package > 0 )
2228 {
2229 found = force_nr_in_package + 1;
2230 dissectorCalled = TRUE;
2231 col_set_str(pinfo->cinfo, COL_PROTOCOL, protocolName);
2232 }
2233
2234 if ( ! dissectorCalled )
2235 {
2236 if ( call_sub_dissector )
2237 call_dissector(protocol_dissector, message_tvb, pinfo, tree);
2238 dissectorCalled = TRUE;
2239
2240 col_set_str(pinfo->cinfo, COL_PROTOCOL, protocolName);
2241 col_clear(pinfo->cinfo, COL_INFO);
2242 }
2243
2244 /* if the tree is NULL, we are called for the overview, otherwise for the
2245 more detailed view of the package */
2246 if ( tree )
2247 {
2248 /* create the opensafety protocol tree */
2249 opensafety_item = proto_tree_add_item(tree, proto_opensafety, message_tvb, frameOffset, frameLength, ENC_NA);
2250 opensafety_tree = proto_item_add_subtree(opensafety_item, ett_opensafety);
2251 } else {
2252 opensafety_item = NULL;
2253 opensafety_tree = NULL;
2254 }
2255
2256 /* Setting type to packet_info */
2257 packet->msg_type = type;
2258
2259 packet->frame.frame_tvb = next_tvb;
2260 packet->frame.byte_offset = frameOffset + tvb_raw_offset(message_tvb);
2261 packet->frame.subframe1 = frameStart1;
2262 packet->frame.subframe2 = frameStart2;
2263 packet->frame.length = frameLength;
2264 packet->frame.malformed = FALSE;
2265
2266 /* Clearing connection valid bit */
2267 if ( packet->msg_type == OPENSAFETY_SPDO_MESSAGE_TYPE )
2268 packet->msg_id = packet->msg_id & 0xF8;
2269
2270 if ( dissect_opensafety_message(packet, next_tvb, pinfo, opensafety_item, opensafety_tree, found, previous_msg_id) != TRUE )
2271 markAsMalformed = TRUE;
2272
2273 previous_msg_id = packet->msg_id;
2274
2275 if ( markAsMalformed )
2276 {
2277 packet->frame.malformed = TRUE;
2278 if ( OSS_FRAME_ADDR_T(message_tvb, byte_offset + frameStart1) > 1024 )
2279 expert_add_info(pinfo, opensafety_item, &ei_message_spdo_address_invalid );
2280 }
2281
2282 tap_queue_packet(opensafety_tap, pinfo, packet);
2283
2284 /* Something is being displayed, therefore this dissector returns true */
2285 handled = TRUE;
2286 }
2287 else
2288 break;
2289
2290 /* findSafetyFrame starts at frameOffset with the search for the next position. But the
2291 * offset is assumed to be the ID, which can lead to scenarios, where the CRC of a previous
2292 * detected frame is assumed to be the addr of the next one. +1 prevents such a scenario.
2293 * It must be checked, if the resulting frameOffset does not scratch the max length. It
2294 * cannot exceed by adding just frameLength, as this value is a result of the heuristic, and
2295 * therefore must be within the correct length, but it can exceed if +1 is added unchecked. */
2296 frameOffset += frameLength;
2297 if ( tvb_captured_length_remaining(message_tvb, frameOffset) > 0 )
2298 frameOffset += 1;
2299 }
2300
2301 if ( handled == TRUE )
2302 {
2303 /* There might be some undissected data at the end of the frame (e.g. SercosIII) */
2304 if ( frameOffset < length && global_display_intergap_data == TRUE && gapStart != frameOffset )
2305 {
2306 /* Storing the gap data in subset, and calling the data dissector to display it */
2307 gap_tvb = tvb_new_subset_length_caplen(message_tvb, gapStart, (length - gapStart), reported_len);
2308 call_dissector(data_dissector, gap_tvb, pinfo, tree);
2309 }
2310 }
2311
2312 return ( handled ? TRUE : FALSE );
2313 }
2314
2315 static gboolean
dissect_opensafety_epl(tvbuff_t * message_tvb,packet_info * pinfo,proto_tree * tree,void * data)2316 dissect_opensafety_epl(tvbuff_t *message_tvb, packet_info *pinfo, proto_tree *tree, void *data )
2317 {
2318 gboolean result = FALSE;
2319 proto_tree *epl_tree = NULL;
2320 guint8 epl_msgtype = 0;
2321
2322 /* We will call the epl dissector by using call_dissector(). The epl dissector will then call
2323 * the heuristic openSAFETY dissector again. By setting this information, we prevent a dissector
2324 * loop */
2325 if ( bDissector_Called_Once_Before == FALSE )
2326 {
2327 bDissector_Called_Once_Before = TRUE;
2328
2329 /* Set the tree up, until it is par with the top-level */
2330 epl_tree = tree;
2331 while ( epl_tree != NULL && epl_tree->parent != NULL )
2332 epl_tree = epl_tree->parent;
2333
2334 /* Ordering message type to traffic types */
2335 if ( *((guint8*)data) == 0x03 || *((guint8*)data) == 0x04 )
2336 epl_msgtype = OPENSAFETY_CYCLIC_DATA;
2337 else
2338 epl_msgtype = OPENSAFETY_ACYCLIC_DATA;
2339
2340 /* We check if we have a asynchronous message, or a synchronous message. In case of
2341 * asynchronous messages, SPDO packages are not valid. */
2342
2343 result = opensafety_package_dissector("openSAFETY/Powerlink", "",
2344 FALSE, FALSE, 0, message_tvb, pinfo, epl_tree, epl_msgtype );
2345
2346 bDissector_Called_Once_Before = FALSE;
2347 }
2348
2349 return result;
2350 }
2351
2352 static gboolean
dissect_opensafety_siii(tvbuff_t * message_tvb,packet_info * pinfo,proto_tree * tree,void * data _U_)2353 dissect_opensafety_siii(tvbuff_t *message_tvb, packet_info *pinfo, proto_tree *tree, void *data _U_ )
2354 {
2355 gboolean result = FALSE;
2356 gboolean udp = FALSE;
2357 guint8 firstByte;
2358
2359 /* The UDP dissection is not done by a heuristic, but rather by a normal dissector. But
2360 * the customer may not expect, that if (s)he disables the SercosIII dissector, that the
2361 * SercosIII UDP packages get still dissected. This will disable them as well. */
2362 if ( ! heuristic_siii_dissection_enabled )
2363 return FALSE;
2364
2365 /* We will call the SercosIII dissector by using call_dissector(). The SercosIII dissector will
2366 * then call the heuristic openSAFETY dissector again. By setting this information, we prevent
2367 * a dissector loop. */
2368 if ( bDissector_Called_Once_Before == FALSE )
2369 {
2370 udp = pinfo->destport == OPENSAFETY_UDP_PORT_SIII;
2371
2372 bDissector_Called_Once_Before = TRUE;
2373 /* No frames can be sent in AT messages, therefore those get filtered right away */
2374 firstByte = ( tvb_get_guint8(message_tvb, 0) << 1 );
2375 if ( udp || ( firstByte & 0x40 ) == 0x40 )
2376 {
2377 result = opensafety_package_dissector( "openSAFETY/SercosIII",
2378 udp ? "" : "sercosiii",
2379 FALSE, FALSE, 0, message_tvb, pinfo, tree,
2380 udp ? OPENSAFETY_ACYCLIC_DATA : OPENSAFETY_CYCLIC_DATA );
2381 }
2382 bDissector_Called_Once_Before = FALSE;
2383 }
2384
2385 return result;
2386 }
2387
2388 static gboolean
dissect_opensafety_pn_io(tvbuff_t * message_tvb,packet_info * pinfo,proto_tree * tree,void * data _U_)2389 dissect_opensafety_pn_io(tvbuff_t *message_tvb, packet_info *pinfo, proto_tree *tree, void *data _U_ )
2390 {
2391 gboolean result = FALSE;
2392
2393 /* We will call the pn_io dissector by using call_dissector(). The epl dissector will then call
2394 * the heuristic openSAFETY dissector again. By setting this information, we prevent a dissector
2395 * loop */
2396 if ( bDissector_Called_Once_Before == FALSE )
2397 {
2398 bDissector_Called_Once_Before = TRUE;
2399 result = opensafety_package_dissector("openSAFETY/Profinet IO", "pn_io",
2400 FALSE, FALSE, 0, message_tvb, pinfo, tree, OPENSAFETY_ANY_TRANSPORT);
2401 bDissector_Called_Once_Before = FALSE;
2402 }
2403
2404 return result;
2405 }
2406
2407 static gboolean
dissect_opensafety_mbtcp(tvbuff_t * message_tvb,packet_info * pinfo,proto_tree * tree,void * data _U_)2408 dissect_opensafety_mbtcp(tvbuff_t *message_tvb, packet_info *pinfo, proto_tree *tree, void *data _U_ )
2409 {
2410 if ( ! global_enable_mbtcp )
2411 return FALSE;
2412
2413 /* When Modbus/TCP gets dissected, openSAFETY would be sorted as a child protocol. Although,
2414 * this behaviour is technically correct, it differs from other implemented IEM protocol handlers.
2415 * Therefore, the openSAFETY frame gets put one up, if the parent is not NULL */
2416 return opensafety_package_dissector("openSAFETY/Modbus TCP", "", FALSE, TRUE, 0,
2417 message_tvb, pinfo, ( ((tree != NULL) && (tree->parent != NULL)) ? tree->parent : tree ),
2418 OPENSAFETY_ANY_TRANSPORT);
2419 }
2420
2421 static gboolean
opensafety_udp_transport_dissector(tvbuff_t * message_tvb,packet_info * pinfo,proto_tree * tree)2422 opensafety_udp_transport_dissector(tvbuff_t *message_tvb, packet_info *pinfo, proto_tree *tree)
2423 {
2424 proto_item *ti = NULL;
2425 proto_tree *transport_tree = NULL;
2426 gint offset = 0;
2427 tvbuff_t *os_tvb = 0;
2428
2429 col_set_str(pinfo->cinfo, COL_PROTOCOL, "openSAFETY over UDP");
2430 col_clear(pinfo->cinfo, COL_INFO);
2431
2432 ti = proto_tree_add_item(tree, proto_oss_udp_transport, message_tvb, 0, -1, ENC_NA);
2433 transport_tree = proto_item_add_subtree(ti, ett_opensafety);
2434
2435 proto_tree_add_item(transport_tree, hf_oss_udp_transport_version, message_tvb, 0, 1, ENC_BIG_ENDIAN);
2436 proto_tree_add_item(transport_tree, hf_oss_udp_transport_flags_type, message_tvb, 1, 1, ENC_BIG_ENDIAN);
2437 proto_tree_add_item(transport_tree, hf_oss_udp_transport_counter, message_tvb, 2, 2, ENC_LITTLE_ENDIAN);
2438
2439 proto_tree_add_item(transport_tree, hf_oss_udp_transport_sender, message_tvb, 4, 4, ENC_LITTLE_ENDIAN);
2440 proto_tree_add_item(transport_tree, hf_oss_udp_transport_datapoint, message_tvb, 8, 2, ENC_LITTLE_ENDIAN);
2441 proto_tree_add_item(transport_tree, hf_oss_udp_transport_length, message_tvb, 10, 2, ENC_LITTLE_ENDIAN);
2442 offset += 12;
2443
2444 os_tvb = tvb_new_subset_remaining(message_tvb, offset);
2445
2446 if ( ! opensafety_package_dissector("openSAFETY/UDP", "", FALSE,
2447 FALSE, 0, os_tvb, pinfo, tree, OPENSAFETY_ANY_TRANSPORT ) )
2448 call_dissector(find_dissector("data"), os_tvb, pinfo, transport_tree);
2449
2450 return TRUE;
2451 }
2452
2453 static gboolean
dissect_opensafety_udpdata(tvbuff_t * message_tvb,packet_info * pinfo,proto_tree * tree,void * data _U_)2454 dissect_opensafety_udpdata(tvbuff_t *message_tvb, packet_info *pinfo, proto_tree *tree, void *data _U_ )
2455 {
2456 gboolean result = FALSE;
2457 static guint32 frameNum = 0;
2458 static guint32 frameIdx = 0;
2459
2460 gboolean frameFound = FALSE;
2461 guint frameOffset = 0;
2462 guint frameLength = 0;
2463
2464 if ( pinfo->destport == OPENSAFETY_UDP_PORT_SIII )
2465 return dissect_opensafety_siii(message_tvb, pinfo, tree, data);
2466
2467 if ( ! global_enable_udp )
2468 return result;
2469
2470 /* An openSAFETY frame has at least OSS_MINIMUM_LENGTH bytes */
2471 if ( tvb_captured_length ( message_tvb ) < OSS_MINIMUM_LENGTH )
2472 return result;
2473
2474 /* More than one openSAFETY package could be transported in the same frame,
2475 * in such a case, we need to establish the number of packages inside the frame */
2476 if ( pinfo->num != frameNum )
2477 {
2478 frameIdx = 0;
2479 frameNum = pinfo->num;
2480 }
2481
2482 /* check for openSAFETY frame at beginning of data */
2483
2484 frameFound = findSafetyFrame(pinfo, message_tvb, 0, global_udp_frame2_first, &frameOffset, &frameLength, NULL );
2485 if ( ! frameFound || ( frameOffset >= 11 ) )
2486 {
2487 dissector_handle_t udp_transport = find_dissector ( "opensafety_udp_transport" );
2488 if ( udp_transport != NULL )
2489 call_dissector(udp_transport, message_tvb, pinfo, tree);
2490 result = opensafety_udp_transport_dissector(message_tvb, pinfo, tree);
2491 }
2492 else
2493 result = opensafety_package_dissector("openSAFETY/UDP", "", global_udp_frame2_first,
2494 FALSE, frameIdx, message_tvb, pinfo, tree, OPENSAFETY_ACYCLIC_DATA );
2495
2496 if ( result )
2497 frameIdx++;
2498
2499 return result;
2500 }
2501
2502 static void
apply_prefs(void)2503 apply_prefs ( void )
2504 {
2505 static guint opensafety_udp_port_number;
2506 static guint opensafety_udp_siii_port_number;
2507 static gboolean opensafety_init = FALSE;
2508
2509 /* It only should delete dissectors, if run for any time except the first */
2510 if ( opensafety_init )
2511 {
2512 /* Delete dissectors in preparation of a changed config setting */
2513 dissector_delete_uint ("udp.port", opensafety_udp_port_number, opensafety_udptransport_handle);
2514 dissector_delete_uint ("udp.port", opensafety_udp_siii_port_number, opensafety_udpdata_handle);
2515 }
2516 opensafety_init = TRUE;
2517
2518 /* Storing the port numbers locally, to being able to delete the old associations */
2519 opensafety_udp_port_number = global_network_udp_port;
2520 opensafety_udp_siii_port_number = global_network_udp_port_sercosiii;
2521
2522 /* Default UDP only based dissector, will hand traffic to SIII dissector if needed */
2523 /* Preference names to specific to use "auto" preference */
2524 dissector_add_uint("udp.port", opensafety_udp_port_number, opensafety_udptransport_handle);
2525 dissector_add_uint("udp.port", opensafety_udp_siii_port_number, opensafety_udpdata_handle);
2526 }
2527
2528 void
proto_register_opensafety(void)2529 proto_register_opensafety(void)
2530 {
2531 /* Setup list of header fields */
2532 static hf_register_info hf[] = {
2533 { &hf_oss_scm_udid,
2534 { "SCM UDID Configured", "opensafety.scm_udid",
2535 FT_STRING, BASE_NONE, NULL, 0x0, NULL, HFILL } },
2536 { &hf_oss_scm_udid_auto,
2537 { "SCM UDID Auto Detect", "opensafety.scm_udid.auto",
2538 FT_STRING, BASE_NONE, NULL, 0x0, NULL, HFILL } },
2539 { &hf_oss_scm_udid_valid,
2540 { "SCM UDID Valid", "opensafety.scm_udid.valid",
2541 FT_BOOLEAN, BASE_NONE, NULL, 0x0, NULL, HFILL } },
2542
2543 { &hf_oss_byte_offset,
2544 { "Byte Offset", "opensafety.msg.byte_offset",
2545 FT_UINT16, BASE_HEX, NULL, 0x0, NULL, HFILL } },
2546 { &hf_oss_msg,
2547 { "Message", "opensafety.msg.id",
2548 FT_UINT8, BASE_HEX, VALS(opensafety_message_type_values), 0x0, NULL, HFILL } },
2549 { &hf_oss_msg_category,
2550 { "Type", "opensafety.msg.type",
2551 FT_UINT8, BASE_HEX, VALS(opensafety_msg_id_values), 0xE0, NULL, HFILL } },
2552 { &hf_oss_msg_direction,
2553 { "Direction", "opensafety.msg.direction",
2554 FT_BOOLEAN, 8, TFS(&opensafety_message_direction), 0x04, NULL, HFILL } },
2555 { &hf_oss_msg_node,
2556 { "Safety Node", "opensafety.msg.node",
2557 FT_UINT16, BASE_HEX, NULL, 0x0, NULL, HFILL } },
2558 { &hf_oss_msg_network,
2559 { "Safety Domain", "opensafety.msg.network",
2560 FT_UINT16, BASE_HEX, NULL, 0x0, NULL, HFILL } },
2561 { &hf_oss_msg_sender,
2562 { "SN send from", "opensafety.msg.sender",
2563 FT_UINT16, BASE_HEX, NULL, 0x0, NULL, HFILL } },
2564 { &hf_oss_msg_receiver,
2565 { "SN send to", "opensafety.msg.receiver",
2566 FT_UINT16, BASE_HEX, NULL, 0x0, NULL, HFILL } },
2567 { &hf_oss_length,
2568 { "Length", "opensafety.length",
2569 FT_UINT8, BASE_DEC, NULL, 0x0, NULL, HFILL } },
2570 { &hf_oss_crc,
2571 { "CRC", "opensafety.crc.data",
2572 FT_UINT16, BASE_HEX, NULL, 0x0, NULL, HFILL } },
2573
2574 { &hf_oss_crc_valid,
2575 { "Is Valid", "opensafety.crc.valid",
2576 FT_BOOLEAN, BASE_NONE, NULL, 0x0, NULL, HFILL } },
2577 { &hf_oss_crc_type,
2578 { "CRC Type", "opensafety.crc.type",
2579 FT_UINT8, BASE_DEC, VALS(opensafety_frame_crc_type), 0x0, NULL, HFILL } },
2580 { &hf_oss_crc2_valid,
2581 { "Is Valid", "opensafety.crc2.valid",
2582 FT_BOOLEAN, BASE_NONE, NULL, 0x0, NULL, HFILL } },
2583
2584 /* SNMT Specific fields */
2585 { &hf_oss_snmt_slave,
2586 { "SNMT Slave", "opensafety.snmt.slave",
2587 FT_UINT16, BASE_HEX, NULL, 0x0, NULL, HFILL } },
2588 { &hf_oss_snmt_master,
2589 { "SNMT Master", "opensafety.snmt.master",
2590 FT_UINT16, BASE_HEX, NULL, 0x0, NULL, HFILL } },
2591 { &hf_oss_snmt_scm,
2592 { "SCM", "opensafety.snmt.scm",
2593 FT_UINT16, BASE_HEX, NULL, 0x0, NULL, HFILL } },
2594 { &hf_oss_snmt_tool,
2595 { "Tool ID", "opensafety.snmt.tool_id",
2596 FT_UINT16, BASE_HEX, NULL, 0x0, NULL, HFILL } },
2597 { &hf_oss_snmt_udid,
2598 { "UDID for SN", "opensafety.snmt.udid",
2599 FT_ETHER, BASE_NONE, NULL, 0x0, NULL, HFILL } },
2600 { &hf_oss_snmt_service_id,
2601 { "Extended Service ID", "opensafety.snmt.service_id",
2602 FT_UINT8, BASE_HEX, VALS(opensafety_message_service_type), 0x0, NULL, HFILL } },
2603 { &hf_oss_snmt_error_group,
2604 { "Error Group", "opensafety.snmt.error_group",
2605 FT_UINT8, BASE_DEC, NULL, 0x0, NULL, HFILL } },
2606 { &hf_oss_snmt_error_code,
2607 { "Error Code", "opensafety.snmt.error_code",
2608 FT_UINT8, BASE_DEC, NULL, 0x0, NULL, HFILL } },
2609 { &hf_oss_snmt_param_type,
2610 { "Parameter Request Type", "opensafety.snmt.parameter_type",
2611 FT_BOOLEAN, BASE_NONE, TFS(&opensafety_addparam_request), 0x0, NULL, HFILL } },
2612 { &hf_oss_snmt_ext_addsaddr,
2613 { "Additional SADDR", "opensafety.snmt.additional.saddr",
2614 FT_UINT16, BASE_HEX, NULL, 0x0, NULL, HFILL } },
2615 { &hf_oss_snmt_ext_addtxspdo,
2616 { "Additional TxSPDO", "opensafety.snmt.additional.txspdo",
2617 FT_UINT16, BASE_HEX, NULL, 0x0, NULL, HFILL } },
2618 { &hf_oss_snmt_ext_initct,
2619 { "Initial CT", "opensafety.snmt.initct",
2620 FT_UINT40, BASE_HEX, NULL, 0x0, NULL, HFILL } },
2621
2622 /* SSDO Specific fields */
2623 { &hf_oss_ssdo_server,
2624 { "SSDO Server", "opensafety.ssdo.master",
2625 FT_UINT16, BASE_HEX, NULL, 0x0, NULL, HFILL } },
2626 { &hf_oss_ssdo_client,
2627 { "SSDO Client", "opensafety.ssdo.client",
2628 FT_UINT16, BASE_HEX, NULL, 0x0, NULL, HFILL } },
2629 { &hf_oss_ssdo_sano,
2630 { "SOD Access Request Number", "opensafety.ssdo.sano",
2631 FT_UINT16, BASE_DEC, NULL, 0x0, NULL, HFILL } },
2632 { &hf_oss_ssdo_sacmd,
2633 { "SOD Access Command", "opensafety.ssdo.sacmd",
2634 FT_UINT8, BASE_HEX, VALS(opensafety_ssdo_sacmd_values), 0x0, NULL, HFILL } },
2635 { &hf_oss_ssdo_sod_index,
2636 { "SOD Index", "opensafety.ssdo.sodentry.index",
2637 FT_UINT16, BASE_HEX, NULL, 0x0, NULL, HFILL } },
2638 { &hf_oss_ssdo_sod_subindex,
2639 { "SOD Sub Index", "opensafety.ssdo.sodentry.subindex",
2640 FT_UINT8, BASE_HEX, NULL, 0x0, NULL, HFILL } },
2641 { &hf_oss_ssdo_payload,
2642 { "SOD Payload", "opensafety.ssdo.payload",
2643 FT_BYTES, BASE_NONE, NULL, 0x0, NULL, HFILL } },
2644 { &hf_oss_ssdo_payload_size,
2645 { "SOD Payload Size", "opensafety.ssdo.payloadsize",
2646 FT_UINT32, BASE_DEC, NULL, 0x0, NULL, HFILL } },
2647 { &hf_oss_ssdo_sodentry_size,
2648 { "SOD Entry Size", "opensafety.ssdo.sodentry.size",
2649 FT_UINT32, BASE_DEC, NULL, 0x0, NULL, HFILL } },
2650 { &hf_oss_ssdo_sodentry_data,
2651 { "SOD Data", "opensafety.ssdo.sodentry.data",
2652 FT_BYTES, BASE_NONE, NULL, 0x0, NULL, HFILL } },
2653 { &hf_oss_sod_par_timestamp,
2654 { "Parameter Timestamp", "opensafety.sod.parameter.timestamp",
2655 FT_UINT32, BASE_HEX, NULL, 0x0, NULL, HFILL } },
2656 { &hf_oss_sod_par_checksum,
2657 { "Parameter Checksum", "opensafety.sod.parameter.checksum",
2658 FT_UINT32, BASE_HEX, NULL, 0x0, NULL, HFILL } },
2659
2660 { &hf_oss_ssdo_sodmapping,
2661 { "Mapping entry", "opensafety.sod.mapping",
2662 FT_NONE, BASE_NONE, NULL, 0x0, NULL, HFILL } },
2663 { &hf_oss_ssdo_sodmapping_bits,
2664 { "Mapping size", "opensafety.sod.mapping.bits",
2665 FT_UINT8, BASE_DEC, NULL, 0x0, NULL, HFILL } },
2666
2667 { &hf_oss_ssdo_extpar_parset,
2668 { "Additional Parameter Set", "opensafety.ssdo.extpar.setnr",
2669 FT_UINT8, BASE_DEC, NULL, 0x0, NULL, HFILL } },
2670 { &hf_oss_ssdo_extpar_version,
2671 { "Parameter Set Version", "opensafety.ssdo.extpar.version",
2672 FT_UINT8, BASE_DEC, NULL, 0x0, NULL, HFILL } },
2673 { &hf_oss_ssdo_extpar_saddr,
2674 { "Parameter Set for SADDR", "opensafety.ssdo.extpar.saddr",
2675 FT_UINT16, BASE_HEX, NULL, 0x0, NULL, HFILL } },
2676 { &hf_oss_ssdo_extpar_length,
2677 { "Parameter Set Length", "opensafety.ssdo.extpar.length",
2678 FT_UINT8, BASE_HEX, NULL, 0x0, NULL, HFILL } },
2679 { &hf_oss_ssdo_extpar_crc,
2680 { "Parameter Set CRC", "opensafety.ssdo.extpar.crc",
2681 FT_UINT32, BASE_HEX, NULL, 0x0, NULL, HFILL } },
2682 { &hf_oss_ssdo_extpar_tstamp,
2683 { "Timestamp", "opensafety.ssdo.extpar.timestamp",
2684 FT_UINT32, BASE_HEX, NULL, 0x0, NULL, HFILL } },
2685 { &hf_oss_ssdo_extpar_data,
2686 { "Ext. Parameter Data", "opensafety.ssdo.extpar.data",
2687 FT_BYTES, BASE_NONE, NULL, 0x0, NULL, HFILL } },
2688 { &hf_oss_ssdo_extpar,
2689 { "Ext. Parameter", "opensafety.ssdo.extpar",
2690 FT_STRING, BASE_NONE, NULL, 0x0, NULL, HFILL } },
2691
2692 {&hf_oss_fragments,
2693 {"Message fragments", "opensafety.ssdo.fragments",
2694 FT_NONE, BASE_NONE, NULL, 0x00, NULL, HFILL } },
2695 {&hf_oss_fragment,
2696 {"Message fragment", "opensafety.ssdo.fragment",
2697 FT_FRAMENUM, BASE_NONE, NULL, 0x00, NULL, HFILL } },
2698 {&hf_oss_fragment_overlap,
2699 {"Message fragment overlap", "opensafety.ssdo.fragment.overlap",
2700 FT_BOOLEAN, 0, NULL, 0x00, NULL, HFILL } },
2701 {&hf_oss_fragment_overlap_conflicts,
2702 {"Message fragment overlapping with conflicting data",
2703 "opensafety.ssdo.fragment.overlap.conflicts",
2704 FT_BOOLEAN, 0, NULL, 0x00, NULL, HFILL } },
2705 {&hf_oss_fragment_multiple_tails,
2706 {"Message has multiple tail fragments", "opensafety.ssdo.fragment.multiple_tails",
2707 FT_BOOLEAN, 0, NULL, 0x00, NULL, HFILL } },
2708 {&hf_oss_fragment_too_long_fragment,
2709 {"Message fragment too long", "opensafety.ssdo.fragment.too_long_fragment",
2710 FT_BOOLEAN, 0, NULL, 0x00, NULL, HFILL } },
2711 {&hf_oss_fragment_error,
2712 {"Message defragmentation error", "opensafety.ssdo.fragment.error",
2713 FT_FRAMENUM, BASE_NONE, NULL, 0x00, NULL, HFILL } },
2714 {&hf_oss_fragment_count,
2715 {"Message fragment count", "opensafety.ssdo.fragment.count",
2716 FT_UINT32, BASE_DEC, NULL, 0x00, NULL, HFILL } },
2717 {&hf_oss_reassembled_in,
2718 {"Reassembled in", "opensafety.ssdo.reassembled.in",
2719 FT_FRAMENUM, BASE_NONE, NULL, 0x00, NULL, HFILL } },
2720 {&hf_oss_reassembled_length,
2721 {"Reassembled length", "opensafety.ssdo.reassembled.length",
2722 FT_UINT32, BASE_DEC, NULL, 0x00, NULL, HFILL } },
2723 {&hf_oss_reassembled_data,
2724 {"Reassembled Data", "opensafety.ssdo.reassembled.data",
2725 FT_BYTES, BASE_NONE, NULL, 0x00, NULL, HFILL } },
2726
2727 { &hf_oss_ssdo_abort_code,
2728 { "Abort Code", "opensafety.ssdo.abortcode",
2729 FT_UINT32, BASE_HEX, NULL, 0x0, NULL, HFILL } },
2730
2731 { &hf_oss_ssdo_preload_error,
2732 { "Wrong/missing segment", "opensafety.ssdo.preload.error",
2733 FT_BOOLEAN, 8, NULL, 0x30, NULL, HFILL } },
2734 { &hf_oss_ssdo_preload_queue,
2735 { "Preload Queue Size", "opensafety.ssdo.preload.queuesize",
2736 FT_UINT8, BASE_DEC, NULL, 0x0, NULL, HFILL } },
2737
2738 /* SSDO SACmd specific fields */
2739 { &hf_oss_ssdo_sacmd_access_type,
2740 { "Access Direction", "opensafety.ssdo.sacmd.access",
2741 FT_BOOLEAN, 8, TFS(&opensafety_sacmd_acc), OPENSAFETY_SSDO_SACMD_ACC, NULL, HFILL } },
2742 { &hf_oss_ssdo_sacmd_preload,
2743 { "Preload Transfer", "opensafety.ssdo.sacmd.preload",
2744 FT_BOOLEAN, 8, TFS(&tfs_enabled_disabled), OPENSAFETY_SSDO_SACMD_PRLD, NULL, HFILL } },
2745 { &hf_oss_ssdo_sacmd_abort_transfer,
2746 { "Abort Transfer", "opensafety.ssdo.sacmd.abort_transfer",
2747 FT_BOOLEAN, 8, TFS(&opensafety_sacmd_abrt), OPENSAFETY_SSDO_SACMD_ABRT, NULL, HFILL } },
2748 { &hf_oss_ssdo_sacmd_segmentation,
2749 { "Segmentation", "opensafety.ssdo.sacmd.segmentation",
2750 FT_BOOLEAN, 8, TFS(&opensafety_sacmd_seg), OPENSAFETY_SSDO_SACMD_SEG, NULL, HFILL } },
2751 { &hf_oss_ssdo_sacmd_toggle,
2752 { "Toggle Bit", "opensafety.ssdo.sacmd.toggle",
2753 FT_BOOLEAN, 8, TFS(&tfs_on_off), OPENSAFETY_SSDO_SACMD_TGL, NULL, HFILL } },
2754 { &hf_oss_ssdo_sacmd_initiate,
2755 { "Initiate Transfer", "opensafety.ssdo.sacmd.initiate",
2756 FT_BOOLEAN, 8, TFS(&opensafety_sacmd_ini), OPENSAFETY_SSDO_SACMD_INI, NULL, HFILL } },
2757 { &hf_oss_ssdo_sacmd_end_segment,
2758 { "End Segment", "opensafety.ssdo.sacmd.end_segment",
2759 FT_BOOLEAN, 8, TFS(&opensafety_sacmd_ensg), OPENSAFETY_SSDO_SACMD_ENSG, NULL, HFILL } },
2760 #if 0
2761 { &hf_oss_ssdo_sacmd_reserved,
2762 { "Reserved", "opensafety.ssdo.sacmd.reserved",
2763 FT_BOOLEAN, 8, TFS(&opensafety_sacmd_res), OPENSAFETY_SSDO_SACMD_RES, NULL, HFILL } },
2764 #endif
2765
2766 /* SPDO Specific fields */
2767 { &hf_oss_spdo_connection_valid,
2768 { "Connection Valid Bit", "opensafety.spdo.connection_valid",
2769 FT_BOOLEAN, 8, TFS(&tfs_set_notset), 0x04, NULL, HFILL } },
2770 { &hf_oss_spdo_direction,
2771 { "Send to", "opensafety.spdo.direction",
2772 FT_BOOLEAN, 8, TFS(&opensafety_spdo_direction), 0x08, NULL, HFILL } },
2773 { &hf_oss_spdo_ct,
2774 { "Consecutive Time", "opensafety.spdo.ct",
2775 FT_UINT16, BASE_HEX, NULL, 0x0, NULL, HFILL } },
2776 { &hf_oss_spdo_ct_40bit,
2777 { "Consecutive Time 40bit", "opensafety.spdo.ct40bit",
2778 FT_UINT40, BASE_HEX, NULL, 0x0, NULL, HFILL } },
2779 { &hf_oss_spdo_time_request,
2780 { "Time Request Counter", "opensafety.spdo.time.request_counter",
2781 FT_UINT8, BASE_HEX, NULL, 0x0, NULL, HFILL } },
2782 { &hf_oss_spdo_time_request_to,
2783 { "Time Request from", "opensafety.spdo.time.request_from",
2784 FT_UINT16, BASE_HEX, NULL, 0x0, NULL, HFILL } },
2785 { &hf_oss_spdo_time_request_from,
2786 { "Time Request by", "opensafety.spdo.time.request_to",
2787 FT_UINT16, BASE_HEX, NULL, 0x0, NULL, HFILL } },
2788 { &hf_oss_spdo_feature_flags,
2789 { "SPDO Feature Flags", "opensafety.spdo.featureflags",
2790 FT_UINT8, BASE_HEX, NULL, 0x0, NULL, HFILL } },
2791 { &hf_oss_spdo_feature_flag_40bit_available,
2792 { "40Bit Request", "opensafety.spdo.features.40bitrequest",
2793 FT_BOOLEAN, 8, TFS(&tfs_requested_not_requested), (OPENSAFETY_SPDO_FEAT_40BIT_AVAIL << 2), NULL, HFILL } },
2794 { &hf_oss_spdo_feature_flag_40bit_used,
2795 { "40Bit Counter", "opensafety.spdo.features.40bitactive",
2796 FT_BOOLEAN, 8, TFS(&tfs_enabled_disabled), (OPENSAFETY_SPDO_FEAT_40BIT_USED << 2), NULL, HFILL } },
2797 };
2798
2799 /* Setup list of header fields */
2800 static hf_register_info hf_oss_udp_transport[] = {
2801 /* UDP transport specific fields */
2802 { &hf_oss_udp_transport_version,
2803 { "Transport Version", "opensafety.udp_transport.version",
2804 FT_UINT8, BASE_DEC, NULL, 0x0, NULL, HFILL } },
2805 { &hf_oss_udp_transport_flags_type,
2806 { "Data Type", "opensafety.udp_transport.flags.type",
2807 FT_BOOLEAN, 8, TFS(&tfs_udp_transport_cyclic_acyclic), 0x01, NULL, HFILL } },
2808 { &hf_oss_udp_transport_counter,
2809 { "Counter", "opensafety.udp_transport.counter",
2810 FT_UINT16, BASE_HEX_DEC, NULL, 0x0, NULL, HFILL } },
2811 { &hf_oss_udp_transport_sender,
2812 { "Sender ID", "opensafety.udp_transport.sender",
2813 FT_UINT32, BASE_HEX_DEC, NULL, 0x0, NULL, HFILL } },
2814 { &hf_oss_udp_transport_datapoint,
2815 { "Datapoint ID", "opensafety.udp_transport.datapoint",
2816 FT_UINT16, BASE_HEX_DEC, NULL, 0x0, NULL, HFILL } },
2817 { &hf_oss_udp_transport_length,
2818 { "Length", "opensafety.udp_transport.length",
2819 FT_UINT16, BASE_DEC, NULL, 0x0, NULL, HFILL } },
2820
2821 };
2822
2823 /* Setup protocol subtree array */
2824 static gint *ett[] = {
2825 &ett_opensafety,
2826 &ett_opensafety_node,
2827 &ett_opensafety_checksum,
2828 &ett_opensafety_snmt,
2829 &ett_opensafety_ssdo,
2830 &ett_opensafety_ssdo_sacmd,
2831 &ett_opensafety_ssdo_fragment,
2832 &ett_opensafety_ssdo_fragments,
2833 &ett_opensafety_ssdo_payload,
2834 &ett_opensafety_ssdo_sodentry,
2835 &ett_opensafety_sod_mapping,
2836 &ett_opensafety_ssdo_extpar,
2837 &ett_opensafety_spdo,
2838 &ett_opensafety_spdo_flags,
2839 };
2840
2841 static gint *ett_oss_udp[] = {
2842 &ett_oss_udp_transport,
2843 };
2844
2845 static ei_register_info ei[] = {
2846 { &ei_crc_frame_1_invalid,
2847 { "opensafety.crc.error.frame1_invalid", PI_PROTOCOL, PI_ERROR,
2848 "Frame 1 CRC invalid, Possible error in package", EXPFILL } },
2849 { &ei_crc_frame_1_valid_frame2_invalid,
2850 { "opensafety.crc.error.frame1_valid_frame2_invalid", PI_PROTOCOL, PI_ERROR,
2851 "Frame 1 is valid, frame 2 id is invalid", EXPFILL } },
2852 { &ei_crc_slimssdo_instead_of_spdo,
2853 { "opensafety.crc.warning.wrong_crc_for_spdo", PI_PROTOCOL, PI_WARN,
2854 "Frame 1 SPDO CRC is Slim SSDO CRC16 0x5935", EXPFILL } },
2855 { &ei_crc_frame_2_invalid,
2856 { "opensafety.crc.error.frame2_invalid", PI_PROTOCOL, PI_ERROR,
2857 "Frame 2 CRC invalid, Possible error in package or crc calculation", EXPFILL } },
2858 { &ei_crc_frame_2_unknown_scm_udid,
2859 { "opensafety.crc.error.frame2_unknown_scmudid", PI_PROTOCOL, PI_WARN,
2860 "Frame 2 CRC invalid, SCM UDID was not auto-detected", EXPFILL } },
2861 { &ei_crc_frame_2_scm_udid_encoded,
2862 { "opensafety.crc.error.crc2_scm_udid_encoded", PI_PROTOCOL, PI_NOTE,
2863 "Frame 2 CRC is encoded with byte 6 of SCM UDID due to payload length of 0 in frame 2 or SLIM SSDO", EXPFILL } },
2864
2865 { &ei_message_reassembly_size_differs_from_header,
2866 { "opensafety.msg.warning.reassembly_size_fail", PI_PROTOCOL, PI_WARN,
2867 "Reassembled message size differs from size in header", EXPFILL } },
2868 { &ei_message_unknown_type,
2869 { "opensafety.msg.error.unknown_type", PI_MALFORMED, PI_ERROR,
2870 "Unknown openSAFETY message type", EXPFILL } },
2871 { &ei_message_spdo_address_invalid,
2872 { "opensafety.msg.error.spdo_address_invalid", PI_MALFORMED, PI_ERROR,
2873 "SPDO address is invalid", EXPFILL } },
2874 { &ei_message_id_field_mismatch,
2875 { "opensafety.msg.error.id.mismatch", PI_PROTOCOL, PI_ERROR,
2876 "ID for frame 2 is not the same as for frame 1", EXPFILL } },
2877
2878 { &ei_scmudid_autodetected,
2879 { "opensafety.scm_udid.note.autodetected", PI_PROTOCOL, PI_NOTE,
2880 "Auto detected payload as SCM UDID", EXPFILL } },
2881 { &ei_scmudid_invalid_preference,
2882 { "opensafety.scm_udid.note.invalid_preference", PI_PROTOCOL, PI_WARN,
2883 "openSAFETY protocol settings are invalid! SCM UDID first octet will be assumed to be 00", EXPFILL } },
2884 { &ei_scmudid_unknown,
2885 { "opensafety.scm_udid.warning.assuming_first_octet", PI_PROTOCOL, PI_WARN,
2886 "SCM UDID unknown, assuming 00 as first UDID octet", EXPFILL } },
2887
2888 { &ei_payload_unknown_format,
2889 { "opensafety.msg.warning.unknown_format", PI_PROTOCOL, PI_WARN,
2890 "Unknown payload format detected", EXPFILL } },
2891 { &ei_payload_length_not_positive,
2892 { "opensafety.msg.warning.reassembly_length_not_positive", PI_PROTOCOL, PI_NOTE,
2893 "Calculation for payload length yielded non-positive result", EXPFILL } },
2894
2895 { &ei_40bit_default_domain,
2896 { "opensafety.msg.warning.default_domain_40bit", PI_PROTOCOL, PI_NOTE,
2897 "SDN is assumed with 1 to allow 40bit dissection", EXPFILL } },
2898
2899 };
2900
2901 module_t *opensafety_module, *oss_udp_module;
2902 expert_module_t *expert_opensafety;
2903
2904 /* Register the protocol name and description */
2905 proto_opensafety = proto_register_protocol("openSAFETY", "openSAFETY", "opensafety");
2906 opensafety_module = prefs_register_protocol(proto_opensafety, apply_prefs);
2907 proto_oss_udp_transport = proto_register_protocol("openSAFETY over UDP", "openSAFETY ov. UDP", "opensafety_udp");
2908 oss_udp_module = prefs_register_protocol(proto_oss_udp_transport, apply_prefs);
2909
2910 /* Register data dissector */
2911 heur_opensafety_spdo_subdissector_list = register_heur_dissector_list("opensafety.spdo", proto_opensafety);
2912
2913 /* Required function calls to register the header fields and subtrees used */
2914 proto_register_field_array(proto_opensafety, hf, array_length(hf));
2915 proto_register_subtree_array(ett, array_length(ett));
2916 proto_register_field_array(proto_oss_udp_transport, hf_oss_udp_transport, array_length(hf_oss_udp_transport));
2917 proto_register_subtree_array(ett_oss_udp, array_length(ett_oss_udp));
2918
2919 /* Register tap */
2920 opensafety_tap = register_tap("opensafety");
2921
2922 expert_opensafety = expert_register_protocol ( proto_opensafety );
2923 expert_register_field_array ( expert_opensafety, ei, array_length (ei ) );
2924
2925 /* register user preferences */
2926 prefs_register_string_preference(opensafety_module, "scm_udid",
2927 "SCM UDID (xx:xx:xx:xx:xx:xx)",
2928 "To be able to fully dissect SSDO and SPDO packages, a valid UDID for the SCM has to be provided",
2929 &global_scm_udid);
2930 prefs_register_bool_preference(opensafety_module, "scm_udid_autoset",
2931 "Set SCM UDID if detected in stream",
2932 "Automatically assign a detected SCM UDID (by reading SNMT->SNTM_assign_UDID_SCM) and set it for the file",
2933 &global_scm_udid_autoset);
2934
2935 prefs_register_string_preference(opensafety_module, "filter_nodes",
2936 "Filter openSAFETY Nodes",
2937 "A comma-separated list of nodes to be filtered during dissection",
2938 &global_filter_nodes);
2939 prefs_register_bool_preference(opensafety_module, "filter_show_nodes_in_filterlist",
2940 "Show nodes in filter, hide otherwise",
2941 "If set to true, only nodes in the list will be shown, otherwise they will be hidden",
2942 &global_show_only_node_in_filter);
2943
2944 prefs_register_uint_preference(opensafety_module, "network_udp_port",
2945 "Port used for Generic UDP",
2946 "Port used by any UDP demo implementation to transport data", 10,
2947 &global_network_udp_port);
2948 prefs_register_uint_preference(opensafety_module, "network_udp_port_sercosiii",
2949 "Port used for SercosIII/UDP",
2950 "UDP port used by SercosIII to transport data", 10,
2951 &global_network_udp_port_sercosiii);
2952 prefs_register_bool_preference(opensafety_module, "network_udp_frame_first_sercosiii",
2953 "openSAFETY frame 2 before frame 1 (SercosIII/UDP only)",
2954 "In an SercosIII/UDP transport stream, openSAFETY frame 2 will be expected before frame 1",
2955 &global_siii_udp_frame2_first );
2956
2957 prefs_register_bool_preference(opensafety_module, "network_udp_frame_first",
2958 "openSAFETY frame 2 before frame 1 (UDP only)",
2959 "In the transport stream, openSAFETY frame 2 will be expected before frame 1",
2960 &global_udp_frame2_first );
2961 prefs_register_bool_preference(opensafety_module, "mbtcp_big_endian",
2962 "Big Endian Word Coding (Modbus/TCP only)",
2963 "Modbus/TCP words can be transcoded either big- or little endian. Default will be little endian",
2964 &global_mbtcp_big_endian);
2965 prefs_register_bool_preference(opensafety_module, "debug_verbose",
2966 "openSAFETY print all dissection information",
2967 "Enables additional information in the dissection for better debugging an openSAFETY trace",
2968 &global_opensafety_debug_verbose );
2969
2970 prefs_register_obsolete_preference(opensafety_module, "enable_plk");
2971 prefs_register_obsolete_preference(opensafety_module, "enable_siii");
2972 prefs_register_obsolete_preference(opensafety_module, "enable_pnio");
2973
2974 prefs_register_bool_preference(opensafety_module, "enable_udp",
2975 "Enable heuristic dissection for openSAFETY over UDP encoded traffic", "Enable heuristic dissection for openSAFETY over UDP encoded traffic",
2976 &global_enable_udp);
2977 prefs_register_bool_preference(opensafety_module, "enable_mbtcp",
2978 "Enable heuristic dissection for Modbus/TCP", "Enable heuristic dissection for Modbus/TCP",
2979 &global_enable_mbtcp);
2980
2981 prefs_register_bool_preference(opensafety_module, "display_intergap_data",
2982 "Display the data between openSAFETY packets", "Display the data between openSAFETY packets",
2983 &global_display_intergap_data);
2984 prefs_register_bool_preference(opensafety_module, "classify_transport",
2985 "Dissect packet based on transport method (EPL + SercosIII only)",
2986 "SPDOs may only be found in cyclic data, SSDOs/SNMTS only in acyclic data",
2987 &global_classify_transport);
2988
2989 prefs_register_uint_preference(oss_udp_module, "network_udp_port",
2990 "Port used for UDP Transport",
2991 "Port used by the openSAFETY over UDP data transport", 10,
2992 &global_network_oss_udp_port);
2993
2994 /* Registering default and ModBus/TCP dissector */
2995 opensafety_udpdata_handle = register_dissector("opensafety_udp", dissect_opensafety_udpdata, proto_opensafety );
2996 opensafety_udptransport_handle =
2997 register_dissector("opensafety_udptransport", dissect_opensafety_udpdata, proto_oss_udp_transport );
2998 opensafety_mbtcp_handle = register_dissector("opensafety_mbtcp", dissect_opensafety_mbtcp, proto_opensafety );
2999 opensafety_pnio_handle = register_dissector("opensafety_pnio", dissect_opensafety_pn_io, proto_opensafety);
3000 }
3001
3002 void
proto_reg_handoff_opensafety(void)3003 proto_reg_handoff_opensafety(void)
3004 {
3005 /* Storing global data_dissector */
3006 data_dissector = find_dissector ( "data" );
3007
3008 /* EPL & SercosIII dissector registration */
3009 heur_dissector_add("epl_data", dissect_opensafety_epl, "openSAFETY over EPL", "opensafety_epl_data", proto_opensafety, HEURISTIC_ENABLE);
3010 heur_dissector_add("sercosiii", dissect_opensafety_siii, "openSAFETY over SercosIII", "opensafety_sercosiii", proto_opensafety, HEURISTIC_ENABLE);
3011
3012 /* Modbus TCP dissector registration */
3013 dissector_add_string("modbus.data", "data", opensafety_mbtcp_handle);
3014
3015 /* For Profinet we have to register as a heuristic dissector, as Profinet
3016 * is implemented as a plugin, and therefore the heuristic dissector is not
3017 * added by the time this method is being called
3018 */
3019 if ( find_dissector("pn_io") != NULL )
3020 {
3021 heur_dissector_add("pn_io", dissect_opensafety_pn_io, "openSAFETY over Profinet", "opensafety_pn_io", proto_opensafety, HEURISTIC_DISABLE);
3022 }
3023 else
3024 {
3025 /* The native dissector cannot be loaded. so we add our protocol directly to
3026 * the ethernet subdissector list. No PNIO specific data will be dissected
3027 * and a warning will be displayed, recognizing the missing dissector plugin.
3028 */
3029 dissector_add_uint("ethertype", ETHERTYPE_PROFINET, opensafety_pnio_handle);
3030 }
3031
3032 apply_prefs();
3033
3034 register_init_routine ( setup_dissector );
3035 register_cleanup_routine ( cleanup_dissector );
3036
3037 reassembly_table_register(&os_reassembly_table, &addresses_reassembly_table_functions);
3038
3039 /* registering frame end routine, to prevent a malformed dissection preventing
3040 * further dissector calls (see bug #6950) */
3041 /* register_frame_end_routine(reset_dissector); */
3042 }
3043
3044 /*
3045 * Editor modelines - https://www.wireshark.org/tools/modelines.html
3046 *
3047 * Local variables:
3048 * c-basic-offset: 4
3049 * tab-width: 8
3050 * indent-tabs-mode: nil
3051 * End:
3052 *
3053 * vi: set shiftwidth=4 tabstop=8 expandtab:
3054 * :indentSize=4:tabSize=8:noTabs=true:
3055 */
3056