Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add support for reading Wireshark SocketCAN log format #790

Merged
merged 2 commits into from
Jul 9, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ of this program. It can load and save in several formats:
9. Vehicle Spy log files
10. CANDump / Kayak (Read only)
11. PCAN Viewer (Read Only)
12. Wireshark socketcan PCAP file (Read only)

## Dependencies

Expand Down
131 changes: 119 additions & 12 deletions framefileio.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -191,6 +191,7 @@ bool FrameFileIO::loadFrameFile(QString &fileName, QVector<CANFrame>* frameCache
filters.append(QString(tr("CLX000 (*.txt *.TXT)")));
filters.append(QString(tr("CANServer Binary Log (*.log *.LOG)")));
filters.append(QString(tr("Wireshark (*.pcap *.PCAP *.pcapng *.PCAPNG)")));
filters.append(QString(tr("Wireshark SocketCAN (*.pcap *.PCAP")));

dialog.setDirectory(settings.value("FileIO/LoadSaveDirectory", dialog.directory().path()).toString());
dialog.setFileMode(QFileDialog::ExistingFile);
Expand Down Expand Up @@ -237,6 +238,7 @@ bool FrameFileIO::loadFrameFile(QString &fileName, QVector<CANFrame>* frameCache
if (selectedNameFilter == filters[22]) result = loadCLX000File(filename, frameCache);
if (selectedNameFilter == filters[23]) result = loadCANServerFile(filename, frameCache);
if (selectedNameFilter == filters[24]) result = loadWiresharkFile(filename, frameCache);
if (selectedNameFilter == filters[25]) result = loadWiresharkSocketCANFile(filename, frameCache);


progress.cancel();
Expand Down Expand Up @@ -288,6 +290,28 @@ bool FrameFileIO::autoDetectLoadFile(QString filename, QVector<CANFrame>* frames
}
}

// Attempt to load socket CAN first to avoid generic wireshark logic catching it
qDebug() << "Attempting Wireshark Socket CAN Log";
if (isWiresharkSocketCANFile(filename))
{
if (loadWiresharkSocketCANFile(filename, frames))
{
qDebug() << "Loaded as Wireshark SocketCAN Log successfully!";
return true;
}
}

// This and the decoder above were both moved above TeslaAPFile as they match based on magic numbers and sometimes these files were falling into the TeslaAP decoder
qDebug() << "Attempting Wireshark Log";
if (isWiresharkFile(filename))
{
if (loadWiresharkFile(filename, frames))
{
qDebug() << "Loaded as Wireshark Log successfully!";
return true;
}
}

qDebug() << "Attempting Tesla AP Snapshot";
if (isTeslaAPFile(filename))
{
Expand All @@ -308,16 +332,6 @@ bool FrameFileIO::autoDetectLoadFile(QString filename, QVector<CANFrame>* frames
}
}

qDebug() << "Attempting Wireshark Log";
if (isWiresharkFile(filename))
{
if (loadWiresharkFile(filename, frames))
{
qDebug() << "Loaded as Wireshark Log successfully!";
return true;
}
}

qDebug() << "Attempting canalyzer ASC";
if (isCanalyzerASC(filename))
{
Expand Down Expand Up @@ -5023,7 +5037,7 @@ bool FrameFileIO::loadWiresharkFile(QString filename, QVector<CANFrame>* frames)

QByteArray ba = filename.toLocal8Bit();

pcap_data_file = pcap_open_offline(ba.data(), errbuf);
pcap_data_file = pcap_open_offline(ba.data(), errbuf, PCAP_LINKTYPE_ANY);
if (!pcap_data_file) {
return false;
}
Expand Down Expand Up @@ -5079,7 +5093,7 @@ bool FrameFileIO::isWiresharkFile(QString filename)
char errbuf[PCAP_ERRBUF_SIZE];
QByteArray ba = filename.toLocal8Bit();

pcap_data_file = pcap_open_offline(ba.data(), errbuf);
pcap_data_file = pcap_open_offline(ba.data(), errbuf, PCAP_LINKTYPE_ANY);
if (!pcap_data_file) {
return false;
}
Expand All @@ -5089,3 +5103,96 @@ bool FrameFileIO::isWiresharkFile(QString filename)

return true;
}

bool FrameFileIO::loadWiresharkSocketCANFile(QString filename, QVector<CANFrame>* frames)
{
pcap_t *pcap_data_file;
CANFrame thisFrame;
long long startTimestamp = 0;
long long timeStamp;
int lineCounter = 0;
bool foundErrors = false;
pcap_pkthdr packetHeader;
const char *packetData = NULL;
char errbuf[PCAP_ERRBUF_SIZE];

QByteArray ba = filename.toLocal8Bit();

pcap_data_file = pcap_open_offline(ba.data(), errbuf, PCAP_LINKTYPE_SOCKETCAN);
if (!pcap_data_file) {
return false;
}

packetData = (const char*)pcap_next(pcap_data_file, &packetHeader);
while (packetData) {
lineCounter++;
if (lineCounter > 100) {
qApp->processEvents();
lineCounter = 0;
}
thisFrame.bus = 0;

// Timestamp
timeStamp = packetHeader.ts.tv_sec * 1000000 + packetHeader.ts.tv_usec;
if (0 == startTimestamp) {
startTimestamp = timeStamp;
}
timeStamp -= startTimestamp;
thisFrame.setTimeStamp(QCanBusFrame::TimeStamp(0, timeStamp));

// ID and extended frame format
const quint32 can_id = qFromBigEndian<quint32>(packetData);
if (can_id & 0x80000000) {
thisFrame.setExtendedFrameFormat(true);
thisFrame.setFrameId(0x1fffffff & can_id);
} else {
thisFrame.setExtendedFrameFormat(false);
thisFrame.setFrameId(0x7ff & can_id);
}

// Frame type
if (can_id & 0x20000000U) {
thisFrame.setFrameType(QCanBusFrame::ErrorFrame);
} else if (can_id & 0x40000000U) {
thisFrame.setFrameType(QCanBusFrame::RemoteRequestFrame);
} else {
thisFrame.setFrameType(QCanBusFrame::DataFrame);
}

// Direction - This isn't actually officially supported, but CAN Bus Debugger device logs set this byte to 1 to indicate a TX frame and 0 for RX
quint8 direction = (quint8) *(packetData + 6);
thisFrame.isReceived = (direction != 1);

// Data
quint8 numBytes = (quint8) *(packetData + 4);
if (numBytes > 8) {
numBytes = 8;
}
QByteArray bytes(numBytes, 0);
for (int d = 0; d < numBytes; d++) {
bytes[d] = *(packetData + 8 + d);
}
thisFrame.setPayload(bytes);
frames->append(thisFrame);

packetData = (const char*) pcap_next(pcap_data_file, &packetHeader);
}
pcap_close(pcap_data_file);
pcap_data_file = NULL;
return !foundErrors;
}

bool FrameFileIO::isWiresharkSocketCANFile(QString filename)
{
pcap_t *pcap_data_file;
char errbuf[PCAP_ERRBUF_SIZE];
QByteArray ba = filename.toLocal8Bit();

pcap_data_file = pcap_open_offline(ba.data(), errbuf, PCAP_LINKTYPE_SOCKETCAN);
if (!pcap_data_file) {
return false;
}
pcap_close(pcap_data_file);
pcap_data_file = NULL;
return true;
}
2 changes: 2 additions & 0 deletions framefileio.h
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,7 @@ class FrameFileIO: public QObject
static bool loadCLX000File(QString filename, QVector<CANFrame>* frames);
static bool loadCANServerFile(QString filename, QVector<CANFrame>* frames);
static bool loadWiresharkFile(QString filename, QVector<CANFrame>* frames);
static bool loadWiresharkSocketCANFile(QString filename, QVector<CANFrame>* frames);

//functions that pre-scan a file to try to figure out if they could read it. Used to automatically determine
//file type and load it.
Expand All @@ -80,6 +81,7 @@ class FrameFileIO: public QObject
static bool isCLX000File(QString filename);
static bool isCANServerFile(QString filename);
static bool isWiresharkFile(QString filename);
static bool isWiresharkSocketCANFile(QString filename);

static bool saveCRTDFile(QString, const QVector<CANFrame>*);
static bool saveNativeCSVFile(QString, const QVector<CANFrame>*);
Expand Down
21 changes: 18 additions & 3 deletions pcaplite.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@
static unsigned char pcap_buffer[MAX_CAN_PACKET_SIZE];
static pcap_t p;

pcap *pcap_open_offline(const char *filename, char *error_text) {
pcap *pcap_open_offline(const char *filename, char *error_text, int expected_link_type) {
FILE *file;

snprintf(error_text, PCAP_ERRBUF_SIZE, "OK");
Expand Down Expand Up @@ -48,6 +48,23 @@ pcap *pcap_open_offline(const char *filename, char *error_text) {
return (NULL);
}

unsigned int link_type;
fseek(file, PCAP_FILE_HEADER_LENGTH - 4, SEEK_SET);
bytes_read = fread(&link_type, 1, sizeof(link_type), file);
if (bytes_read != sizeof(link_type)) {
snprintf(error_text, PCAP_ERRBUF_SIZE, "Cannot read linktype word");
fclose(file);
return (NULL);
}
if (expected_link_type >= 0) {
// Check the link type
if ((int) link_type != expected_link_type) {
snprintf(error_text, PCAP_ERRBUF_SIZE, "This link type is not supported by this decoder");
fclose(file);
return (NULL);
}
}

// set the format
// and seek past file header
if (MAGIC_NG == magic) {
Expand All @@ -61,14 +78,12 @@ pcap *pcap_open_offline(const char *filename, char *error_text) {
return (NULL);
}
fseek(file, section_length, SEEK_SET);

} else {
p.is_ng = 0;
fseek(file, PCAP_FILE_HEADER_LENGTH, SEEK_SET);
}

p.file = file;

return(&p);
}

Expand Down
6 changes: 4 additions & 2 deletions pcaplite.h
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,9 @@
#include <winsock.h>
#endif

#define PCAP_ERRBUF_SIZE 256
#define PCAP_ERRBUF_SIZE (256)
#define PCAP_LINKTYPE_SOCKETCAN (227)
#define PCAP_LINKTYPE_ANY (-1)

struct pcap_pkthdr {
struct timeval ts; /* time stamp */
Expand All @@ -23,7 +25,7 @@ struct pcap {

typedef struct pcap pcap_t;

pcap *pcap_open_offline(const char *, char *);
pcap *pcap_open_offline(const char *, char *, int);

const unsigned char *pcap_next(pcap_t *, struct pcap_pkthdr *);

Expand Down