Add support for reading Wireshark SocketCAN log format
This commit is contained in:
@@ -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
|
||||
|
||||
|
||||
+119
-12
@@ -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);
|
||||
@@ -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();
|
||||
@@ -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))
|
||||
{
|
||||
@@ -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))
|
||||
{
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -54,6 +54,7 @@ public:
|
||||
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.
|
||||
@@ -80,6 +81,7 @@ public:
|
||||
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>*);
|
||||
|
||||
+18
-3
@@ -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");
|
||||
@@ -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) {
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
|
||||
+4
-2
@@ -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 */
|
||||
@@ -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 *);
|
||||
|
||||
|
||||
Reference in New Issue
Block a user