From 8bb5320cb2c2574f657679e387068d27bc178fdb Mon Sep 17 00:00:00 2001 From: Maksim Salau Date: Thu, 27 Jun 2019 21:19:53 +0300 Subject: wiretap: Add support of candump logs The change adds ability to import text logs produced by the candump tool. E.g.: candump -L can0 -or- candump -l can0 The whole file is read and converted into a temporary PCAPNG file with Exported PDU packets containing SocketCAN frames. Bug: 15889 Change-Id: I5ad93dca96d6e955a4b21cf624f0553e60f060f6 Reviewed-on: https://code.wireshark.org/review/33800 Petri-Dish: Jim Young Tested-by: Petri Dish Buildbot Reviewed-by: Anders Broman --- wiretap/CMakeLists.txt | 6 + wiretap/candump.c | 323 ++++++++++++++++++++++++++++++++++++++++ wiretap/candump.h | 20 +++ wiretap/candump_parser.lemon | 344 +++++++++++++++++++++++++++++++++++++++++++ wiretap/candump_priv.h | 87 +++++++++++ wiretap/candump_scanner.l | 107 ++++++++++++++ wiretap/file_access.c | 2 + 7 files changed, 889 insertions(+) create mode 100644 wiretap/candump.c create mode 100644 wiretap/candump.h create mode 100644 wiretap/candump_parser.lemon create mode 100644 wiretap/candump_priv.h create mode 100644 wiretap/candump_scanner.l (limited to 'wiretap') diff --git a/wiretap/CMakeLists.txt b/wiretap/CMakeLists.txt index dddb894b00..d7ea972133 100644 --- a/wiretap/CMakeLists.txt +++ b/wiretap/CMakeLists.txt @@ -27,6 +27,7 @@ set(WIRETAP_NONGENERATED_FILES ber.c btsnoop.c camins.c + candump.c capsa.c catapult_dct2000.c commview.c @@ -90,6 +91,7 @@ set(WIRETAP_FILES ${WIRETAP_NONGENERATED_FILES}) add_lex_files(LEX_FILES WIRETAP_FILES ascend_scanner.l + candump_scanner.l k12text.l ) @@ -97,6 +99,10 @@ add_yacc_files(YACC_FILES WIRETAP_FILES ascend.y ) +add_lemon_files(LEMON_FILES WIRETAP_FILES + candump_parser.lemon +) + # # All files are warning-clean. (Let's keep it that way.) # diff --git a/wiretap/candump.c b/wiretap/candump.c new file mode 100644 index 0000000000..331eaee079 --- /dev/null +++ b/wiretap/candump.c @@ -0,0 +1,323 @@ +/* candump.c + * + * Wiretap Library + * Copyright (c) 1998 by Gilbert Ramirez + * + * Support for candump log file format + * Copyright (c) 2019 by Maksim Salau + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include "candump.h" +#include "candump_priv.h" + +static gboolean candump_read(wtap *wth, wtap_rec *rec, Buffer *buf, + int *err, gchar **err_info, + gint64 *data_offset); +static gboolean candump_seek_read(wtap *wth, gint64 seek_off, + wtap_rec *rec, Buffer *buf, + int *err, gchar **err_info); +static void candump_close(wtap *wth); + +static gboolean +candump_add_packet(wtap_dumper *wdh, const msg_t *msg, int *err, char **err_info) +{ + static const char *can_proto_name = "can-hostendian"; + static const char *canfd_proto_name = "canfd"; + const char *proto_name = msg->is_fd ? canfd_proto_name : can_proto_name; + guint proto_name_length = (guint)strlen(proto_name) + 1; + guint header_length; + guint packet_length; + guint frame_length; + + guint8 buf[128]; + wtap_rec rec; + + /* Adjust proto name length to be aligned on 4 byte boundary */ + proto_name_length += (proto_name_length % 4) ? (4 - (proto_name_length % 4)) : 0; + + header_length = 4 + proto_name_length + 4; + frame_length = msg->is_fd ? sizeof(canfd_frame_t) : sizeof(can_frame_t); + packet_length = header_length + frame_length; + + memset(buf, 0, sizeof(buf)); + buf[1] = EXP_PDU_TAG_PROTO_NAME; + buf[3] = proto_name_length; + memcpy(buf + 4, proto_name, strlen(proto_name)); + + if (msg->is_fd) + { + canfd_frame_t canfd_frame; + + memset(&canfd_frame, 0, sizeof(canfd_frame)); + canfd_frame.can_id = msg->id; + canfd_frame.flags = msg->flags; + canfd_frame.len = msg->data.length; + memcpy(canfd_frame.data, msg->data.data, msg->data.length); + + memcpy(buf + header_length, (guint8 *)&canfd_frame, sizeof(canfd_frame)); + } + else + { + can_frame_t can_frame; + + memset(&can_frame, 0, sizeof(can_frame)); + can_frame.can_id = msg->id; + can_frame.can_dlc = msg->data.length; + memcpy(can_frame.data, msg->data.data, msg->data.length); + + memcpy(buf + header_length, (guint8 *)&can_frame, sizeof(can_frame)); + } + + memset(&rec, 0, sizeof(rec)); + rec.rec_type = REC_TYPE_PACKET; + rec.presence_flags = WTAP_HAS_TS; + rec.ts = msg->ts; + rec.tsprec = WTAP_TSPREC_USEC; + + rec.rec_header.packet_header.caplen = packet_length; + rec.rec_header.packet_header.len = packet_length; + + return wtap_dump(wdh, &rec, buf, err, err_info); +} + +static gchar * +candump_dump(GSList *packets, int *err, char **err_info) +{ + gchar *filename; + int import_file_fd; + wtap_dumper *wdh; + GSList *packet; + + /* pcapng defs */ + GArray *shb_hdrs = g_array_new(FALSE, FALSE, sizeof(wtap_block_t)); + wtap_block_t shb_hdr; + wtapng_iface_descriptions_t *idb_inf = NULL; + wtap_block_t int_data; + wtapng_if_descr_mandatory_t *int_data_mand; + GString *os_info_str; + gsize opt_len; + gchar *opt_str = NULL; + + static const gchar *opt_comment = "File converted to Exported PDU format during opening"; + static const gchar *if_name = "Fake IF"; + + ws_debug_printf("%s: Creating a temporary file\n", G_STRFUNC); + import_file_fd = create_tempfile(&filename, "Wireshark_PDU_candump_", NULL); + + /* Now open a file and dump to it */ + /* Create data for SHB */ + os_info_str = g_string_new(""); + get_os_version_info(os_info_str); + + shb_hdr = wtap_block_create(WTAP_BLOCK_NG_SECTION); + /* options */ + wtap_block_add_string_option(shb_hdr, OPT_COMMENT, opt_comment, + strlen(opt_comment)); + /* + * UTF-8 string containing the name of the operating system used to create + * this section. + */ + opt_len = os_info_str->len; + opt_str = g_string_free(os_info_str, FALSE); + if (opt_str) + { + wtap_block_add_string_option(shb_hdr, OPT_SHB_OS, opt_str, opt_len); + g_free(opt_str); + } + + /* + * UTF-8 string containing the name of the application used to create + * this section. Avoid the precise version (get_appname_and_version) to + * avoid wiretap rebuilds when only the version changes. + */ + wtap_block_add_string_option_format(shb_hdr, OPT_SHB_USERAPPL, "Wireshark %s", VERSION); + + /* Add header to the array */ + g_array_append_val(shb_hdrs, shb_hdr); + + /* Create fake IDB info */ + idb_inf = g_new(wtapng_iface_descriptions_t, 1); + idb_inf->interface_data = g_array_new(FALSE, FALSE, sizeof(wtap_block_t)); + + /* create the fake interface data */ + int_data = wtap_block_create(WTAP_BLOCK_IF_DESCR); + int_data_mand = (wtapng_if_descr_mandatory_t *)wtap_block_get_mandatory_data(int_data); + int_data_mand->wtap_encap = WTAP_ENCAP_WIRESHARK_UPPER_PDU; + int_data_mand->time_units_per_second = 1000000; /* default microsecond resolution */ + int_data_mand->snap_len = WTAP_MAX_PACKET_SIZE_STANDARD; + + wtap_block_add_string_option(int_data, OPT_IDB_NAME, if_name, strlen(if_name)); + int_data_mand->num_stat_entries = 0; /* Number of ISB:s */ + int_data_mand->interface_statistics = NULL; + + g_array_append_val(idb_inf->interface_data, int_data); + + const wtap_dump_params params = { + .encap = WTAP_ENCAP_WIRESHARK_UPPER_PDU, + .snaplen = WTAP_MAX_PACKET_SIZE_STANDARD, + .shb_hdrs = shb_hdrs, + .idb_inf = idb_inf, + }; + + ws_debug_printf("%s: Opening the temporary file for writing\n", G_STRFUNC); + wdh = wtap_dump_fdopen(import_file_fd, WTAP_FILE_TYPE_SUBTYPE_PCAPNG, + WTAP_UNCOMPRESSED, ¶ms, err); + + if (!wdh) + goto error_open; + + ws_debug_printf("%s: Writing packet data into the file\n", G_STRFUNC); + /* OK we've opened a new pcapng file and written the headers, time to do the packets */ + for (packet = packets; packet; packet = g_slist_next(packet)) + { + if (!candump_add_packet(wdh, (msg_t *)packet->data, err, err_info)) + goto error_write; + } + + ws_debug_printf("%s: Closing the file\n", G_STRFUNC); + /* Close the written file */ + if (!wtap_dump_close(wdh, err)) + goto error_write; + + goto exit; + +error_write: + wtap_dump_close(wdh, err); + ws_unlink(filename); +error_open: + g_free(filename); + filename = NULL; +exit: + wtap_block_array_free(shb_hdrs); + wtap_free_idb_info(idb_inf); + + return filename; +} + +static wtap_open_return_val +candump_parse(candump_priv_t **priv, wtap *wth, int *err, char **err_info) +{ + GSList *packets; + gchar *filename; + wtap *fh; + + ws_debug_printf("%s: Trying candump file decoder\n", G_STRFUNC); + packets = run_candump_parser(wth->fh, err, err_info); + + if (!packets) + return WTAP_OPEN_NOT_MINE; + + if (*err) + { + g_slist_free_full(packets, g_free); + return WTAP_OPEN_ERROR; + } + + ws_debug_printf("%s: Creating a PCAPNG file with data we've just read\n", G_STRFUNC); + /* Dump packets into a temporary file */ + filename = candump_dump(packets, err, err_info); + g_slist_free_full(packets, g_free); + + if (!filename) + return WTAP_OPEN_ERROR; + + ws_debug_printf("%s: Opening the newly created file\n", G_STRFUNC); + /* Now open the file for reading */ + fh = wtap_open_offline(filename, WTAP_TYPE_AUTO, + err, err_info, + (wth->random_fh ? TRUE : FALSE)); + + if (!fh) + { + g_free(filename); + return WTAP_OPEN_ERROR; + } + + *priv = g_new0(candump_priv_t, 1); + + (*priv)->tmp_file = fh; + (*priv)->tmp_filename = filename; + + ws_debug_printf("%s: Ok\n", G_STRFUNC); + return WTAP_OPEN_MINE; +} + +wtap_open_return_val +candump_open(wtap *wth, int *err, char **err_info) +{ + wtap_open_return_val ret; + candump_priv_t *priv = NULL; + + ret = candump_parse(&priv, wth, err, err_info); + + if (ret != WTAP_OPEN_MINE) + return ret; + + if (!priv) + return WTAP_OPEN_ERROR; + + /* Copy header section block from the temp file */ + wtap_block_copy(g_array_index(wth->shb_hdrs, wtap_block_t, 0), g_array_index(priv->tmp_file->shb_hdrs, wtap_block_t, 0)); + + wth->priv = priv; + wth->file_type_subtype = WTAP_FILE_TYPE_SUBTYPE_PCAPNG; + wth->file_encap = priv->tmp_file->file_encap; + wth->file_tsprec = priv->tmp_file->file_tsprec; + wth->subtype_read = candump_read; + wth->subtype_seek_read = candump_seek_read; + wth->subtype_close = candump_close; + wth->snapshot_length = 0; + + return WTAP_OPEN_MINE; +} + +static gboolean +candump_read(wtap *wth, wtap_rec *rec, Buffer *buf, int *err, gchar **err_info, + gint64 *data_offset) +{ + candump_priv_t *priv = (candump_priv_t *)wth->priv; + + return wtap_read(priv->tmp_file, rec, buf, err, err_info, data_offset); + +} + +static gboolean +candump_seek_read(wtap *wth , gint64 seek_off, wtap_rec *rec, + Buffer *buf, int *err, gchar **err_info) +{ + candump_priv_t *priv = (candump_priv_t *)wth->priv; + + return wtap_seek_read(priv->tmp_file, seek_off, rec, buf, err, err_info); +} + +static void candump_close(wtap *wth) +{ + candump_priv_t *priv = (candump_priv_t *)wth->priv; + + wtap_close(priv->tmp_file); + ws_unlink(priv->tmp_filename); + g_free(priv->tmp_filename); +} + +/* + * Editor modelines - http://www.wireshark.org/tools/modelines.html + * + * Local variables: + * c-basic-offset: 4 + * tab-width: 8 + * indent-tabs-mode: nil + * End: + * + * vi: set shiftwidth=4 tabstop=8 expandtab: + * :indentSize=4:tabSize=8:noTabs=true: + */ diff --git a/wiretap/candump.h b/wiretap/candump.h new file mode 100644 index 0000000000..4f66863894 --- /dev/null +++ b/wiretap/candump.h @@ -0,0 +1,20 @@ +/* candump.h + * + * Wiretap Library + * Copyright (c) 1998 by Gilbert Ramirez + * + * Support for candump log file format + * Copyright (c) 2019 by Maksim Salau + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#ifndef CANDUMP_H__ +#define CANDUMP_H__ + +#include + +wtap_open_return_val +candump_open(wtap *wth, int *err, char **err_info); + +#endif /* CANDUMP_H__ */ diff --git a/wiretap/candump_parser.lemon b/wiretap/candump_parser.lemon new file mode 100644 index 0000000000..d1e78342f9 --- /dev/null +++ b/wiretap/candump_parser.lemon @@ -0,0 +1,344 @@ +%include { + +/* candump_parser.lemon + * + * Wiretap Library + * Copyright (c) 1998 by Gilbert Ramirez + * + * Support for candump log file format + * Copyright (c) 2019 by Maksim Salau + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#include +#include +#include "candump_priv.h" + +extern void *CandumpParserAlloc(void *(*mallocProc)(size_t)); +extern void CandumpParser(void *yyp, int yymajor, token_t yyminor, candump_state_t *state); +extern void CandumpParserFree(void *p, void (*freeProc)(void*)); + +#ifndef NDEBUG +extern void CandumpParserTrace(FILE *TraceFILE, char *zTracePrompt); +#endif + +static void merge_msg_data(msg_data_t *dst, const msg_data_t *a, const msg_data_t *b) +{ + dst->length = a->length + b->length; + memcpy(&dst->data[0], &a->data[0], a->length); + memcpy(&dst->data[a->length], &b->data[0], b->length); +} + +DIAG_OFF(unreachable-code) + +} + +%name CandumpParser + +%token_prefix TOKEN_ + +%token_type { token_t } + +%token_destructor +{ + (void)state; + (void)yypParser; + (void)yypminor; +} + +%extra_argument { candump_state_t* state } + +%syntax_error +{ + (void)yypParser; + (void)yyminor; + +#ifndef NDEBUG + const int n = sizeof(yyTokenName) / sizeof(yyTokenName[0]); + ws_debug_printf("%s: got token: %s\n", G_STRFUNC, yyTokenName[yymajor]); + for (int i = 0; i < n; ++i) { + int a = yy_find_shift_action((YYCODETYPE)i, yypParser->yytos->stateno); + if (a < YYNSTATE + YYNRULE) { + ws_debug_printf("%s: possible token: %s\n", G_STRFUNC, yyTokenName[i]); + } + } +#endif + + g_free(state->parse_error); + state->parse_error = g_strdup_printf("Syntax Error"); + ws_debug_printf("%s: Syntax Error\n", G_STRFUNC); +} + +%parse_failure +{ + g_free(state->parse_error); + state->parse_error = g_strdup("Parse Error"); + ws_debug_printf("%s: Parse Error\n", G_STRFUNC); +} + +%type msg { msg_t } + +%type timestamp { nstime_t } +%type id { guint32 } +%type flags { guint8 } + +%type byte { guint8 } +%type data_max_8 { msg_data_t } +%type data_max_64 { msg_data_t } +%type data0 { msg_data_t } +%type data1 { msg_data_t } +%type data2 { msg_data_t } +%type data3 { msg_data_t } +%type data4 { msg_data_t } +%type data5 { msg_data_t } +%type data6 { msg_data_t } +%type data7 { msg_data_t } +%type data8 { msg_data_t } +%type data12 { msg_data_t } +%type data16 { msg_data_t } +%type data20 { msg_data_t } +%type data24 { msg_data_t } +%type data32 { msg_data_t } +%type data48 { msg_data_t } +%type data64 { msg_data_t } + +%start_symbol log + +log ::= lines . + +lines ::= line . +lines ::= lines ENDL line . + +line ::= maybe_spaces msg(M) . +{ + ws_debug_printf("%s: read message\n", G_STRFUNC); + state->packets = g_slist_append(state->packets, g_memdup(&(M), sizeof(M))); +} + +line ::= maybe_spaces . +{ + ws_debug_printf("%s: read empty line\n", G_STRFUNC); +} + +maybe_spaces ::= maybe_spaces SPACE . +maybe_spaces ::= . + +msg(M) ::= timestamp(T) SPACE ifname SPACE id(I) RTR(R) . +{ + M.ts = T; + M.is_fd = FALSE; + M.id = I | CAN_RTR_FLAG; + M.data.length = (guint8)R.v0; + + memset(M.data.data, 0, sizeof(M.data.data)); +} + +msg(M) ::= timestamp(T) SPACE ifname SPACE id(I) data_max_8(D) . +{ + M.ts = T; + M.is_fd = FALSE; + M.id = I; + M.data = D; +} + +msg(M) ::= timestamp(T) SPACE ifname SPACE id(I) flags(F) data_max_64(D) . +{ + M.ts = T; + M.is_fd = TRUE; + M.id = I; + M.flags = F; + M.data = D; +} + +timestamp(R) ::= TIMESTAMP(A) . +{ + R.secs = (time_t)A.v0; + R.nsecs = (int)A.v1 * 1000; +} + +ifname ::= ifname any . +ifname ::= any . + +any ::= UNKNOWN . +any ::= RTR . +any ::= STD_ID . +any ::= EXT_ID . +any ::= FLAGS . +any ::= TIMESTAMP . +any ::= BYTE . + +id(R) ::= STD_ID(A) . +{ + R = (guint32)A.v0; +} + +id(R) ::= EXT_ID(A) . +{ + R = (guint32)A.v0; + + if (!(R & CAN_ERR_FLAG)) + R |= CAN_EFF_FLAG; +} + +flags(R) ::= FLAGS(A) . +{ + R = (guint8)A.v0; +} + +data_max_8 ::= data0 . +data_max_8 ::= data1 . +data_max_8 ::= data2 . +data_max_8 ::= data3 . +data_max_8 ::= data4 . +data_max_8 ::= data5 . +data_max_8 ::= data6 . +data_max_8 ::= data7 . +data_max_8 ::= data8 . + +data_max_64 ::= data_max_8 . +data_max_64 ::= data12 . +data_max_64 ::= data16 . +data_max_64 ::= data20 . +data_max_64 ::= data24 . +data_max_64 ::= data32 . +data_max_64 ::= data48 . +data_max_64 ::= data64 . + +byte(R) ::= BYTE(A) . +{ + R = (guint8)A.v0; +} + +data0(R) ::= . +{ + R.length = 0; +} + +data1(R) ::= byte(A) . +{ + R.length = 1; + R.data[0] = A; +} + +data2(R) ::= byte(A) byte(B) . +{ + R.length = 2; + R.data[0] = A; + R.data[1] = B; +} + +data3(R) ::= byte(A) byte(B) byte(C) . +{ + R.length = 3; + R.data[0] = A; + R.data[1] = B; + R.data[2] = C; +} + +data4(R) ::= byte(A) byte(B) byte(C) byte(D) . +{ + R.length = 4; + R.data[0] = A; + R.data[1] = B; + R.data[2] = C; + R.data[3] = D; +} + +data5(R) ::= data4(A) data1(B) . { merge_msg_data(&R, &A, &B); } +data6(R) ::= data4(A) data2(B) . { merge_msg_data(&R, &A, &B); } +data7(R) ::= data4(A) data3(B) . { merge_msg_data(&R, &A, &B); } +data8(R) ::= data4(A) data4(B) . { merge_msg_data(&R, &A, &B); } +data12(R) ::= data8(A) data4(B) . { merge_msg_data(&R, &A, &B); } +data16(R) ::= data8(A) data8(B) . { merge_msg_data(&R, &A, &B); } +data20(R) ::= data16(A) data4(B) . { merge_msg_data(&R, &A, &B); } +data24(R) ::= data16(A) data8(B) . { merge_msg_data(&R, &A, &B); } +data32(R) ::= data16(A) data16(B) . { merge_msg_data(&R, &A, &B); } +data48(R) ::= data32(A) data16(B) . { merge_msg_data(&R, &A, &B); } +data64(R) ::= data32(A) data32(B) . { merge_msg_data(&R, &A, &B); } + +%code { + +DIAG_ON(unreachable-code) + +#include "candump_scanner_lex.h" +#include "candump_parser.h" + +GSList * +run_candump_parser(FILE_T fh, int *err, gchar **err_info) +{ + candump_state_t state; + int lex_code; + yyscan_t scanner; + void *parser; + + if (file_seek(fh, 0, SEEK_SET, err) == -1) + return NULL; + + memset(&state, 0, sizeof(state)); + state.fh = fh; + + if (candump_lex_init_extra(&state, &scanner) != 0) + { + *err = errno; + *err_info = g_strdup(g_strerror(errno)); + + return NULL; + } + + parser = CandumpParserAlloc(g_malloc); + +#ifndef NDEBUG + CandumpParserTrace(stdout, "parser >> "); +#endif + + ws_debug_printf("%s: Starting parsing\n", G_STRFUNC); + + do + { + lex_code = candump_lex(scanner); + +#ifndef NDEBUG + if (lex_code && lex_code != TOKEN_ENDL) + ws_debug_printf("%s: Feeding %s '%s'\n", + G_STRFUNC, yyTokenName[lex_code], + candump_get_text(scanner)); + else + ws_debug_printf("%s: Feeding %s\n", + G_STRFUNC, yyTokenName[lex_code]); +#endif + + CandumpParser(parser, lex_code, state.token, &state); + + if (state.err || state.err_info || state.parse_error) + break; + } + while (lex_code); + + ws_debug_printf("%s: Done (%d)\n", G_STRFUNC, lex_code); + + CandumpParserFree(parser, g_free); + candump_lex_destroy(scanner); + + if (state.err || state.err_info || state.parse_error) + { + if (state.err_info) + { + *err_info = state.err_info; + g_free(state.parse_error); + } + else + { + *err_info = state.parse_error; + } + + if (state.err) + *err = state.err; + else + *err = WTAP_ERR_BAD_FILE; + } + + return state.packets; +} + +} diff --git a/wiretap/candump_priv.h b/wiretap/candump_priv.h new file mode 100644 index 0000000000..9749daec3e --- /dev/null +++ b/wiretap/candump_priv.h @@ -0,0 +1,87 @@ +/* candump-priv.h + * + * Wiretap Library + * Copyright (c) 1998 by Gilbert Ramirez + * + * Support for candump log file format + * Copyright (c) 2019 by Maksim Salau + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#ifndef CANDUMP_PRIV_H__ +#define CANDUMP_PRIV_H__ + +#include +#include +#include + +#define CAN_MAX_DLEN 8 +#define CANFD_MAX_DLEN 64 + +typedef struct can_frame { + guint32 can_id; /* 32 bit CAN_ID + EFF/RTR/ERR flags */ + guint8 can_dlc; /* frame payload length in byte (0 .. CAN_MAX_DLEN) */ + guint8 __pad; /* padding */ + guint8 __res0; /* reserved / padding */ + guint8 __res1; /* reserved / padding */ + guint8 data[CAN_MAX_DLEN]; +} can_frame_t; + +typedef struct canfd_frame { + guint32 can_id; /* 32 bit CAN_ID + EFF/RTR/ERR flags */ + guint8 len; /* frame payload length in byte (0 .. CANFD_MAX_DLEN) */ + guint8 flags; /* additional flags for CAN FD */ + guint8 __res0; /* reserved / padding */ + guint8 __res1; /* reserved / padding */ + guint8 data[CANFD_MAX_DLEN]; +} canfd_frame_t; + +typedef struct { + guint8 length; + guint8 data[CANFD_MAX_DLEN]; +} msg_data_t; + +typedef struct { + nstime_t ts; + guint32 id; + gboolean is_fd; + guint8 flags; + msg_data_t data; +} msg_t; + +typedef struct { + gint64 v0; + gint64 v1; +} token_t; + +typedef struct { + wtap *tmp_file; + char *tmp_filename; +} candump_priv_t; + +typedef struct { + GSList *packets; + + FILE_T fh; + int err; + gchar *err_info; + gchar *parse_error; + + token_t token; +} candump_state_t; + +GSList * +run_candump_parser(FILE_T fh, int *err, gchar **err_info); + +#include + +/* Uncomment the following line to make decoder verbose */ +//#undef NDEBUG + +#ifdef NDEBUG +#undef ws_debug_printf +#define ws_debug_printf(...) (void)0 +#endif + +#endif /* CANDUMP_PRIV_H__ */ diff --git a/wiretap/candump_scanner.l b/wiretap/candump_scanner.l new file mode 100644 index 0000000000..59b2188e29 --- /dev/null +++ b/wiretap/candump_scanner.l @@ -0,0 +1,107 @@ +/* candump_scanner.l + * + * Wiretap Library + * Copyright (c) 1998 by Gilbert Ramirez + * + * Support for candump log file format + * Copyright (c) 2019 by Maksim Salau + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +%option reentrant +%option noyywrap +%option noinput +%option nounput +%option batch +%option never-interactive +%option prefix="candump_" +%option extra-type="candump_state_t *" +%option yylineno +%option nodefault + +%option noyy_scan_buffer +%option noyy_scan_bytes +%option noyy_scan_string + +%{ + +#include +#include +#include "candump_parser.h" +#include "candump_priv.h" + +#ifndef HAVE_UNISTD_H +#define YY_NO_UNISTD_H +#endif + +static int candump_yyinput(void *buf, unsigned int max_size, + candump_state_t *state) +{ + int result = file_read(buf, max_size, state->fh); + + if (result == EOF) + { + state->err = file_error(state->fh, &state->err_info); + result = YY_NULL; + } + + return result; +} + +#define YY_INPUT(buf, result, max_size) \ + do { (result) = candump_yyinput((buf), (max_size), yyextra); } while (0) + +DIAG_OFF_FLEX + +%} + +INT [0-9] +HEX [0-9A-Fa-f] + +%% + +[ \t] { return TOKEN_SPACE; }; +[\r\n] { return TOKEN_ENDL; } + +\({INT}+\.{INT}+\) { + yyextra->token.v0 = strtoul(yytext + 1, NULL, 10); + yyextra->token.v1 = strtoul(strchr(yytext, '.') + 1, NULL, 10); + return TOKEN_TIMESTAMP; + } + +R{INT} { + yyextra->token.v0 = strtoul(yytext + 1, NULL, 10); + return TOKEN_RTR; + } + +R { + yyextra->token.v0 = 0; + return TOKEN_RTR; + } + +{HEX}{8}# { + yyextra->token.v0 = strtoul(yytext, NULL, 16); + return TOKEN_EXT_ID; + } + +{HEX}{3}# { + yyextra->token.v0 = strtoul(yytext, NULL, 16); + return TOKEN_STD_ID; + } + +{HEX}{HEX} { + yyextra->token.v0 = strtoul(yytext, NULL, 16); + return TOKEN_BYTE; + } + +#{HEX} { + yyextra->token.v0 = strtoul(yytext + 1, NULL, 16); + return TOKEN_FLAGS; + } + +. { return TOKEN_UNKNOWN; } + +%% + +DIAG_ON_FLEX diff --git a/wiretap/file_access.c b/wiretap/file_access.c index b028e01096..76c2c91896 100644 --- a/wiretap/file_access.c +++ b/wiretap/file_access.c @@ -77,6 +77,7 @@ #include "ruby_marshal.h" #include "systemd_journal.h" #include "log3gpp.h" +#include "candump.h" /* @@ -416,6 +417,7 @@ static const struct open_info open_info_base[] = { { "NetScaler", OPEN_INFO_HEURISTIC, nstrace_open, "cap", NULL, NULL }, { "Android Logcat Binary format", OPEN_INFO_HEURISTIC, logcat_open, "logcat", NULL, NULL }, { "Android Logcat Text formats", OPEN_INFO_HEURISTIC, logcat_text_open, "txt", NULL, NULL }, + { "Candump log", OPEN_INFO_HEURISTIC, candump_open, NULL, NULL, NULL }, /* ASCII trace files from Telnet sessions. */ { "Lucent/Ascend access server trace", OPEN_INFO_HEURISTIC, ascend_open, "txt", NULL, NULL }, { "Toshiba Compact ISDN Router snoop", OPEN_INFO_HEURISTIC, toshiba_open, "txt", NULL, NULL }, -- cgit v1.2.3