4 * Copyright (c) 1998 by Gilbert Ramirez <gram@alumni.rice.edu>
6 * Support for Busmaster log file format
7 * Copyright (c) 2019 by Maksim Salau <maksim.salau@gmail.com>
9 * SPDX-License-Identifier: GPL-2.0-or-later
13 #include "busmaster.h"
16 #include <file_wrappers.h>
17 #include <epan/dissectors/packet-socketcan.h>
18 #include <wsutil/exported_pdu_tlvs.h>
19 #include "busmaster_priv.h"
25 busmaster_close(wtap
*wth
);
28 busmaster_read(wtap
*wth
, wtap_rec
*rec
, Buffer
*buf
,
29 int *err
, char **err_info
,
30 int64_t *data_offset
);
33 busmaster_seek_read(wtap
*wth
, int64_t seek_off
,
34 wtap_rec
*rec
, Buffer
*buf
,
35 int *err
, char **err_info
);
37 static int busmaster_file_type_subtype
= -1;
39 void register_busmaster(void);
44 * https://rbei-etas.github.io/busmaster/
46 * for the BUSMASTER software.
50 busmaster_gen_packet(wtap_rec
*rec
, Buffer
*buf
,
51 const busmaster_priv_t
*priv_entry
, const msg_t
*msg
,
52 int *err
, char **err_info
)
57 bool is_fd
= (msg
->type
== MSG_TYPE_STD_FD
)
58 || (msg
->type
== MSG_TYPE_EXT_FD
);
59 bool is_eff
= (msg
->type
== MSG_TYPE_EXT
)
60 || (msg
->type
== MSG_TYPE_EXT_RTR
)
61 || (msg
->type
== MSG_TYPE_EXT_FD
);
62 bool is_rtr
= (msg
->type
== MSG_TYPE_STD_RTR
)
63 || (msg
->type
== MSG_TYPE_EXT_RTR
);
64 bool is_err
= (msg
->type
== MSG_TYPE_ERR
);
68 *err
= WTAP_ERR_BAD_FILE
;
69 *err_info
= g_strdup("Header is missing");
77 canfd_frame_t canfd_frame
= {0};
79 canfd_frame
.can_id
= g_htonl((msg
->id
& (is_eff
? CAN_EFF_MASK
: CAN_SFF_MASK
)) |
80 (is_eff
? CAN_EFF_FLAG
: 0) |
81 (is_err
? CAN_ERR_FLAG
: 0));
82 canfd_frame
.flags
= CANFD_FDF
;
83 canfd_frame
.len
= msg
->data
.length
;
85 memcpy(canfd_frame
.data
,
87 MIN(msg
->data
.length
, sizeof(canfd_frame
.data
)));
90 (uint8_t *)&canfd_frame
,
95 can_frame_t can_frame
= {0};
97 can_frame
.can_id
= g_htonl((msg
->id
& (is_eff
? CAN_EFF_MASK
: CAN_SFF_MASK
)) |
98 (is_rtr
? CAN_RTR_FLAG
: 0) |
99 (is_eff
? CAN_EFF_FLAG
: 0) |
100 (is_err
? CAN_ERR_FLAG
: 0));
101 can_frame
.can_dlc
= msg
->data
.length
;
103 memcpy(can_frame
.data
,
105 MIN(msg
->data
.length
, sizeof(can_frame
.data
)));
107 ws_buffer_append(buf
,
108 (uint8_t *)&can_frame
,
112 if (priv_entry
->time_mode
== TIME_MODE_SYSTEM
)
116 tm
.tm_year
= priv_entry
->start_date
.year
- 1900;
117 tm
.tm_mon
= priv_entry
->start_date
.month
- 1;
118 tm
.tm_mday
= priv_entry
->start_date
.day
;
119 tm
.tm_hour
= msg
->timestamp
.hours
;
120 tm
.tm_min
= msg
->timestamp
.minutes
;
121 tm
.tm_sec
= msg
->timestamp
.seconds
;
125 nsecs
= msg
->timestamp
.micros
* 1000u;
128 else if (priv_entry
->time_mode
== TIME_MODE_ABSOLUTE
)
133 tm
.tm_year
= priv_entry
->start_date
.year
- 1900;
134 tm
.tm_mon
= priv_entry
->start_date
.month
- 1;
135 tm
.tm_mday
= priv_entry
->start_date
.day
;
136 tm
.tm_hour
= priv_entry
->start_time
.hours
;
137 tm
.tm_min
= priv_entry
->start_time
.minutes
;
138 tm
.tm_sec
= priv_entry
->start_time
.seconds
;
143 secs
+= msg
->timestamp
.hours
* 3600;
144 secs
+= msg
->timestamp
.minutes
* 60;
145 secs
+= msg
->timestamp
.seconds
;
147 micros
= priv_entry
->start_time
.micros
+ msg
->timestamp
.micros
;
148 if (micros
>= 1000000u)
154 nsecs
= micros
* 1000u;
158 rec
->rec_type
= REC_TYPE_PACKET
;
159 rec
->block
= wtap_block_create(WTAP_BLOCK_PACKET
);
160 rec
->presence_flags
= has_ts
? WTAP_HAS_TS
: 0;
162 rec
->ts
.nsecs
= nsecs
;
164 rec
->rec_header
.packet_header
.caplen
= (uint32_t)ws_buffer_length(buf
);
165 rec
->rec_header
.packet_header
.len
= (uint32_t)ws_buffer_length(buf
);
170 static log_entry_type_t
171 busmaster_parse(FILE_T fh
, busmaster_state_t
*state
, int *err
, char **err_info
)
176 busmaster_debug_printf("%s: Running busmaster file decoder\n", G_STRFUNC
);
183 return LOG_ENTRY_EOF
;
185 seek_off
= file_tell(fh
);
186 busmaster_debug_printf("%s: Starting parser at offset %" PRIi64
"\n",
187 G_STRFUNC
, seek_off
);
188 state
->file_bytes_read
= 0;
189 ok
= run_busmaster_parser(state
, err
, err_info
);
191 /* Rewind the file to the offset we have finished parsing */
192 busmaster_debug_printf("%s: Rewinding to offset %" PRIi64
"\n",
193 G_STRFUNC
, seek_off
+ state
->file_bytes_read
);
194 if (file_seek(fh
, seek_off
+ state
->file_bytes_read
, SEEK_SET
, err
) == -1)
198 *err_info
= g_strdup(g_strerror(errno
));
199 return LOG_ENTRY_ERROR
;
202 while (ok
&& state
->entry_type
== LOG_ENTRY_NONE
);
205 return LOG_ENTRY_ERROR
;
207 busmaster_debug_printf("%s: Success\n", G_STRFUNC
);
209 return state
->entry_type
;
213 busmaster_open(wtap
*wth
, int *err
, char **err_info
)
215 busmaster_state_t state
= {0};
216 log_entry_type_t entry
;
218 busmaster_debug_printf("%s: Trying to open with busmaster log reader\n",
221 /* Rewind to the beginning */
222 if (file_seek(wth
->fh
, 0, SEEK_SET
, err
) == -1)
223 return WTAP_OPEN_ERROR
;
225 entry
= busmaster_parse(wth
->fh
, &state
, err
, err_info
);
231 if (entry
!= LOG_ENTRY_HEADER
)
232 return WTAP_OPEN_NOT_MINE
;
234 /* Rewind to the beginning, so busmaster_read may read from the very beginning */
235 if (file_seek(wth
->fh
, 0, SEEK_SET
, err
) == -1)
236 return WTAP_OPEN_ERROR
;
238 busmaster_debug_printf("%s: That's a busmaster log\n", G_STRFUNC
);
241 wth
->subtype_close
= busmaster_close
;
242 wth
->subtype_read
= busmaster_read
;
243 wth
->subtype_seek_read
= busmaster_seek_read
;
244 wth
->file_type_subtype
= busmaster_file_type_subtype
;
245 wth
->file_encap
= WTAP_ENCAP_SOCKETCAN
;
246 wth
->file_tsprec
= WTAP_TSPREC_USEC
;
248 return WTAP_OPEN_MINE
;
252 busmaster_close(wtap
*wth
)
254 busmaster_debug_printf("%s\n", G_STRFUNC
);
256 g_slist_free_full((GSList
*)wth
->priv
, g_free
);
260 static busmaster_priv_t
*
261 busmaster_find_priv_entry(void *priv
, int64_t offset
)
265 for (list
= (GSList
*)priv
; list
; list
= g_slist_next(list
))
267 busmaster_priv_t
*entry
= (busmaster_priv_t
*)list
->data
;
269 if (((entry
->file_end_offset
== -1)
270 && (g_slist_next(list
) == NULL
))
271 || ((offset
>= entry
->file_start_offset
)
272 && (offset
<= entry
->file_end_offset
)))
282 busmaster_read(wtap
*wth
, wtap_rec
*rec
, Buffer
*buf
, int *err
, char **err_info
,
283 int64_t *data_offset
)
285 log_entry_type_t entry
;
286 busmaster_state_t state
;
287 busmaster_priv_t
*priv_entry
;
291 while (!is_msg
&& is_ok
)
293 busmaster_debug_printf("%s: offset = %" PRIi64
"\n",
294 G_STRFUNC
, file_tell(wth
->fh
));
296 if (file_eof(wth
->fh
))
298 busmaster_debug_printf("%s: End of file detected, nothing to do here\n",
305 *data_offset
= file_tell(wth
->fh
);
306 priv_entry
= busmaster_find_priv_entry(wth
->priv
, *data_offset
);
308 memset(&state
, 0, sizeof(state
));
310 state
.header
= *priv_entry
;
311 entry
= busmaster_parse(wth
->fh
, &state
, err
, err_info
);
313 busmaster_debug_printf("%s: analyzing output\n", G_STRFUNC
);
316 case LOG_ENTRY_EMPTY
:
318 case LOG_ENTRY_FOOTER_AND_HEADER
:
319 case LOG_ENTRY_FOOTER
:
320 priv_entry
= (busmaster_priv_t
*)g_slist_last((GSList
*)wth
->priv
)->data
;
323 *err
= WTAP_ERR_BAD_FILE
;
324 *err_info
= g_strdup("Header is missing");
327 priv_entry
->file_end_offset
= *data_offset
;
328 if (entry
== LOG_ENTRY_FOOTER
)
331 case LOG_ENTRY_HEADER
:
332 if (state
.header
.protocol
!= PROTOCOL_CAN
&&
333 state
.header
.protocol
!= PROTOCOL_J1939
)
335 *err
= WTAP_ERR_UNSUPPORTED
;
336 *err_info
= g_strdup("Unsupported protocol type");
342 /* Check that the previous section has a footer */
343 priv_entry
= (busmaster_priv_t
*)g_slist_last((GSList
*)wth
->priv
)->data
;
345 if (priv_entry
&& priv_entry
->file_end_offset
== -1)
347 *err
= WTAP_ERR_BAD_FILE
;
348 *err_info
= g_strdup("Footer is missing");
353 /* Start a new section */
354 priv_entry
= g_new(busmaster_priv_t
, 1);
356 priv_entry
[0] = state
.header
;
357 priv_entry
->file_start_offset
= file_tell(wth
->fh
);
358 priv_entry
->file_end_offset
= -1;
360 wth
->priv
= g_slist_append((GSList
*)wth
->priv
, priv_entry
);
364 priv_entry
= busmaster_find_priv_entry(wth
->priv
, *data_offset
);
365 is_ok
= busmaster_gen_packet(rec
, buf
, priv_entry
, &state
.msg
, err
, err_info
);
368 case LOG_ENTRY_ERROR
:
376 busmaster_debug_printf("%s: stopped at offset %" PRIi64
" with entry %d\n",
377 G_STRFUNC
, file_tell(wth
->fh
), entry
);
383 busmaster_seek_read(wtap
*wth
, int64_t seek_off
, wtap_rec
*rec
,
384 Buffer
*buf
, int *err
, char **err_info
)
386 busmaster_priv_t
*priv_entry
;
387 busmaster_state_t state
= {0};
388 log_entry_type_t entry
;
390 busmaster_debug_printf("%s: offset = %" PRIi64
"\n", G_STRFUNC
, seek_off
);
392 priv_entry
= busmaster_find_priv_entry(wth
->priv
, seek_off
);
395 busmaster_debug_printf("%s: analyzing output\n", G_STRFUNC
);
396 *err
= WTAP_ERR_BAD_FILE
;
397 *err_info
= g_strdup("Malformed header");
401 if (file_seek(wth
->random_fh
, seek_off
, SEEK_SET
, err
) == -1)
404 state
.header
= *priv_entry
;
405 entry
= busmaster_parse(wth
->random_fh
, &state
, err
, err_info
);
407 busmaster_debug_printf("%s: analyzing output\n", G_STRFUNC
);
409 if (entry
== LOG_ENTRY_ERROR
|| entry
== LOG_ENTRY_NONE
)
412 if (entry
!= LOG_ENTRY_MSG
)
414 *err
= WTAP_ERR_BAD_FILE
;
415 *err_info
= g_strdup("Failed to read a frame");
419 return busmaster_gen_packet(rec
, buf
, priv_entry
, &state
.msg
, err
, err_info
);
422 static const struct supported_block_type busmaster_blocks_supported
[] = {
424 * We support packet blocks, with no comments or other options.
426 { WTAP_BLOCK_PACKET
, MULTIPLE_BLOCKS_SUPPORTED
, NO_OPTIONS_SUPPORTED
}
429 static const struct file_type_subtype_info busmaster_info
= {
430 "BUSMASTER log file", "busmaster", "log", NULL
,
431 false, BLOCKS_SUPPORTED(busmaster_blocks_supported
),
435 void register_busmaster(void)
437 busmaster_file_type_subtype
= wtap_register_file_type_subtype(&busmaster_info
);
441 * Editor modelines - https://www.wireshark.org/tools/modelines.html
446 * indent-tabs-mode: nil
449 * vi: set shiftwidth=4 tabstop=8 expandtab:
450 * :indentSize=4:tabSize=8:noTabs=true: