Skip to content

Commit

Permalink
Support for reading Wireshark files
Browse files Browse the repository at this point in the history
Detection of Wireshark files implemented

Guard statements for build on Linux

pcaplite added

Wireshark support works on Windows

pcap support on Lunux fixed

Build warning fixed

Update pcaplite.cpp

Cleanup

Removed extra define
  • Loading branch information
tcfx44 committed Jan 29, 2022
1 parent 37e8ad5 commit a102cd4
Show file tree
Hide file tree
Showing 5 changed files with 348 additions and 2 deletions.
6 changes: 4 additions & 2 deletions SavvyCAN.pro
Original file line number Diff line number Diff line change
Expand Up @@ -92,7 +92,8 @@ SOURCES += main.cpp\
re/sniffer/SnifferDelegate.cpp \
connections/newconnectiondialog.cpp \
re/temporalgraphwindow.cpp \
filterutility.cpp
filterutility.cpp \
pcaplite.cpp

HEADERS += mainwindow.h \
can_structs.h \
Expand Down Expand Up @@ -178,7 +179,8 @@ HEADERS += mainwindow.h \
re/sniffer/SnifferDelegate.h \
connections/newconnectiondialog.h \
re/temporalgraphwindow.h \
filterutility.h
filterutility.h \
pcaplite.h

FORMS += ui/candatagrid.ui \
ui/dbccomparatorwindow.ui \
Expand Down
96 changes: 96 additions & 0 deletions framefileio.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
#include <QSettings>
#include <iostream>
#include <memory>
#include "pcaplite.h"

#include "utility.h"
#include "blfhandler.h"
Expand Down Expand Up @@ -189,6 +190,7 @@ bool FrameFileIO::loadFrameFile(QString &fileName, QVector<CANFrame>* frameCache
filters.append(QString(tr("Tesla Autopilot Snapshot (*.CAN *.can)")));
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)")));

dialog.setDirectory(settings.value("FileIO/LoadSaveDirectory", dialog.directory().path()).toString());
dialog.setFileMode(QFileDialog::ExistingFile);
Expand Down Expand Up @@ -234,6 +236,7 @@ bool FrameFileIO::loadFrameFile(QString &fileName, QVector<CANFrame>* frameCache
if (selectedNameFilter == filters[21]) result = loadTeslaAPFile(filename, 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);


progress.cancel();
Expand Down Expand Up @@ -305,6 +308,16 @@ 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 @@ -4825,3 +4838,86 @@ bool FrameFileIO::loadCANServerFile(QString filename, QVector<CANFrame>* frames)
delete inFile;
return !foundErrors;
}

bool FrameFileIO::loadWiresharkFile(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];

thisFrame.setFrameType(QCanBusFrame::DataFrame);

QByteArray ba = filename.toLocal8Bit();

pcap_data_file = pcap_open_offline(ba.data(), errbuf);
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;
}

timeStamp = packetHeader.ts.tv_sec * 1000000 + packetHeader.ts.tv_usec;
if (0 == startTimestamp)
{
startTimestamp = timeStamp;
}

timeStamp -= startTimestamp;

thisFrame.setTimeStamp(QCanBusFrame::TimeStamp(0, timeStamp));
thisFrame.isReceived = true; // TODO: check if tx detection is possible

