Skip to content

Commit

Permalink
Merge pull request collin80#452 from tcfx44/master
Browse files Browse the repository at this point in the history
 Support for reading Wireshark files
  • Loading branch information
collin80 authored Feb 6, 2022
2 parents 37e8ad5 + a102cd4 commit 3901e4f
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 3901e4f

Please sign in to comment.