wireshark/wiretap/peak-trc.c
Michael Mann 362528c613 Start finding commonality between CAN wiretaps
Use socketcan.[c|h] for shared (Socket)CAN functionality where the wiretaps create records of the  WTAP_ENCAP_SOCKETCAN encapsulation type.

Adjust existing "homegrown" structures to use as much of the "shared" data structures from socketcan.h so that all can use the single function wtap_socketcan_gen_packet() to create records.
2025-06-17 15:05:15 +00:00

873 lines
30 KiB
C

/* peak_trc.c
*
* Wiretap Library
* Copyright (c) 1998 by Gilbert Ramirez <gram@alumni.rice.edu>
*
* Support for TRC log file format generated by several CAN tools from PEAK Gmbh
* Copyright (c) 2024 by Miklos Marton <martonmiklosqdev@gmail.com>
*
* File format description:
* https://www.peak-system.com/produktcd/Pdf/English/PEAK_CAN_TRC_File_Format.pdf
*
* SPDX-License-Identifier: GPL-2.0-or-later
*/
#include <config.h>
#include <file_wrappers.h>
#include <epan/dissectors/packet-socketcan.h>
#include <wctype.h>
#include <string.h>
#include <stdlib.h>
#include <errno.h>
#include <time.h>
#include "peak-trc.h"
#include "socketcan.h"
//#define PEAK_TRC_DEBUG
#ifdef PEAK_TRC_DEBUG
#include <stdio.h>
#define peak_trc_debug_printf(...) printf(__VA_ARGS__)
#else
#define peak_trc_debug_printf(...) (void)0
#endif
#define PEAK_TRC_MAX_LINE_SIZE 4096 // J1939 logs could contain long lines
typedef enum {
FileVersion,
StartTime,
ColumnHeader,
Comment,
Data
} peak_trc_parse_state_t;
typedef enum {
Col_BusNumber, // 0
Col_BusNumber_v1, // 1
Col_Direction, // 2
Col_CanId, // 3
Col_DLC, // 4
Col_MessageNumber, // 5
Col_TimeOffset_v1_0, // 6
Col_TimeOffset, // 7
Col_Reserved, // 8
Col_MessageType, // 9
Col_ErrorOrRtr_v1p0, // 10
// keep it to the last one to get the
Col_Data_v1_0, // 11
Col_Data, // 12
Col_Invalid // 13
} peak_trc_column_type_t;
typedef struct {
peak_trc_column_type_t type;
char code;
char* regex;
} peak_trc_column_map_t;
typedef struct {
guint32 flags;
wtap_can_msg_t msg;
uint8_t bus_id;
} peak_trc_msg_t;
#define PEAK_TRC_MSG_FLAG_BUS_ID 1
typedef struct {
time_t trace_start;
time_t trace_start_nsec;
uint8_t column_positions[Col_Invalid];
uint8_t file_version_major, file_version_minor;
GRegex* data_matcher;
} peak_trc_state_t;
static int peak_trc_file_type_subtype = -1;
static peak_trc_column_map_t colmap[] = {
{Col_BusNumber, 'B', "\\s*(([0-9]*)|-)"},
/* This isn't a real column type, so give it a "type" that shouldn't be used */
{Col_BusNumber_v1, 1, "\\s*([0-9]*)"},
{Col_Direction, 'd', "\\s*(Rx|Tx|Warng|Error)"},
/* This isn't a real column type, so give it a "type" that shouldn't be used */
{Col_Data_v1_0, 2, "\\s?(((--|[0-9A-F]{2})\\s?)*)"},
{Col_Data, 'D', "\\s?((((--|[0-9A-F]{2})\\s?))*)"},
{Col_CanId, 'I', "\\s*([0-9A-F]*)"},
{Col_DLC, 'l', "\\s*([0-9]*)"},
{Col_DLC, 'L', "\\s*([0-9]*)"},
{Col_MessageNumber, 'N', "\\s*([0-9]*)\\)"},
{Col_TimeOffset, 'O', "\\s*([0-9]*\\.[0-9]*)"},
/* This isn't a real column type, so give it a "type" that shouldn't be used */
{Col_TimeOffset_v1_0, 3, "\\s*([0-9]*)"},
{Col_Reserved, 'R', "\\s*(-)"},
{Col_MessageType, 'T', "\\s*([A-Z]{2})"},
/* This isn't a real column, so give it a "type" that shouldn't be used */
{Col_ErrorOrRtr_v1p0, 4, "\\s*(ERROR|RTR)?"},
{Col_Invalid, 0, ""},
};
void register_peak_trc(void);
static bool
peak_trc_write_packet(wtap* wth, wtap_rec *rec, const peak_trc_msg_t *peak_msg,
int *err, gchar **err_info)
{
if (!wtap_socketcan_gen_packet(wth, rec, &peak_msg->msg, "peak", err, err_info))
return false;
if (peak_msg->flags & PEAK_TRC_MSG_FLAG_BUS_ID)
{
rec->presence_flags |= WTAP_HAS_INTERFACE_ID;
rec->rec_header.packet_header.interface_id = peak_msg->bus_id;
}
return true;
}
static wtap_open_return_val
peak_trc_parse(wtap* wth, gint64* offset, int* err, char** err_info)
{
gint64 seek_off = 0;
bool found_timestamp = false;
char line_buffer[PEAK_TRC_MAX_LINE_SIZE];
/* 1.0 file format does not have version info or column line,
* so assume that until told otherwise
*/
int major = 1;
int minor = 0;
peak_trc_state_t* state = (peak_trc_state_t*)wth->priv;
#ifdef PEAK_TRC_DEBUG
peak_trc_debug_printf("%s: Trying peak_trc file decoder\n", G_STRFUNC);
#endif
/* Initial start time until we find it in the header */
state->trace_start = time(NULL);
state->trace_start_nsec = 0;
state->file_version_major = 1;
state->file_version_minor = 0;
/* Initialize columns to "invalid" */
for (int i = 0; i < Col_Invalid; i++)
state->column_positions[i] = Col_Invalid;
/* Bail if the end of the file is here and it hasn't been determined it's a PEAK file */
while (!file_eof(wth->fh))
{
seek_off = file_tell(wth->fh);
#ifdef PEAK_TRC_DEBUG
peak_trc_debug_printf("%s: Starting parser at offset %" PRIi64 "\n", G_STRFUNC, seek_off);
#endif
if (file_gets(line_buffer, PEAK_TRC_MAX_LINE_SIZE, wth->fh) == NULL)
{
/* Error reading file, bail out */
*err = file_error(wth->fh, err_info);
if (*err != 0 && *err != WTAP_ERR_SHORT_READ)
return WTAP_OPEN_ERROR;
return WTAP_OPEN_NOT_MINE;
}
g_strstrip(line_buffer);
size_t line_len = strlen(line_buffer);
/* Ignore empty lines */
if (line_len == 0)
continue;
/* The intention here is to get through all of the "header" which consists of
* "commented" lines (notated with a ;)
* Once the header is complete, parsing can be confident it's a PEAK file
*/
if (line_buffer[0] != ';')
{
/* Two possibilities:
* 1. This is the start of the actual packet data and header parsing is complete
* 2. This isn't a PEAK file
*
* Determine this by if the timestamp has been found
*/
if (!found_timestamp)
return WTAP_OPEN_NOT_MINE;
/* Valid PEAK file */
GString* regex_str = g_string_new(NULL);
*offset = seek_off;
/* Save off the data found in the header */
state->file_version_major = (uint8_t)major;
state->file_version_minor = (uint8_t)minor;
/* File version 1.x doesn't explicitly provide column order information */
if (state->file_version_major == 1)
{
state->column_positions[Col_MessageNumber] = 1;
switch(state->file_version_minor)
{
case 0:
state->column_positions[Col_TimeOffset_v1_0] = 2;
state->column_positions[Col_CanId] = 3;
state->column_positions[Col_DLC] = 4;
state->column_positions[Col_ErrorOrRtr_v1p0] = 5;
state->column_positions[Col_Data_v1_0] = 6;
break;
case 1:
state->column_positions[Col_TimeOffset] = 2;
state->column_positions[Col_Direction] = 3;
state->column_positions[Col_CanId] = 4;
state->column_positions[Col_DLC] = 5;
state->column_positions[Col_ErrorOrRtr_v1p0] = 6;
state->column_positions[Col_Data] = 7;
break;
case 2:
state->column_positions[Col_TimeOffset] = 2;
state->column_positions[Col_BusNumber_v1] = 3;
state->column_positions[Col_Direction] = 4;
state->column_positions[Col_CanId] = 5;
state->column_positions[Col_DLC] = 6;
state->column_positions[Col_ErrorOrRtr_v1p0] = 7;
state->column_positions[Col_Data] = 8;
break;
default:
state->column_positions[Col_TimeOffset] = 2;
state->column_positions[Col_BusNumber_v1] = 3;
state->column_positions[Col_Direction] = 4;
state->column_positions[Col_CanId] = 5;
state->column_positions[Col_Reserved] = 6;
state->column_positions[Col_DLC] = 7;
state->column_positions[Col_ErrorOrRtr_v1p0] = 8;
state->column_positions[Col_Data] = 9;
break;
}
for (int column_index = 0; column_index < Col_Invalid; column_index++)
{
for (int col_type = Col_BusNumber; col_type < Col_Invalid; col_type++)
{
if (state->column_positions[col_type] == column_index)
{
for (peak_trc_column_map_t* item = colmap; item->type != Col_Invalid; item++)
{
if ((int)item->type == col_type)
{
g_string_append(regex_str, item->regex);
break;
}
}
}
}
}
state->data_matcher = g_regex_new(regex_str->str, (GRegexCompileFlags)(G_REGEX_OPTIMIZE | G_REGEX_RAW), (GRegexMatchFlags)0, NULL);
g_string_free(regex_str, TRUE);
}
return WTAP_OPEN_MINE;
}
if (g_str_has_prefix(line_buffer, ";$FILEVERSION="))
{
if (sscanf(line_buffer, ";$FILEVERSION=%d.%d\r\n", &major, &minor) != 2)
return WTAP_OPEN_NOT_MINE;
}
else if (g_str_has_prefix(line_buffer, ";$STARTTIME="))
{
/* $STARTTIME keyword to store the absolute start time of the trace file:
Format: Floating point, decimal separator is a point.
Value: Integral part = Number of days that have passed since 30. December 1899.
Fractional Part = Fraction of a 24-hour day that has elapsed, resolution is 1 millisecond. */
gchar** parts = g_strsplit(line_buffer, "=", 2);
if (parts[1] == NULL)
{
/* At this point, the file is probably a PEAK file that is poorly formatted, so bail with an error */
*err_info = ws_strdup("peak_trc: unable to parse start time");
g_strfreev(parts);
return WTAP_OPEN_ERROR;
}
/* Convert to a more "standard" timestamp */
double days_since_epoch = g_strtod(parts[1], NULL);
days_since_epoch -= 25569.0;
time_t ts = 0;
struct tm* t = gmtime(&ts);
t->tm_sec += (int)(days_since_epoch * 24 * 3600);
state->trace_start = mktime(t);
state->trace_start_nsec = 0;
found_timestamp = true;
g_strfreev(parts);
}
else if (g_str_has_prefix(line_buffer, ";$COLUMNS="))
{
gchar** parts = g_strsplit(line_buffer, "=", 2);
if (parts[1] == NULL)
{
/* At this point, the file is probably a PEAK file that is poorly formatted, so bail with an error */
*err_info = ws_strdup("peak_trc: unable to parse column definitions");
g_strfreev(parts);
return WTAP_OPEN_ERROR;
}
gchar** columns = g_strsplit(parts[1], ",", 0);
int column_index = 0;
for (gchar** iter = columns; *iter != NULL; iter++)
{
size_t col_length = strlen(*iter);
if (col_length > 1) {
*err_info = ws_strdup_printf("peak_trc: unknown column definition in the $COLUMNS line: '%s'", *iter);
g_strfreev(columns);
g_strfreev(parts);
return WTAP_OPEN_ERROR;
}
if (col_length == 0)
{
//Done ?
break;
}
/* Assign the column value if found */
for (peak_trc_column_map_t* item = colmap; item->type != Col_Invalid; item++)
{
if ((*iter)[0] == item->code)
{
// column type matched
state->column_positions[item->type] = column_index;
break;
}
}
column_index++;
}
g_strfreev(columns);
g_strfreev(parts);
}
else if (line_len >= 2)
{
/* The rest of the "keywords" are separated by whitespace after the initial ';' */
char* keyword = &line_buffer[1];
while (iswspace(*keyword))
keyword++;
if (g_str_has_prefix(keyword, "Start time:"))
{
/* Use the $STARTTIME if it has already been found */
if (!found_timestamp)
{
int day, month, year, hour, minute, second, millisecond;
if (sscanf(keyword, "Start time: %d.%d.%d %d:%d:%d.%d", &month, &day, &year, &hour, &minute, &second, &millisecond) != 7)
{
*err_info = ws_strdup("peak_trc: unable to parse start time");
return WTAP_OPEN_ERROR;
}
struct tm t;
t.tm_sec = second;
t.tm_min = minute;
t.tm_hour = hour;
t.tm_mday = day;
t.tm_mon = month-1;
t.tm_year = year-1900;
t.tm_wday = 0;
t.tm_yday = 0;
t.tm_isdst = -1;
state->trace_start = mktime(&t);
state->trace_start_nsec = millisecond*1000000;
found_timestamp = true;
}
}
}
}
return WTAP_OPEN_NOT_MINE;
}
static bool peak_trc_read_packet_v1(peak_trc_state_t* state, peak_trc_msg_t* peak_msg, char* line_buffer)
{
// pre 2.0 packets use regexp with fixed column order with a regexp
GMatchInfo* match_info = NULL;
bool found = g_regex_match(state->data_matcher, line_buffer, 0, &match_info);
int column_count = g_match_info_get_match_count(match_info);
if (!found || column_count < 5)
{
/* Not a valid v1 packet */
g_match_info_free(match_info);
return false;
}
for (int col = Col_BusNumber; col < Col_Invalid; col++)
{
if (state->column_positions[col] >= column_count || state->column_positions[col] == Col_Invalid)
continue;
gchar* column_text = g_match_info_fetch(match_info, state->column_positions[col]);
if (column_text == NULL)
{
g_match_info_free(match_info);
return false;
}
switch (col)
{
case Col_Direction:
if (strcmp(column_text, "Warng") == 0)
{
// Warning lines cannot be interpreted in Wireshark needs to be skipped
g_free(column_text);
g_match_info_free(match_info);
return false;
}
if (strcmp(column_text, "Error") == 0)
{
peak_msg->msg.type = MSG_TYPE_ERR;
}
break;
case Col_CanId:
peak_msg->msg.id = (guint32)g_ascii_strtoull(column_text, NULL, 16);
break;
case Col_ErrorOrRtr_v1p0:
if (state->file_version_minor == 1 && strcmp(column_text, "RTR") == 0)
{
peak_msg->msg.type = MSG_TYPE_STD_RTR;
}
break;
case Col_TimeOffset:
case Col_TimeOffset_v1_0:
{
double tsd = g_ascii_strtod(column_text, NULL) / 1000.0; // parse to seconds
tsd += state->trace_start;
peak_msg->msg.ts.secs = (guint64)tsd;
peak_msg->msg.ts.nsecs = (int)((tsd - (guint64)tsd) * 1000000000);
}
break;
case Col_DLC:
peak_msg->msg.data.length = (uint8_t)g_ascii_strtoull(column_text, NULL, 10);
break;
case Col_BusNumber_v1:
peak_msg->flags |= PEAK_TRC_MSG_FLAG_BUS_ID;
peak_msg->bus_id = (uint8_t)g_ascii_strtoull(column_text, NULL, 10);
break;
case Col_Data:
case Col_Data_v1_0:
{
gchar* err_or_rtr = g_match_info_fetch(match_info, state->column_positions[Col_ErrorOrRtr_v1p0]);
gchar** bytes = g_strsplit(g_strstrip(column_text), " ", CAN_MAX_DLEN);
// 1.0 format has an ERROR prefix in the data bytes, 1.1 has a separate columns for message type
if ((g_str_has_prefix(err_or_rtr, "ERROR")) ||
(state->file_version_minor == 1 && peak_msg->msg.type == MSG_TYPE_ERR))
{
peak_msg->msg.type = MSG_TYPE_ERR;
/* For LINUX_CAN_ERR the length has to be CAN_MAX_DLEN */
peak_msg->msg.data.length = CAN_MAX_DLEN;
/* Clear the data of the message to populate it with SocketCAN meta data */
memset(peak_msg->msg.data.data, 0, CAN_MAX_DLEN);
guint32 old_id = peak_msg->msg.id;
peak_msg->msg.id = 0;
// ID: Type of Error Frame
switch (old_id)
{
case 1: // 1 = Bit Error
peak_msg->msg.id |= CAN_ERR_PROT_BIT;
break;
case 2: // 2 = Form Error
peak_msg->msg.id |= CAN_ERR_PROT_FORM;
break;
case 4: // 4 = Stuff Error
peak_msg->msg.id |= CAN_ERR_PROT_STUFF;
break;
}
//Translate PEAK error data into SocketCAN meta data for supported error types
if ((peak_msg->msg.id | (CAN_ERR_PROT_FORM& CAN_ERR_PROT_STUFF)) != 0)
{
// Data Byte 0: Direction
uint8_t byte0 = (uint8_t)g_ascii_strtoull(bytes[0], NULL, 16);
if (byte0 < 2)
{
//0 = Error occurred while sending
//1 = Error occurred while receiving.
peak_msg->msg.data.data[2] |= CAN_ERR_PROT_TX;
}
// Data Byte 1: Current Position in Bit Stream
uint8_t bit_pos = (uint8_t)g_ascii_strtoull(bytes[1], NULL, 16);
if (bit_pos == 28)
{
peak_msg->msg.data.data[2] |= CAN_ERR_PROT_OVERLOAD;
}
else
{
// magically SocketCAN and PEAK error bit positions are identical with some exceptions
peak_msg->msg.data.data[3] = bit_pos;
if (peak_msg->msg.data.data[3] == 23)
peak_msg->msg.data.data[3] = 0; // Error position not present in SocketCAN
}
}
}
else if (g_str_has_prefix(err_or_rtr, "RTR") || (state->file_version_minor == 1 && peak_msg->msg.type == MSG_TYPE_STD_RTR))
{
peak_msg->msg.type = MSG_TYPE_STD_RTR;
peak_msg->msg.data.length = 0;
}
else
{
peak_msg->msg.type = MSG_TYPE_STD;
if (bytes)
{
for (int byte_i = 0; ((byte_i < CAN_MAX_DLEN) && (bytes[byte_i] != 0)); byte_i++)
peak_msg->msg.data.data[byte_i] = (uint8_t)g_ascii_strtoull(bytes[byte_i], NULL, 16);
}
}
g_strfreev(bytes);
g_free(err_or_rtr);
break;
}
}
g_free(column_text);
}
#ifdef PEAK_TRC_DEBUG
for (int i = 0; i < column_count; i++)
peak_trc_debug_printf("%d: %s\n", i, g_match_info_fetch(match_info, i));
#endif
g_match_info_free(match_info);
return true;
}
static bool peak_trc_read_packet_v2(peak_trc_state_t* state, peak_trc_msg_t* peak_msg, char* line_buffer)
{
int i = 0;
int column_i = 0;
int column_start = 0;
bool last_char_is_ws = true;
peak_trc_column_type_t current_column = Col_Invalid;
while (line_buffer[i] != 0)
{
bool is_whitespace = iswspace(line_buffer[i]);
if (current_column == Col_Data)
{
// data always the last and could contain spaces
is_whitespace = (line_buffer[i] == '\r' || line_buffer[i] == '\n');
}
if (is_whitespace)
{
if (!last_char_is_ws)
{
// column closed -> process data
gchar* column_text = g_utf8_substring(line_buffer, column_start, i);
#ifdef PEAK_TRC_DEBUG
peak_trc_debug_printf("Column %d: %s\n", current_column, column_text);
#endif
switch (current_column) {
case Col_BusNumber:
peak_msg->flags |= PEAK_TRC_MSG_FLAG_BUS_ID;
peak_msg->bus_id = (uint8_t)g_ascii_strtoull(column_text, NULL, 10);
break;
case Col_Direction:
break;
case Col_Data: {
if (peak_msg->msg.type == MSG_TYPE_ERR)
{
// FIXME fill data and msg id
}
else
{
gchar** bytes = g_strsplit(g_strstrip(column_text), " ", CANFD_MAX_DLEN);
int byte_i = 0;
while (byte_i < CANFD_MAX_DLEN) {
if (bytes[byte_i] == 0)
break;
peak_msg->msg.data.data[byte_i] = (uint8_t)g_ascii_strtoull(bytes[byte_i], NULL, 16);
byte_i++;
}
}
break;
}
case Col_CanId:
peak_msg->msg.id = (guint)g_ascii_strtoull(column_text, NULL, 16);
break;
case Col_DLC:
peak_msg->msg.data.length = (guint)g_ascii_strtoull(column_text, NULL, 10);
break;
case Col_TimeOffset:
{
double tsd = g_ascii_strtod(column_text, NULL) / 1000.0; // parse to seconds
tsd += state->trace_start;
peak_msg->msg.ts.secs = (guint64)tsd;
peak_msg->msg.ts.nsecs = (int)((tsd - (guint64)tsd) * 1000000000);
break;
}
case Col_MessageType:
if (strcmp(column_text, "DT") == 0)
{
peak_msg->msg.type = MSG_TYPE_STD;
}
else if (strcmp(column_text, "FD") == 0)
{
peak_msg->msg.type = MSG_TYPE_STD_FD;
}
else if (strcmp(column_text, "FB") == 0)
{
// CAN FD data frame with BRS bit set (Bit Rate Switch).
peak_msg->msg.type = MSG_TYPE_STD_FD; // TODO
}
else if (strcmp(column_text, "FE") == 0)
{
// CAN FD data frame with ESI bit set (Error State Indicator).
peak_msg->msg.type = MSG_TYPE_ERR;
}
else if (strcmp(column_text, "BI") == 0)
{
// CAN FD data frame with both BRS and ESI bits set.
peak_msg->msg.type = MSG_TYPE_ERR;
}
else if (strcmp(column_text, "RR") == 0)
{
peak_msg->msg.type = MSG_TYPE_STD_RTR;
}
else if (strcmp(column_text, "ST") == 0)
{
// TODO: Hardware Status change.
// Currently not supported in Wireshark
return false;
}
else if (strcmp(column_text, "ER") == 0)
{
peak_msg->msg.type = MSG_TYPE_ERR;
}
else if (strcmp(column_text, "EC") == 0)
{
// TODO: Error Counter change
// Currently not supported in Wireshark
return false;
}
else if (strcmp(column_text, "EV") == 0)
{
// Event. User-defined text, begins directly after bus specifier
// TODO add support for this event type
return false;
}
break;
case Col_MessageNumber:
case Col_Reserved:
case Col_Invalid:
break;
/* Version 1 column types ignored */
case Col_ErrorOrRtr_v1p0:
case Col_BusNumber_v1:
case Col_TimeOffset_v1_0:
case Col_Data_v1_0:
break;
}
g_free(column_text);
}
}
else
{
// non whitespace read
if (last_char_is_ws)
{
for (int lut_i = 0; lut_i < Col_Invalid; lut_i++)
{
if (state->column_positions[lut_i] == column_i)
{
current_column = lut_i;
break;
}
}
column_i++;
column_start = i;
}
}
last_char_is_ws = is_whitespace;
i++;
}
return true;
}
static bool
peak_trc_read_packet(wtap *wth, FILE_T fh, peak_trc_state_t* state,
wtap_rec *rec, int *err, gchar **err_info,
gint64 *data_offset)
{
peak_trc_msg_t peak_msg = {0};
char line_buffer[PEAK_TRC_MAX_LINE_SIZE];
while (!file_eof(fh))
{
if (data_offset)
*data_offset = file_tell(fh);
/* Read a line */
if (file_gets(line_buffer, PEAK_TRC_MAX_LINE_SIZE, fh) == NULL)
{
*err = file_error(fh, err_info);
return false;
}
/* Ignore empty or commented lines */
if ((strlen(line_buffer) == 0) ||
(line_buffer[0] == ';'))
continue;
if (state->file_version_major >= 2)
{
if (!peak_trc_read_packet_v2(state, &peak_msg, line_buffer))
continue;
}
else
{
if (!peak_trc_read_packet_v1(state, &peak_msg, line_buffer))
continue;
}
if (peak_msg.msg.id > 0x7FF)
{
//Translate from 11-bit to 32-bit IDs
switch (peak_msg.msg.type)
{
case MSG_TYPE_STD_FD:
peak_msg.msg.type = MSG_TYPE_EXT_FD;
break;
case MSG_TYPE_STD:
peak_msg.msg.type = MSG_TYPE_EXT;
break;
case MSG_TYPE_STD_RTR:
peak_msg.msg.type = MSG_TYPE_EXT_RTR;
break;
default:
//Ignore other types
break;
}
}
return peak_trc_write_packet(wth, rec, &peak_msg, err, err_info);
}
return false;
}
static bool
peak_trc_read(wtap *wth, wtap_rec *rec, int *err, char **err_info,
int64_t *data_offset)
{
peak_trc_state_t* state = (peak_trc_state_t*)wth->priv;
#ifdef PEAK_TRC_DEBUG
peak_trc_debug_printf("%s: Try reading at offset %" PRIi64 "\n", G_STRFUNC, file_tell(wth->fh));
#endif
return peak_trc_read_packet(wth, wth->fh, state, rec, err, err_info, data_offset);
}
static bool peak_trc_seek_read(wtap *wth, int64_t seek_off, wtap_rec *rec, int *err, char **err_info)
{
#ifdef PEAK_TRC_DEBUG
peak_trc_debug_printf("%s: Read at offset %" PRIi64 "\n", G_STRFUNC, seek_off);
#endif
peak_trc_state_t* state = (peak_trc_state_t*)wth->priv;
if (file_seek(wth->random_fh, seek_off, SEEK_SET, err) == -1)
{
*err = errno;
*err_info = g_strdup(g_strerror(errno));
return false;
}
return peak_trc_read_packet(wth, wth->random_fh, state, rec, err, err_info, NULL);
}
static void peak_trc_close(wtap* wth)
{
peak_trc_state_t* state = (peak_trc_state_t*)wth->priv;
if (state != NULL)
{
if (state->data_matcher != NULL)
g_regex_unref(state->data_matcher);
}
}
wtap_open_return_val
peak_trc_open(wtap* wth, int* err, char** err_info)
{
gint64 data_offset = 0;
/* wth->priv stores a pointer to the general file properties.
* It it updated when the header data is parsed */
wth->priv = g_new0(peak_trc_state_t, 1);
wtap_open_return_val open_val = peak_trc_parse(wth, &data_offset, err, err_info);
if (open_val != WTAP_OPEN_MINE)
{
peak_trc_close(wth);
g_free(wth->priv);
wth->priv = NULL;
return open_val;
}
#ifdef PEAK_TRC_DEBUG
peak_trc_debug_printf("%s: This is our file\n", G_STRFUNC);
#endif
/* Go to the start of the real packet data since header is now done */
if (file_seek(wth->fh, data_offset, SEEK_SET, err) == -1)
{
*err = errno;
*err_info = g_strdup(g_strerror(errno));
return WTAP_OPEN_ERROR;
}
wtap_set_as_socketcan(wth, peak_trc_file_type_subtype, WTAP_TSPREC_USEC);
wth->subtype_read = peak_trc_read;
wth->subtype_seek_read = peak_trc_seek_read;
wth->subtype_close = peak_trc_close;
return WTAP_OPEN_MINE;
}
static const struct supported_block_type peak_trc_blocks_supported[] = {
/*
* We support packet blocks, with no comments or other options.
*/
{ WTAP_BLOCK_PACKET, MULTIPLE_BLOCKS_SUPPORTED, NO_OPTIONS_SUPPORTED }
};
static const struct file_type_subtype_info peak_trc_info = {
"PEAK CAN TRC file", "peak-trc", "trc", NULL,
false, BLOCKS_SUPPORTED(peak_trc_blocks_supported),
NULL, NULL, NULL
};
void register_peak_trc(void)
{
peak_trc_file_type_subtype = wtap_register_file_type_subtype(&peak_trc_info);
}
/*
* Editor modelines - https://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:
*/