blob: 860425962bf96f532b3cba7c0cda0d0c5697abfd [file] [log] [blame]
// Copyright 2019 The Fuchsia Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "netdump_parser.h"
#include "netdump_types.h"
#include "src/lib/fxl/logging.h"
#define INSUFF_LEN(a, b) \
if ((a) < (b)) { \
FX_LOGS(ERROR) << "Insufficient length " << (a) << ", need " << (b); \
return false; \
}
namespace netemul {
namespace testing {
bool NetDumpParser::Parse(const uint8_t* data, size_t len) {
interfaces_.clear();
packets_.clear();
INSUFF_LEN(len, sizeof(pcap_shb_t));
pcap_shb_t shb;
memcpy(&shb, data, sizeof(shb));
data += sizeof(shb);
len -= sizeof(shb);
// expect the first block to be a section block
if (shb.magic != kSectionHeaderByteOrderMagic || shb.blk_tot_len != sizeof(shb) ||
shb.blk_tot_len2 != sizeof(shb) || shb.type != kSectionHeaderBlockType) {
FX_LOGS(ERROR) << "Invalid section header block";
return false;
}
// now read all the rest of the blocks:
while (len > 0) {
struct {
uint32_t type;
uint32_t len;
} __attribute__((packed)) block_header{};
INSUFF_LEN(len, sizeof(block_header));
memcpy(&block_header, data, sizeof(block_header));
INSUFF_LEN(len, block_header.len);
// at the end of every block, we repeat the size:
uint32_t len2;
memcpy(&len2, data + block_header.len - sizeof(len2), sizeof(len2));
// end-of-block length doesn't match:
if (len2 != block_header.len) {
FX_LOGS(ERROR) << "Block header and footer don't match lengths";
return false;
}
if (block_header.type == kInterfaceDescriptionBlockType) {
pcap_idb_t idb;
INSUFF_LEN(len, sizeof(idb));
memcpy(&idb, data, sizeof(idb));
// only link type we know:
if (idb.linktype != kLinkTypeEthernet) {
FX_LOGS(ERROR) << "Unrecognized link type: " << idb.linktype;
return false;
}
auto options = data + sizeof(idb);
// length of options is the rest of the block,
// removing the header and the end of block length
uint32_t options_len = idb.blk_tot_len - sizeof(idb) - sizeof(uint32_t);
while (options_len > 0) {
option_tlv_t option_tlv;
INSUFF_LEN(options_len, sizeof(option_tlv));
memcpy(&option_tlv, options, sizeof(option_tlv));
auto padded_len = PAD(static_cast<uint32_t>(option_tlv.len));
INSUFF_LEN(options_len, padded_len);
if (option_tlv.type == kOptionInterfaceName) {
interfaces_.emplace_back(options + sizeof(option_tlv_t),
options + sizeof(option_tlv_t) + option_tlv.len);
} else {
// we don't recognize any other types of options.
FX_LOGS(ERROR) << "Unrecognized interface definition option: " << option_tlv.type;
return false;
}
padded_len += sizeof(option_tlv);
options_len -= padded_len;
options += padded_len;
}
} else if (block_header.type == kEnhancedPacketBlockType) {
INSUFF_LEN(len, sizeof(enhanced_pkt_t));
enhanced_pkt_t pkt;
memcpy(&pkt, data, sizeof(pkt));
if (
// we always put capture and orig len on the same value:
pkt.captured_len != pkt.orig_len
// check that the reported size matches the external block
// information:
|| PAD(pkt.captured_len) + sizeof(enhanced_pkt_t) + sizeof(uint32_t) != block_header.len
// check that the interface is actually specified in interfaces:
|| pkt.interface_id >= interfaces_.size()) {
FX_LOGS(ERROR) << "Invalid enhanced packet block";
return false;
}
packets_.push_back(ParsedPacket{data + sizeof(pkt), pkt.captured_len, pkt.interface_id});
} else {
FX_LOGS(ERROR) << "Unrecognized block type: " << block_header.type;
return false;
}
data += len2;
len -= len2;
}
return true;
}
} // namespace testing
} // namespace netemul