Merge pull request #452 from tcfx44/master
Support for reading Wireshark files
This commit is contained in:
+4
-2
@@ -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 \
|
||||
@@ -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 \
|
||||
|
||||
@@ -8,6 +8,7 @@
|
||||
#include <QSettings>
|
||||
#include <iostream>
|
||||
#include <memory>
|
||||
#include "pcaplite.h"
|
||||
|
||||
#include "utility.h"
|
||||
#include "blfhandler.h"
|
||||
@@ -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);
|
||||
@@ -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();
|
||||
@@ -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))
|
||||
{
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -53,6 +53,7 @@ public:
|
||||
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.
|
||||
@@ -78,6 +79,7 @@ public:
|
||||
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>*);
|
||||
|
||||
+214
@@ -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(§ion_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);
|
||||
}
|
||||
+32
@@ -0,0 +1,32 @@
|
||||
#ifndef PCAPLITE_H
|
||||
#define PCAPLITE_H
|
||||
|
||||
#include <stdio.h>
|
||||
#if defined(unix)
|
||||
#include <sys/time.h>
|
||||
#else
|
||||
#include <winsock.h>
|
||||
#endif
|
||||
|
||||
#define PCAP_ERRBUF_SIZE 256
|
||||
|
||||
struct pcap_pkthdr {
|
||||
struct timeval ts; /* time stamp */
|
||||
unsigned int caplen; /* length of portion present */
|
||||
unsigned int len; /* length of this packet (off wire) */
|
||||
};
|
||||
|
||||
struct pcap {
|
||||
FILE *file;
|
||||
bool is_ng;
|
||||
};
|
||||
|
||||
typedef struct pcap pcap_t;
|
||||
|
||||
pcap *pcap_open_offline(const char *, char *);
|
||||
|
||||
const unsigned char *pcap_next(pcap_t *, struct pcap_pkthdr *);
|
||||
|
||||
void pcap_close(pcap_t *);
|
||||
|
||||
#endif// PCAPLITE_H
|
||||
Reference in New Issue
Block a user