mirror of
https://github.com/intrepidcs/libicsneo.git
synced 2026-08-05 01:18:36 +02:00
Device/Disk: Add VSA read and parse functionality
Implement ability to extract network traffic (CAN, LIN, Ethernet, etc.) from VSA message records on disk. Add a method to Device class that uses the VSAParser and the individual record types to extract messages from the VSA message records and pass them back to the communication system. This routes messages such that it appears as if they were discovered live instead of read from disk. The parse process (in Device) requires determination of metadata about the VSA file system on a device before it can begin extracting messages. This currently only handles data captured from the current coremini script on a device.
This commit is contained in:
@@ -0,0 +1,460 @@
|
||||
#include "icsneo/disk/vsa/vsaparser.h"
|
||||
#include "icsneo/disk/vsa/vsa02.h"
|
||||
#include "icsneo/disk/vsa/vsa03.h"
|
||||
#include "icsneo/disk/vsa/vsa04.h"
|
||||
#include "icsneo/disk/vsa/vsa05.h"
|
||||
#include "icsneo/disk/vsa/vsa06.h"
|
||||
#include "icsneo/disk/vsa/vsa07.h"
|
||||
#include "icsneo/disk/vsa/vsa08.h"
|
||||
#include "icsneo/disk/vsa/vsa09.h"
|
||||
#include "icsneo/disk/vsa/vsa0b.h"
|
||||
#include "icsneo/disk/vsa/vsa0c.h"
|
||||
#include "icsneo/disk/vsa/vsa0d.h"
|
||||
#include "icsneo/disk/vsa/vsa0e.h"
|
||||
#include "icsneo/disk/vsa/vsa0f.h"
|
||||
#include "icsneo/disk/vsa/vsa6a.h"
|
||||
#include "icsneo/disk/diskdriver.h"
|
||||
|
||||
#include <iostream>
|
||||
|
||||
using namespace icsneo;
|
||||
|
||||
bool VSAParser::parseBytes(uint8_t* const bytes, uint64_t arrLen)
|
||||
{
|
||||
uint64_t bytesOffset = 0;
|
||||
while(bytesOffset + VSA::StandardRecordSize <= arrLen) { // Enough bytes to read for Standard Record
|
||||
if(bytes[bytesOffset] != 0xAAu) {
|
||||
// Invalid Input
|
||||
return false;
|
||||
}
|
||||
switch(bytes[bytesOffset + 1]) {
|
||||
case 0x00u: // Pad Record
|
||||
bytesOffset += VSA::StandardRecordSize;
|
||||
break;
|
||||
case 0x01u: // Message Data (Deprecated)
|
||||
hasDeprecatedRecords = true;
|
||||
bytesOffset += VSA::StandardRecordSize;
|
||||
break;
|
||||
case 0x02u: // Logdata Record
|
||||
if(settings.extractAA02) {
|
||||
vsaRecords.push_back(std::make_shared<VSA02>(bytes + bytesOffset));
|
||||
}
|
||||
bytesOffset += VSA::StandardRecordSize;
|
||||
break;
|
||||
case 0x03u: // Event Record
|
||||
if(settings.extractAA03) {
|
||||
vsaRecords.push_back(std::make_shared<VSA03>(bytes + bytesOffset));
|
||||
}
|
||||
bytesOffset += VSA::StandardRecordSize;
|
||||
break;
|
||||
case 0x04u: // Partition Info Record
|
||||
if(settings.extractAA04) {
|
||||
vsaRecords.push_back(std::make_shared<VSA04>(bytes + bytesOffset));
|
||||
}
|
||||
bytesOffset += VSA::StandardRecordSize;
|
||||
break;
|
||||
case 0x05u: // Application Error Record
|
||||
if(settings.extractAA05) {
|
||||
vsaRecords.push_back(std::make_shared<VSA05>(bytes + bytesOffset));
|
||||
}
|
||||
bytesOffset += VSA::StandardRecordSize;
|
||||
break;
|
||||
case 0x06u: // Debug/Internal
|
||||
if(settings.extractAA06) {
|
||||
vsaRecords.push_back(std::make_shared<VSA06>(bytes + bytesOffset));
|
||||
}
|
||||
bytesOffset += VSA::StandardRecordSize;
|
||||
break;
|
||||
case 0x07u: // Debug/Internal
|
||||
if(settings.extractAA07) {
|
||||
vsaRecords.push_back(std::make_shared<VSA07>(bytes + bytesOffset));
|
||||
}
|
||||
bytesOffset += VSA::StandardRecordSize;
|
||||
break;
|
||||
case 0x08u: // Buffer Info Record
|
||||
if(settings.extractAA08) {
|
||||
vsaRecords.push_back(std::make_shared<VSA08>(bytes + bytesOffset));
|
||||
}
|
||||
bytesOffset += VSA::StandardRecordSize;
|
||||
break;
|
||||
case 0x09u: // Device Info Record
|
||||
if(settings.extractAA09) {
|
||||
vsaRecords.push_back(std::make_shared<VSA09>(bytes + bytesOffset));
|
||||
}
|
||||
bytesOffset += VSA::StandardRecordSize;
|
||||
break;
|
||||
case 0x0Au: // Logger Info Configuration (Deprecated)
|
||||
hasDeprecatedRecords = true;
|
||||
bytesOffset += VSA::StandardRecordSize;
|
||||
break;
|
||||
case 0x0Bu: // Message Data
|
||||
if(settings.extractAA0B) {
|
||||
auto record = std::make_shared<VSA0B>(bytes + bytesOffset);
|
||||
vsaRecords.push_back(record);
|
||||
}
|
||||
bytesOffset += VSA::StandardRecordSize;
|
||||
break;
|
||||
case 0x0Cu: // PCM Audio Data
|
||||
if(settings.extractAA0C) {
|
||||
vsaRecords.push_back(std::make_shared<VSA0C>(bytes + bytesOffset));
|
||||
}
|
||||
bytesOffset += VSA::StandardRecordSize;
|
||||
break;
|
||||
case 0x0Du: // Message Data (Extended)
|
||||
if(settings.extractAA0D) {
|
||||
if(!handleExtendedRecord(bytes, bytesOffset, VSA::Type::AA0D)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
bytesOffset += VSA::StandardRecordSize;
|
||||
break;
|
||||
case 0x0Eu: // Message Data (Extended)
|
||||
if(settings.extractAA0E) {
|
||||
if(!handleExtendedRecord(bytes, bytesOffset, VSA::Type::AA0E)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
bytesOffset += VSA::StandardRecordSize;
|
||||
break;
|
||||
case 0x0Fu: // Message Data (Extended)
|
||||
if(settings.extractAA0F) {
|
||||
if(!handleExtendedRecord(bytes, bytesOffset, VSA::Type::AA0F)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
bytesOffset += VSA::StandardRecordSize;
|
||||
break;
|
||||
case 0x6Au: // Logger Configuration Backup
|
||||
if(bytesOffset + Disk::SectorSize <= arrLen) {
|
||||
if(settings.extractAA6A) {
|
||||
vsaRecords.push_back(std::make_shared<VSA6A>(bytes + bytesOffset));
|
||||
}
|
||||
}
|
||||
bytesOffset += Disk::SectorSize;
|
||||
break;
|
||||
default:
|
||||
// Unhandled VSA Record Type
|
||||
return false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool VSAParser::handleExtendedRecord(uint8_t* const bytes, uint64_t& bytesOffset, VSA::Type type)
|
||||
{
|
||||
// Gather info about the extended record sequence of the record contained in bytes
|
||||
std::shared_ptr<VSAExtendedMessage> first;
|
||||
uint16_t seqNum;
|
||||
ExtendedMessageState::ExtendedRecordSeqInfo* seqInfo;
|
||||
uint32_t runningChecksum = 0;
|
||||
switch(type) {
|
||||
case VSA::Type::AA0D:
|
||||
first = std::make_shared<VSA0DFirst>(bytes + bytesOffset, runningChecksum);
|
||||
seqNum = first->getSequenceNum();
|
||||
seqInfo = &state.vsa0DSeqInfo[seqNum];
|
||||
break;
|
||||
case VSA::Type::AA0E:
|
||||
first = std::make_shared<VSA0EFirst>(bytes + bytesOffset, runningChecksum);
|
||||
seqNum = first->getSequenceNum();
|
||||
seqInfo = &state.vsa0ESeqInfo[seqNum];
|
||||
break;
|
||||
case VSA::Type::AA0F:
|
||||
first = std::make_shared<VSA0FFirst>(bytes + bytesOffset, runningChecksum);
|
||||
seqNum = first->getSequenceNum();
|
||||
seqInfo = &state.vsa0FSeqInfo[seqNum];
|
||||
break;
|
||||
default:
|
||||
return false; // Invalid type was passed
|
||||
}
|
||||
|
||||
if(seqInfo->nextIndex == 0 && seqInfo->records.size() == 0) { // This is the first record in the sequence
|
||||
if(first->getIndex() != 0) {
|
||||
seqInfo->clear();
|
||||
report(APIEvent::Type::VSAExtendedMessageError, APIEvent::Severity::EventWarning);
|
||||
return true; // This is not actually the first record
|
||||
}
|
||||
seqInfo->records.push_back(first);
|
||||
seqInfo->totalRecordCount = first->getRecordCount();
|
||||
seqInfo->nextIndex++;
|
||||
seqInfo->runningChecksum = runningChecksum;
|
||||
} else if(seqInfo->nextIndex < seqInfo->totalRecordCount && seqInfo->records.size() > 0) { // Consecutive Record
|
||||
std::shared_ptr<VSAExtendedMessage> consecutive;
|
||||
bool isLast = seqInfo->nextIndex == seqInfo->totalRecordCount - 1;
|
||||
|
||||
// Construct the consecutive record from bytes
|
||||
switch(type) {
|
||||
case VSA::Type::AA0D:
|
||||
consecutive = std::make_shared<VSA0DConsecutive>(
|
||||
bytes + bytesOffset,
|
||||
seqInfo->runningChecksum,
|
||||
std::dynamic_pointer_cast<VSA0DFirst>(seqInfo->records[0]),
|
||||
isLast
|
||||
);
|
||||
break;
|
||||
case VSA::Type::AA0E:
|
||||
consecutive = std::make_shared<VSA0EConsecutive>(
|
||||
bytes + bytesOffset,
|
||||
seqInfo->runningChecksum,
|
||||
std::dynamic_pointer_cast<VSA0EFirst>(seqInfo->records[0]),
|
||||
isLast
|
||||
);
|
||||
break;
|
||||
case VSA::Type::AA0F:
|
||||
consecutive = std::make_shared<VSA0FConsecutive>(
|
||||
bytes + bytesOffset,
|
||||
seqInfo->runningChecksum,
|
||||
std::dynamic_pointer_cast<VSA0FFirst>(seqInfo->records[0]),
|
||||
isLast
|
||||
);
|
||||
break;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
|
||||
if(consecutive->getIndex() == seqInfo->nextIndex && consecutive->getSequenceNum() == seqNum) { // This record is valid in the sequence
|
||||
seqInfo->records.push_back(consecutive);
|
||||
seqInfo->nextIndex++;
|
||||
} else { // Sequence is out of order/invalid
|
||||
// Throw away incomplete sequence and report warning
|
||||
seqInfo->clear();
|
||||
report(APIEvent::Type::VSAExtendedMessageError, APIEvent::Severity::EventWarning);
|
||||
// Save data for new sequence
|
||||
if(first->getIndex() == 0) {
|
||||
seqInfo->records.push_back(first);
|
||||
seqInfo->totalRecordCount = first->getRecordCount();
|
||||
seqInfo->nextIndex++;
|
||||
seqInfo->runningChecksum = runningChecksum;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
if(seqInfo->nextIndex == seqInfo->totalRecordCount) { // This is the last record in the sequence
|
||||
if(consecutive->getChecksumFailed()) {
|
||||
// Fail out if checksum fails
|
||||
seqInfo->clear();
|
||||
return false;
|
||||
}
|
||||
vsaRecords.insert(vsaRecords.end(), seqInfo->records.begin(), seqInfo->records.end());
|
||||
seqInfo->clear();
|
||||
}
|
||||
} else {
|
||||
return false; // Undefined behavior
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
VSAParser::RecordParseStatus VSAParser::getRecordFromBytes(uint8_t* const bytes, size_t arrLen, std::shared_ptr<VSA>& record)
|
||||
{
|
||||
record = nullptr;
|
||||
if(arrLen < VSA::StandardRecordSize) {
|
||||
// Not enough bytes
|
||||
return VSAParser::RecordParseStatus::InsufficientData;
|
||||
} else if(bytes[0] != 0xAAu) {
|
||||
return VSAParser::RecordParseStatus::NotARecordStart;
|
||||
} else {
|
||||
switch(bytes[1]) {
|
||||
case 0x00u: // Pad Record
|
||||
return VSAParser::RecordParseStatus::Pad;
|
||||
case 0x01u: // Message Data (Deprecated)
|
||||
return VSAParser::RecordParseStatus::Deprecated;
|
||||
case 0x02u: // Logdata Record
|
||||
if(settings.extractAA02) {
|
||||
record = std::make_shared<VSA02>(bytes);
|
||||
return VSAParser::RecordParseStatus::Success;
|
||||
}
|
||||
break;
|
||||
case 0x03u: // Event Record
|
||||
if(settings.extractAA03) {
|
||||
record = std::make_shared<VSA03>(bytes);
|
||||
return VSAParser::RecordParseStatus::Success;
|
||||
}
|
||||
break;
|
||||
case 0x04u: // Partition Info Record
|
||||
if(settings.extractAA04) {
|
||||
record = std::make_shared<VSA04>(bytes);
|
||||
return VSAParser::RecordParseStatus::Success;
|
||||
}
|
||||
break;
|
||||
case 0x05u: // Application Error Record
|
||||
if(settings.extractAA05) {
|
||||
record = std::make_shared<VSA05>(bytes);
|
||||
return VSAParser::RecordParseStatus::Success;
|
||||
}
|
||||
break;
|
||||
case 0x06u: // Debug/Internal
|
||||
if(settings.extractAA06) {
|
||||
record = std::make_shared<VSA06>(bytes);
|
||||
return VSAParser::RecordParseStatus::Success;
|
||||
}
|
||||
break;
|
||||
case 0x07u: // Debug/Internal
|
||||
if(settings.extractAA07) {
|
||||
record = std::make_shared<VSA07>(bytes);
|
||||
return VSAParser::RecordParseStatus::Success;
|
||||
}
|
||||
break;
|
||||
case 0x08u: // Buffer Info Record
|
||||
if(settings.extractAA08) {
|
||||
record = std::make_shared<VSA08>(bytes);
|
||||
return VSAParser::RecordParseStatus::Success;
|
||||
}
|
||||
break;
|
||||
case 0x09u: // Device Info Record
|
||||
if(settings.extractAA09) {
|
||||
record = std::make_shared<VSA09>(bytes);
|
||||
return VSAParser::RecordParseStatus::Success;
|
||||
}
|
||||
break;
|
||||
case 0x0Au:
|
||||
return VSAParser::RecordParseStatus::Deprecated;
|
||||
case 0x0Bu: // Message Data
|
||||
if(settings.extractAA0B) {
|
||||
record = std::make_shared<VSA0B>(bytes);
|
||||
return VSAParser::RecordParseStatus::Success;
|
||||
}
|
||||
break;
|
||||
case 0x0Cu: // PCM Audio Data
|
||||
if(settings.extractAA0C) {
|
||||
record = std::make_shared<VSA0C>(bytes);
|
||||
return VSAParser::RecordParseStatus::Success;
|
||||
}
|
||||
break;
|
||||
case 0x0Du: // Message Data (Extended)
|
||||
if(settings.extractAA0D) {
|
||||
uint32_t payloadChecksum = 0;
|
||||
const auto& vsa = std::make_shared<VSA0DFirst>(bytes, payloadChecksum);
|
||||
record = vsa;
|
||||
if(vsa->getIndex() == 0) {
|
||||
return VSAParser::RecordParseStatus::Success;
|
||||
}
|
||||
// This returns the consecutive record as a first record
|
||||
return VSAParser::RecordParseStatus::ConsecutiveExtended;
|
||||
}
|
||||
break;
|
||||
case 0x0Eu: // Message Data (Extended)
|
||||
if(settings.extractAA0E) {
|
||||
uint32_t payloadChecksum = 0;
|
||||
const auto& vsa = std::make_shared<VSA0EFirst>(bytes, payloadChecksum);
|
||||
record = vsa;
|
||||
if(vsa->getIndex() == 0) {
|
||||
return VSAParser::RecordParseStatus::Success;
|
||||
}
|
||||
// This returns the consecutive record as a first record
|
||||
return VSAParser::RecordParseStatus::ConsecutiveExtended;
|
||||
}
|
||||
break;
|
||||
case 0x0Fu: // Message Data (Extended)
|
||||
if(settings.extractAA0F) {
|
||||
uint32_t payloadChecksum = 0;
|
||||
const auto& vsa = std::make_shared<VSA0FFirst>(bytes, payloadChecksum);
|
||||
record = vsa;
|
||||
if(vsa->getIndex() == 0) {
|
||||
return VSAParser::RecordParseStatus::Success;
|
||||
}
|
||||
// This returns the consecutive record as a first record
|
||||
return VSAParser::RecordParseStatus::ConsecutiveExtended;
|
||||
}
|
||||
break;
|
||||
case 0x6Au: // Logger Configuration Backup
|
||||
if(settings.extractAA6A) {
|
||||
if(arrLen < Disk::SectorSize) {
|
||||
return VSAParser::RecordParseStatus::InsufficientData;
|
||||
}
|
||||
record = std::make_shared<VSA6A>(bytes);
|
||||
return VSAParser::RecordParseStatus::Success;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
// Unhandled VSA Record Type
|
||||
return VSAParser::RecordParseStatus::UnknownRecordType;
|
||||
break;
|
||||
}
|
||||
}
|
||||
return VSAParser::RecordParseStatus::FilteredOut;
|
||||
}
|
||||
|
||||
void VSAParser::clearParseState()
|
||||
{
|
||||
for(size_t i = 0; i < state.vsa0DSeqInfo.size(); i++) {
|
||||
state.vsa0DSeqInfo[i].clear();
|
||||
}
|
||||
for(size_t i = 0; i < state.vsa0ESeqInfo.size(); i++) {
|
||||
state.vsa0ESeqInfo[i].clear();
|
||||
}
|
||||
for(size_t i = 0; i < state.vsa0DSeqInfo.size(); i++) {
|
||||
state.vsa0ESeqInfo[i].clear();
|
||||
}
|
||||
}
|
||||
|
||||
bool VSAParser::extractMessagePackets(std::vector<std::shared_ptr<Packet>>& packets)
|
||||
{
|
||||
if(settings != Settings::messageRecords()) {
|
||||
report(APIEvent::Type::ParameterOutOfRange, APIEvent::Severity::Error);
|
||||
return false; // We do not have exclusively message records
|
||||
}
|
||||
std::shared_ptr<Packet> packet;
|
||||
bool activeExtendedMessage = false;
|
||||
VSA::Type previousRecordType = VSA::Type::Invalid;
|
||||
for(const auto& record : vsaRecords) {
|
||||
VSA::Type activeRecordType = record->getType();
|
||||
switch(activeRecordType) {
|
||||
// Handle standard message records
|
||||
case VSA::Type::AA0B: {
|
||||
if(activeExtendedMessage) {
|
||||
// Non-terminated extended message record
|
||||
// There was a failure/unexpected behavior in the parsing process
|
||||
report(APIEvent::Type::VSAExtendedMessageError, APIEvent::Severity::Error);
|
||||
return false;
|
||||
}
|
||||
|
||||
std::shared_ptr<VSAMessage> messageRecord = std::dynamic_pointer_cast<VSAMessage>(record);
|
||||
if(!settings.messageFilter || messageRecord->filter(settings.messageFilter)) {
|
||||
packet = messageRecord->getPacket();
|
||||
packets.push_back(packet);
|
||||
}
|
||||
packet = nullptr;
|
||||
break;
|
||||
}
|
||||
|
||||
// Handle extended message records
|
||||
case VSA::Type::AA0D:
|
||||
case VSA::Type::AA0E:
|
||||
case VSA::Type::AA0F: {
|
||||
std::shared_ptr<VSAExtendedMessage> extendedMessageRecord = std::dynamic_pointer_cast<VSAExtendedMessage>(record);
|
||||
|
||||
if(!activeExtendedMessage) { // Start new extended message packet
|
||||
packet = extendedMessageRecord->getPacket();
|
||||
activeExtendedMessage = true;
|
||||
previousRecordType = extendedMessageRecord->getType();
|
||||
} else if(previousRecordType == activeRecordType) { // Continue existing extended message packet
|
||||
extendedMessageRecord->appendPacket(packet);
|
||||
if(extendedMessageRecord->getRecordCount() == static_cast<uint32_t>(extendedMessageRecord->getIndex() + 1)) { // Last record in sequence
|
||||
if(!settings.messageFilter || extendedMessageRecord->filter(settings.messageFilter)) {
|
||||
VSAExtendedMessage::truncatePacket(packet);
|
||||
packets.push_back(packet);
|
||||
}
|
||||
activeExtendedMessage = false;
|
||||
packet = nullptr;
|
||||
previousRecordType = activeRecordType;
|
||||
}
|
||||
} else {
|
||||
// Non-terminated extended message record
|
||||
// There was a failure/unexpected behavior in the parsing process
|
||||
activeExtendedMessage = false;
|
||||
packet = nullptr;
|
||||
previousRecordType = VSA::Type::Invalid;
|
||||
report(APIEvent::Type::VSAOtherError, APIEvent::Severity::Error);
|
||||
return false;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
default:
|
||||
// Non-message record discovered
|
||||
report(APIEvent::Type::VSAOtherError, APIEvent::Severity::Error);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
vsaRecords.clear();
|
||||
return true;
|
||||
}
|
||||
Reference in New Issue
Block a user