aboutsummaryrefslogtreecommitdiffstats
path: root/wiretap
diff options
context:
space:
mode:
authorMaksim Salau <maksim.salau@gmail.com>2019-06-27 21:19:53 +0300
committerAnders Broman <a.broman58@gmail.com>2019-07-11 04:44:00 +0000
commit8bb5320cb2c2574f657679e387068d27bc178fdb (patch)
tree88a8c69f849a63b3d5fcecd73b11969282cfd11a /wiretap
parentdb9ec7fc46dfa685fd4ff0376d6ea594f73cf081 (diff)
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 <jim.young.ws@gmail.com> Tested-by: Petri Dish Buildbot Reviewed-by: Anders Broman <a.broman58@gmail.com>
Diffstat (limited to 'wiretap')
-rw-r--r--wiretap/CMakeLists.txt6
-rw-r--r--wiretap/candump.c323
-rw-r--r--wiretap/candump.h20
-rw-r--r--wiretap/candump_parser.lemon344
-rw-r--r--wiretap/candump_priv.h87
-rw-r--r--wiretap/candump_scanner.l107
-rw-r--r--wiretap/file_access.c2
7 files changed, 889 insertions, 0 deletions
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 <gram@alumni.rice.edu>
+ *
+ * Support for candump log file format
+ * Copyright (c) 2019 by Maksim Salau <maksim.salau@gmail.com>
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+
+#include <config.h>
+#include <wtap-int.h>
+#include <file_wrappers.h>
+#include <epan/exported_pdu.h>
+#include <wsutil/tempfile.h>
+#include <wsutil/os_version_info.h>
+#include <string.h>
+#include <inttypes.h>
+#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, &params, 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 <gram@alumni.rice.edu>
+ *
+ * Support for candump log file format
+ * Copyright (c) 2019 by Maksim Salau <maksim.salau@gmail.com>
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+
+#ifndef CANDUMP_H__
+#define CANDUMP_H__
+
+#include <wiretap/wtap.h>
+
+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 <gram@alumni.rice.edu>
+ *
+ * Support for candump log file format
+ * Copyright (c) 2019 by Maksim Salau <maksim.salau@gmail.com>
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+
+#include <assert.h>
+#include <wiretap/file_wrappers.h>
+#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 <gram@alumni.rice.edu>
+ *
+ * Support for candump log file format
+ * Copyright (c) 2019 by Maksim Salau <maksim.salau@gmail.com>
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+
+#ifndef CANDUMP_PRIV_H__
+#define CANDUMP_PRIV_H__
+
+#include <gmodule.h>
+#include <wiretap/wtap.h>
+#include <epan/dissectors/packet-socketcan.h>
+
+#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 <wsutil/ws_printf.h>
+
+/* 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 <gram@alumni.rice.edu>
+ *
+ * Support for candump log file format
+ * Copyright (c) 2019 by Maksim Salau <maksim.salau@gmail.com>
+ *
+ * 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 <ws_diag_control.h>
+#include <wiretap/file_wrappers.h>
+#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 },