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/candump_parser.lemon | 344 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 344 insertions(+) create mode 100644 wiretap/candump_parser.lemon (limited to 'wiretap/candump_parser.lemon') 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; +} + +} -- cgit v1.2.3