thisFrame.setFrameType(QCanBusFrame::DataFrame);
thisFrame.setFrameId((0xff & *(packetData+17)) << 8 | (0xff & *(packetData+16)));
if (thisFrame.frameId() <= 0x7FF) thisFrame.setExtendedFrameFormat(false);
else thisFrame.setExtendedFrameFormat(true);
thisFrame.bus = 0;
int numBytes = *(packetData+20);
QByteArray bytes(numBytes, 0);
if (thisFrame.payload().length() > 8) thisFrame.payload().resize(8);
for (int d = 0; d < numBytes; d++)
{
bytes[d] = *(packetData + 24 + 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::isWiresharkFile(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);
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 @@ -53,6 +53,7 @@ class FrameFileIO: public QObject
static bool loadTeslaAPFile(QString filename, QVector<CANFrame>* frames);
static bool loadCLX000File(QString filename, QVector<CANFrame>* frames);
static bool loadCANServerFile(QString filename, QVector<CANFrame>* frames);
static bool loadWiresharkFile(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 @@ -78,6 +79,7 @@ class FrameFileIO: public QObject
static bool isTeslaAPFile(QString filename);
static bool isCLX000File(QString filename);
static bool isCANServerFile(QString filename);
static bool isWiresharkFile(QString filename);

static bool saveCRTDFile(QString, const QVector<CANFrame>*);
static bool saveNativeCSVFile(QString, const QVector<CANFrame>*);
Expand Down
214 changes: 214 additions & 0 deletions pcaplite.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,214 @@
#include <math.h>
#include "pcaplite.h"

#define MAGIC_NG 0x0A0D0D0A
#define MACIG 0xA1B2C3D4

#define MAX_CAN_PACKET_SIZE 256

// some pcap format constants
#define PCAP_FILE_HEADER_LENGTH 24
#define PCAP_FRAME_HEADER_LENGTH 16
#define PCAP_CAP_FRAME_LENGTH_OFFSET 8
#define PCAP_FRAME_LENGTH_OFFSET 12

// some pcapng format constants
#define INTERFACE_DESCRITION_BLOCK 0x01
#define ENCHANCED_PACKET_BLOCK 0x06

static unsigned char pcap_buffer[MAX_CAN_PACKET_SIZE];
static pcap_t p;

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

snprintf(error_text, PCAP_ERRBUF_SIZE, "OK");

file = fopen(filename,"rb");

if (NULL == file) {
snprintf(error_text, PCAP_ERRBUF_SIZE,
"Cannot open input file");
return NULL;
}

unsigned int magic;
size_t bytes_read;

bytes_read = fread(&magic, 1, sizeof(magic), file);
if (bytes_read != sizeof(magic)) {
snprintf(error_text, PCAP_ERRBUF_SIZE, "Cannot read magic word");
fclose(file);
return (NULL);
}

if (magic != MACIG && magic != MAGIC_NG) {
snprintf(error_text, PCAP_ERRBUF_SIZE, "Not a supported format %04x", magic);
fclose(file);
return (NULL);
}

// set the format
// and seek past file header
if (MAGIC_NG == magic) {
p.is_ng = 1;
fseek(file, sizeof(magic), SEEK_SET);
unsigned int section_length;
bytes_read = fread(&section_length, 1, sizeof(section_length), file);
if (bytes_read != sizeof(section_length)) {
snprintf(error_text, PCAP_ERRBUF_SIZE, "Cannot read section length");
fclose(file);
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);
}

const unsigned char *pcap_next_ng(pcap_t *p, struct pcap_pkthdr *h) {
// this is for ENCHANCED_PACKET_BLOCK
struct block_header {
unsigned int block_type;
unsigned int block_size;
unsigned int interface_id;
unsigned int timestamp_hi;
unsigned int timestamp_lo;
unsigned int cap_len;
unsigned int len;
} bh;

struct option_header {
unsigned short option_type;
unsigned short option_length;
} oh;

size_t bytes_read;

static double timestamp_multiplier = 0.000001; //microseconds resolution

long fpos = ftell(p->file);

do {
bytes_read = fread(&bh, 1, sizeof(bh), p->file);
if (bytes_read != sizeof(bh)) {
//probably EOF
return (NULL);
}

if (bh.block_type != ENCHANCED_PACKET_BLOCK) {
if (INTERFACE_DESCRITION_BLOCK == bh.block_type) {
// Seek to start of options part in the header
fseek(p->file, fpos + 16, SEEK_SET);
do {
bytes_read = fread(&oh, 1, sizeof(oh), p->file);
if (bytes_read != sizeof(oh)) {
//probably EOF
return (NULL);
}

// calcualte option length aligned on 4 byte boundary
unsigned int option_file_length = 4*(oh.option_length/4) + ((oh.option_length%4) ? 4:0);


// read in the buffer if it's large enough or skip
if (option_file_length <= sizeof(pcap_buffer)) {
bytes_read = fread(pcap_buffer, 1, option_file_length, p->file);
if (bytes_read != option_file_length) {
//probably EOF
return (NULL);
}
//check if time resolution option
if (0x09 == oh.option_type) {
unsigned int res = *(unsigned int*)pcap_buffer;
if ((0x80000000 & res) == 0) {
timestamp_multiplier = 1/pow(10, res);
} else {
timestamp_multiplier = 1/pow(2, (res & 0x7fffffff));
}
}
} else if (fseek(p->file, option_file_length, SEEK_CUR)) {
//probably EOF
return (NULL);
}

} while(ftell(p->file) < fpos + bh.block_size - sizeof(bh.block_size));

if (fseek(p->file, sizeof(bh.block_size), SEEK_CUR)) {
//probably EOF
return (NULL);
}
}

// skip to the next block
if (fseek(p->file, fpos + bh.block_size, SEEK_SET)) {
//probably EOF
return (NULL);
}
fpos = ftell(p->file);
}
} while (bh.block_type != ENCHANCED_PACKET_BLOCK);

h->caplen = bh.cap_len;

double timestamp = ((unsigned long long)bh.timestamp_hi << 32 | bh.timestamp_lo) * timestamp_multiplier;
double fractional, integer;

fractional = modf(timestamp, &integer);
h->ts.tv_sec = (long)integer;
h->ts.tv_usec = (long)(fractional * 1000000);

bytes_read = fread(pcap_buffer, 1, h->caplen, p->file);
if (bytes_read != h->caplen) {
//probably EOF
return (NULL);
}

//seek to the next block (go back with bytes read of the block and seek fwd with the block size)
if (fseek(p->file, fpos + bh.block_size, SEEK_SET)) {
//probably EOF
return (NULL);
}

return pcap_buffer;
}


const unsigned char *pcap_next(pcap_t *p, struct pcap_pkthdr *h)
{
if (p->is_ng) {
return pcap_next_ng(p, h);
}

size_t bytes_read;

bytes_read = fread(pcap_buffer, 1, PCAP_FRAME_HEADER_LENGTH, p->file);
if (bytes_read != PCAP_FRAME_HEADER_LENGTH) {
//probably EOF
return (NULL);
}

h->ts.tv_sec = *(unsigned int*)(pcap_buffer);
h->ts.tv_usec = *(unsigned int*)(pcap_buffer + 4);

h->caplen = *(unsigned int*)(pcap_buffer + PCAP_CAP_FRAME_LENGTH_OFFSET);
h->len = *(unsigned int*)(pcap_buffer + PCAP_FRAME_LENGTH_OFFSET);

bytes_read = fread(pcap_buffer, 1, h->caplen, p->file);
if (bytes_read != h->caplen) {
//probably EOF
return (NULL);
}

return pcap_buffer;
}

void pcap_close(pcap_t *p) {
fclose(p->file);
}
Loading

0 comments on commit a102cd4

Please sign in to comment.