ostinato/common/pcapfileformat.cpp
2011-02-18 23:32:57 +05:30

253 lines
6.8 KiB
C++

/*
Copyright (C) 2010 Srivats P.
This file is part of "Ostinato"
This is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>
*/
#include "pcapfileformat.h"
#include "streambase.h"
#include "hexdump.pb.h"
#include <QDataStream>
#include <QFile>
#include <QtGlobal>
static inline quint32 swap32(quint32 val)
{
return (((val >> 24) && 0x000000FF) |
((val >> 16) && 0x0000FF00) |
((val << 16) && 0x00FF0000) |
((val << 24) && 0xFF000000));
}
static inline quint16 swap16(quint16 val)
{
return (((val >> 8) && 0x00FF) |
((val << 8) && 0xFF00));
}
typedef struct {
quint32 magicNumber; /* magic number */
quint16 versionMajor; /* major version number */
quint16 versionMinor; /* minor version number */
qint32 thisZone; /* GMT to local correction */
quint32 sigfigs; /* accuracy of timestamps */
quint32 snapLen; /* max length of captured packets, in octets */
quint32 network; /* data link type */
} PcapFileHeader;
const quint32 kPcapFileMagic = 0xa1b2c3d4;
const quint32 kPcapFileMagicSwapped = 0xd4c3b2a1;
const quint16 kPcapFileVersionMajor = 2;
const quint16 kPcapFileVersionMinor = 4;
const quint32 kMaxSnapLen = 65535;
typedef struct {
quint32 tsSec; /* timestamp seconds */
quint32 tsUsec; /* timestamp microseconds */
quint32 inclLen; /* number of octets of packet saved in file */
quint32 origLen; /* actual length of packet */
} PcapPacketHeader;
PcapFileFormat pcapFileFormat;
PcapFileFormat::PcapFileFormat()
{
}
PcapFileFormat::~PcapFileFormat()
{
}
bool PcapFileFormat::openStreams(const QString fileName,
OstProto::StreamConfigList &streams, QString &error)
{
bool isOk = false;
QFile file(fileName);
QDataStream fd;
quint32 magic;
PcapFileHeader fileHdr;
PcapPacketHeader pktHdr;
quint32 len;
int pktCount = 1;
QByteArray pktBuf;
if (!file.open(QIODevice::ReadOnly))
goto _err_open;
if (file.size() < sizeof(fileHdr))
goto _err_truncated;
fd.setDevice(&file);
fd >> magic;
if (magic == kPcapFileMagicSwapped)
{
// Toggle Byte order
if (fd.byteOrder() == QDataStream::BigEndian)
fd.setByteOrder(QDataStream::LittleEndian);
else
fd.setByteOrder(QDataStream::BigEndian);
}
else if (magic != kPcapFileMagic)
goto _err_bad_magic;
fd >> fileHdr.versionMajor;
fd >> fileHdr.versionMinor;
fd >> fileHdr.thisZone;
fd >> fileHdr.sigfigs;
fd >> fileHdr.snapLen;
fd >> fileHdr.network;
if ((fileHdr.versionMajor != kPcapFileVersionMajor) ||
(fileHdr.versionMinor != kPcapFileVersionMinor))
goto _err_unsupported_version;
// TODO: what do we do about non-ethernet networks?
pktBuf.resize(fileHdr.snapLen);
while (!fd.atEnd())
{
OstProto::Stream *stream = streams.add_stream();
OstProto::Protocol *proto = stream->add_protocol();
OstProto::HexDump *hexDump = proto->MutableExtension(OstProto::hexDump);
stream->mutable_stream_id()->set_id(pktCount);
stream->mutable_core()->set_is_enabled(true);
proto->mutable_protocol_id()->set_id(
OstProto::Protocol::kHexDumpFieldNumber);
// read PcapPacketHeader
fd >> pktHdr.tsSec;
fd >> pktHdr.tsUsec;
fd >> pktHdr.inclLen;
fd >> pktHdr.origLen;
// TODO: chk fd.status()
// validations on inclLen <= origLen && inclLen <= snapLen
Q_ASSERT(pktHdr.inclLen <= fileHdr.snapLen); // TODO: convert to if
// read Pkt contents
len = fd.readRawData(pktBuf.data(), pktHdr.inclLen); // TODO: use while?
Q_ASSERT(len == pktHdr.inclLen); // TODO: remove assert
hexDump->set_content(pktBuf.data(), pktHdr.inclLen);
hexDump->set_pad_until_end(false);
stream->mutable_core()->set_frame_len(pktHdr.inclLen+4); // FCS
pktCount++;
}
isOk = true;
goto _exit;
_err_unsupported_version:
error = QString(tr("%1 is in PCAP version %2.%3 format which is "
"not supported - Sorry!"))
.arg(fileName).arg(fileHdr.versionMajor).arg(fileHdr.versionMinor);
goto _exit;
_err_bad_magic:
error = QString(tr("%1 is not a valid PCAP file")).arg(fileName);
goto _exit;
_err_truncated:
error = QString(tr("%1 is too short")).arg(fileName);
goto _exit;
_err_open:
error = QString(tr("Unable to open file: %1")).arg(fileName);
goto _exit;
_exit:
return isOk;
}
bool PcapFileFormat::saveStreams(const OstProto::StreamConfigList streams,
const QString fileName, QString &error)
{
bool isOk = false;
QFile file(fileName);
QDataStream fd;
PcapFileHeader fileHdr;
PcapPacketHeader pktHdr;
QByteArray pktBuf;
if (!file.open(QIODevice::WriteOnly))
goto _err_open;
fd.setDevice(&file);
fileHdr.magicNumber = kPcapFileMagic;
fileHdr.versionMajor = kPcapFileVersionMajor;
fileHdr.versionMinor = kPcapFileVersionMinor;
fileHdr.thisZone = 0;
fileHdr.sigfigs = 0;
fileHdr.snapLen = kMaxSnapLen;
fileHdr.network = 1; // Ethernet; FIXME: Hardcoding
fd << fileHdr.magicNumber;
fd << fileHdr.versionMajor;
fd << fileHdr.versionMinor;
fd << fileHdr.thisZone;
fd << fileHdr.sigfigs;
fd << fileHdr.snapLen;
fd << fileHdr.network;
pktBuf.resize(kMaxSnapLen);
for (int i = 0; i < streams.stream_size(); i++)
{
StreamBase s;
s.setId(i);
s.protoDataCopyFrom(streams.stream(i));
// TODO: expand frameIndex for each stream
s.frameValue((uchar*)pktBuf.data(), pktBuf.size(), 0);
// TODO: write actual timing!?!?
pktHdr.tsSec = 0;
pktHdr.tsUsec = 0;
pktHdr.inclLen = pktHdr.origLen = s.frameLen() - 4; // FCS; FIXME: Hardcoding
if (pktHdr.inclLen > fileHdr.snapLen)
pktHdr.inclLen = fileHdr.snapLen;
fd << pktHdr.tsSec;
fd << pktHdr.tsUsec;
fd << pktHdr.inclLen;
fd << pktHdr.origLen;
fd.writeRawData(pktBuf.data(), pktHdr.inclLen);
}
file.close();
isOk = true;
goto _exit;
_err_open:
error = QString(tr("Unable to open file: %1")).arg(fileName);
goto _exit;
_exit:
return isOk;
}