[nexus] enhance pcap to support pcapng and multiple interfaces (#12677)

Enhance the Nexus Pcap class to support the pcapng format and logging from
multiple interfaces:

- Transition from pcap to pcapng format to support multiple interface
  descriptions in a single capture file.
- Add support for logging both IEEE 802.15.4 (Thread) and Raw IPv6
  (Backbone) traffic.
- Implement Interface Description Blocks (IDB) and Enhanced Packet
  Blocks (EPB) for pcapng compliance.
This commit is contained in:
Jonathan Hui
2026-03-12 22:36:46 -05:00
committed by GitHub
parent cf733a331e
commit 5e6d1af48f
2 changed files with 70 additions and 12 deletions
+60 -12
View File
@@ -34,6 +34,18 @@
namespace ot {
namespace Nexus {
OT_TOOL_PACKED_BEGIN
struct Epb
{
uint32_t mBlockType;
uint32_t mBlockTotalLength;
uint32_t mInterfaceId;
uint32_t mTimestampHigh;
uint32_t mTimestampLow;
uint32_t mCapturedLen;
uint32_t mOriginalLen;
} OT_TOOL_PACKED_END;
Pcap::Pcap(void)
: mFile(nullptr)
{
@@ -89,6 +101,14 @@ void Pcap::Open(const char *aFilename)
idb.mBlockTotalLength2 = idb.mBlockTotalLength;
VerifyOrExit(fwrite(&idb, sizeof(idb), 1, mFile) == 1, Close());
ClearAllBytes(idb);
idb.mBlockType = LittleEndian::HostSwap(kPcapngIdbType);
idb.mBlockTotalLength = LittleEndian::HostSwap(static_cast<uint32_t>(sizeof(idb)));
idb.mLinkType = LittleEndian::HostSwap(static_cast<uint16_t>(kPcapngLinkTypeIPv6));
idb.mSnapLen = LittleEndian::HostSwap(kPcapngSnapLen);
idb.mBlockTotalLength2 = idb.mBlockTotalLength;
VerifyOrExit(fwrite(&idb, sizeof(idb), 1, mFile) == 1, Close());
VerifyOrExit(fflush(mFile) == 0, Close());
exit:
@@ -108,17 +128,7 @@ exit:
void Pcap::WriteFrame(const otRadioFrame &aFrame, uint64_t aTimeUs)
{
OT_TOOL_PACKED_BEGIN
struct Epb
{
uint32_t mBlockType;
uint32_t mBlockTotalLength;
uint32_t mInterfaceId;
uint32_t mTimestampHigh;
uint32_t mTimestampLow;
uint32_t mCapturedLen;
uint32_t mOriginalLen;
} OT_TOOL_PACKED_END epb;
Epb epb;
OT_TOOL_PACKED_BEGIN
struct TapHeader
@@ -147,7 +157,7 @@ void Pcap::WriteFrame(const otRadioFrame &aFrame, uint64_t aTimeUs)
ClearAllBytes(epb);
epb.mBlockType = LittleEndian::HostSwap(kPcapngEpbType);
epb.mBlockTotalLength = LittleEndian::HostSwap(blockTotalLength);
epb.mInterfaceId = 0;
epb.mInterfaceId = LittleEndian::HostSwap(0u);
epb.mTimestampHigh = LittleEndian::HostSwap(static_cast<uint32_t>(aTimeUs >> 32));
epb.mTimestampLow = LittleEndian::HostSwap(static_cast<uint32_t>(aTimeUs & 0xffffffff));
epb.mCapturedLen = LittleEndian::HostSwap(packetLen);
@@ -191,5 +201,43 @@ exit:
return;
}
void Pcap::WritePacket(const uint8_t *aBuffer, uint16_t aLength, uint64_t aTimeUs)
{
Epb epb;
uint32_t paddedLen;
uint32_t blockTotalLength;
uint32_t padding = 0;
VerifyOrExit(mFile != nullptr);
paddedLen = (aLength + 3) & ~3u;
blockTotalLength = sizeof(epb) + paddedLen + sizeof(uint32_t);
ClearAllBytes(epb);
epb.mBlockType = LittleEndian::HostSwap(kPcapngEpbType);
epb.mBlockTotalLength = LittleEndian::HostSwap(blockTotalLength);
epb.mInterfaceId = LittleEndian::HostSwap(1u); // Use Interface ID 1 for IPv6
epb.mTimestampHigh = LittleEndian::HostSwap(static_cast<uint32_t>(aTimeUs >> 32));
epb.mTimestampLow = LittleEndian::HostSwap(static_cast<uint32_t>(aTimeUs & 0xffffffff));
epb.mCapturedLen = LittleEndian::HostSwap(static_cast<uint32_t>(aLength));
epb.mOriginalLen = LittleEndian::HostSwap(static_cast<uint32_t>(aLength));
VerifyOrExit(fwrite(&epb, sizeof(epb), 1, mFile) == 1, Close());
VerifyOrExit(fwrite(aBuffer, aLength, 1, mFile) == 1, Close());
if (paddedLen > aLength)
{
VerifyOrExit(fwrite(&padding, paddedLen - aLength, 1, mFile) == 1, Close());
}
VerifyOrExit(fwrite(&epb.mBlockTotalLength, sizeof(epb.mBlockTotalLength), 1, mFile) == 1, Close());
VerifyOrExit(fflush(mFile) == 0, Close());
exit:
return;
}
} // namespace Nexus
} // namespace ot
+10
View File
@@ -63,6 +63,15 @@ public:
*/
void WriteFrame(const otRadioFrame &aFrame, uint64_t aTimeUs);
/**
* Writes a packet to the pcapng file.
*
* @param[in] aBuffer The packet buffer to write.
* @param[in] aLength The packet length.
* @param[in] aTimeUs The timestamp in microseconds.
*/
void WritePacket(const uint8_t *aBuffer, uint16_t aLength, uint64_t aTimeUs);
private:
static constexpr uint32_t kPcapngShbType = 0x0a0d0d0a;
static constexpr uint32_t kPcapngByteOrderMagic = 0x1a2b3c4d;
@@ -72,6 +81,7 @@ private:
static constexpr uint32_t kPcapngEpbType = 0x00000006;
static constexpr uint32_t kPcapngSnapLen = 65535;
static constexpr uint32_t kPcapngLinkTypeIeee802154 = 283; // DLT_IEEE802_15_4_TAP
static constexpr uint32_t kPcapngLinkTypeIPv6 = 101; // LINKTYPE_RAW
static constexpr uint8_t kTapVersion = 0;