Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .oppfeatures
Original file line number Diff line number Diff line change
Expand Up @@ -1171,6 +1171,7 @@
inet.physicallayer.wireless.common.neighborcache
inet.physicallayer.wireless.common.obstacleloss
inet.physicallayer.wireless.common.pathloss
inet.physicallayer.wireless.common.pcap
inet.physicallayer.wireless.common.propagation
inet.physicallayer.wireless.common.radio
inet.physicallayer.wireless.common.signal
Expand Down
65 changes: 65 additions & 0 deletions src/inet/common/packet/recorder/IPcapCaptureAdapter.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
//
// Copyright (C) 2026 OpenSim Ltd.
//
// SPDX-License-Identifier: LGPL-3.0-or-later
//

#ifndef __INET_IPCAPCAPTUREADAPTER_H
#define __INET_IPCAPCAPTUREADAPTER_H

#include <optional>
#include <vector>

#include "inet/common/DirectionTag_m.h"
#include "inet/common/IPrintableObject.h"
#include "inet/common/packet/Packet.h"
#include "inet/common/packet/recorder/IPcapWriter.h"

namespace inet {

class INET_API PcapCaptureObservation
{
public:
const Packet *const packet;
const Direction direction;
const IPrintableObject *const transmission;
const IPrintableObject *const reception;

PcapCaptureObservation(const Packet *packet, Direction direction, const IPrintableObject *transmission = nullptr, const IPrintableObject *reception = nullptr) :
packet(packet), direction(direction), transmission(transmission), reception(reception) {}
};

class INET_API PcapCaptureRecord
{
protected:
std::vector<uint8_t> prefix;

public:
const b frontOffset;
const b backOffset;

PcapCaptureRecord(b frontOffset, b backOffset, std::vector<uint8_t> prefix = {}) :
prefix(std::move(prefix)), frontOffset(frontOffset), backOffset(backOffset) {}

const std::vector<uint8_t>& getPrefix() const { return prefix; }
};

class INET_API IPcapCaptureAdapter
{
public:
virtual ~IPcapCaptureAdapter() {}
virtual PcapLinkType getLinkType() const = 0;
virtual std::optional<std::pair<b, b>> tryResolvePacket(const Packet *, b, b) const { return std::nullopt; }
virtual std::vector<PcapCaptureRecord> createRecords(const PcapCaptureObservation& observation, b frontOffset, b backOffset) const = 0;
};

class INET_API IPcapCaptureObservationAdapter
{
public:
virtual ~IPcapCaptureObservationAdapter() {}
virtual std::optional<PcapCaptureObservation> tryCreateObservation(const cObject *object, Direction direction) const = 0;
};

} // namespace inet

#endif
15 changes: 14 additions & 1 deletion src/inet/common/packet/recorder/IPcapWriter.h
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@
#ifndef __INET_IPCAPWRITER_H
#define __INET_IPCAPWRITER_H

#include <vector>

#include "inet/common/DirectionTag_m.h"
#include "inet/common/packet/Packet.h"
#include "inet/networklayer/common/NetworkInterface.h"
Expand Down Expand Up @@ -212,9 +214,20 @@ class INET_API IPcapWriter
virtual void setFlush(bool flush) = 0;

virtual void writePacket(simtime_t time, const Packet *packet, b frontOffset, b backOffset, Direction direction, NetworkInterface *ie, PcapLinkType linkType) = 0;

/**
* Writes an octet prefix followed by the selected range of the original packet.
* Protocol-specific capture adapters use the prefix as part of the selected link-layer
* record format. The default fails explicitly instead of silently writing a malformed
* prefix-less record. Third-party writers must override this method before adapters are enabled.
*/
virtual void writePacketWithPrefix(simtime_t time, const std::vector<uint8_t>& prefix, const Packet *packet, b frontOffset, b backOffset,
Direction direction, NetworkInterface *ie, PcapLinkType linkType)
{
throw cRuntimeError("This PCAP writer does not support prefixed packet records");
}
};

} // namespace inet

#endif

88 changes: 88 additions & 0 deletions src/inet/common/packet/recorder/PcapCaptureAdapterRegistry.cc
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
//
// Copyright (C) 2026 OpenSim Ltd.
//
// SPDX-License-Identifier: LGPL-3.0-or-later
//

#include "inet/common/packet/recorder/PcapCaptureAdapterRegistry.h"

namespace inet {

PcapCaptureAdapterRegistry::~PcapCaptureAdapterRegistry()
{
for (auto entry : protocolAdapters)
delete entry.second;
for (auto entry : observationAdapters)
delete entry.second;
}

// Each protocol, resolver key, and observation key has one owner per network setup. Silently
// replacing an entry would make capture behavior depend on registration order, so conflicts fail fast.
void PcapCaptureAdapterRegistry::registerProtocolAdapter(const Protocol *protocol, const IPcapCaptureAdapter *adapter)
{
if (protocol == nullptr || adapter == nullptr || protocolAdapters.find(protocol) != protocolAdapters.end()) {
delete adapter;
throw cRuntimeError("Duplicate or invalid PCAP capture protocol adapter registration");
}
protocolAdapters.emplace(protocol, adapter);
}

void PcapCaptureAdapterRegistry::registerProtocolResolver(const Protocol *outerProtocol, const Protocol *captureProtocol)
{
if (outerProtocol == nullptr || captureProtocol == nullptr || protocolResolvers.find(outerProtocol) != protocolResolvers.end())
throw cRuntimeError("Duplicate or invalid PCAP capture protocol resolver registration");
protocolResolvers.emplace(outerProtocol, captureProtocol);
}

void PcapCaptureAdapterRegistry::registerObservationAdapter(const char *key, const IPcapCaptureObservationAdapter *adapter)
{
if (opp_isempty(key) || adapter == nullptr || observationAdapters.find(key) != observationAdapters.end()) {
delete adapter;
throw cRuntimeError("Duplicate or invalid PCAP capture observation adapter registration for '%s'", key == nullptr ? "" : key);
}
observationAdapters.emplace(key, adapter);
}

const IPcapCaptureAdapter *PcapCaptureAdapterRegistry::findProtocolAdapter(const Protocol *protocol) const
{
auto iterator = protocolAdapters.find(protocol);
return iterator == protocolAdapters.end() ? nullptr : iterator->second;
}

std::optional<std::tuple<const Protocol *, b, b>> PcapCaptureAdapterRegistry::tryResolveProtocol(const Protocol *outerProtocol, const Packet *packet, b frontOffset, b backOffset) const
{
auto resolution = tryResolveProtocolWithAdapter(outerProtocol, packet, frontOffset, backOffset);
return resolution.has_value() ? std::optional<std::tuple<const Protocol *, b, b>>({std::get<0>(*resolution), std::get<1>(*resolution), std::get<2>(*resolution)}) : std::nullopt;
}

std::optional<std::tuple<const Protocol *, b, b, const IPcapCaptureAdapter *>> PcapCaptureAdapterRegistry::tryResolveProtocolWithAdapter(const Protocol *outerProtocol, const Packet *packet, b frontOffset, b backOffset) const
{
auto resolver = protocolResolvers.find(outerProtocol);
if (resolver == protocolResolvers.end())
return std::nullopt;
auto adapter = findProtocolAdapter(resolver->second);
if (adapter == nullptr)
return std::nullopt;
auto offsets = adapter->tryResolvePacket(packet, frontOffset, backOffset);
return offsets.has_value() ? std::optional<std::tuple<const Protocol *, b, b, const IPcapCaptureAdapter *>>({resolver->second, offsets->first, offsets->second, adapter}) : std::nullopt;
}

std::optional<PcapCaptureObservation> PcapCaptureAdapterRegistry::tryCreateObservation(const cObject *object, Direction direction) const
{
for (const auto& entry : observationAdapters) {
auto observation = entry.second->tryCreateObservation(object, direction);
if (observation.has_value())
return observation;
}
return std::nullopt;
}

PcapCaptureAdapterRegistry& PcapCaptureAdapterRegistry::getInstance()
{
// SharedDataManager scopes the registry to the current network lifecycle, allowing the
// pre-network registration fragments to run again after the previous network is deleted.
static int handle = cSimulationOrSharedDataManager::registerSharedVariableName("inet::PcapCaptureAdapterRegistry::instance");
return getSimulationOrSharedDataManager()->getSharedVariable<PcapCaptureAdapterRegistry>(handle);
}

} // namespace inet
44 changes: 44 additions & 0 deletions src/inet/common/packet/recorder/PcapCaptureAdapterRegistry.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
//
// Copyright (C) 2026 OpenSim Ltd.
//
// SPDX-License-Identifier: LGPL-3.0-or-later
//

#ifndef __INET_PCAPCAPTUREADAPTERREGISTRY_H
#define __INET_PCAPCAPTUREADAPTERREGISTRY_H

#include <map>
#include <tuple>

#include "inet/common/packet/recorder/IPcapCaptureAdapter.h"

namespace inet {

#define Register_Pcap_Capture_Adapter(PROTOCOL, CLASSNAME) EXECUTE_PRE_NETWORK_SETUP(::inet::PcapCaptureAdapterRegistry::getInstance().registerProtocolAdapter(PROTOCOL, new CLASSNAME()));
#define Register_Pcap_Capture_Protocol_Resolver(OUTER_PROTOCOL, CAPTURE_PROTOCOL) EXECUTE_PRE_NETWORK_SETUP(::inet::PcapCaptureAdapterRegistry::getInstance().registerProtocolResolver(OUTER_PROTOCOL, CAPTURE_PROTOCOL));
#define Register_Pcap_Capture_Observation_Adapter(KEY, CLASSNAME) EXECUTE_PRE_NETWORK_SETUP(::inet::PcapCaptureAdapterRegistry::getInstance().registerObservationAdapter(KEY, new CLASSNAME()));

class INET_API PcapCaptureAdapterRegistry
{
protected:
std::map<const Protocol *, const IPcapCaptureAdapter *> protocolAdapters;
std::map<const Protocol *, const Protocol *> protocolResolvers;
std::map<std::string, const IPcapCaptureObservationAdapter *> observationAdapters;

public:
~PcapCaptureAdapterRegistry();

void registerProtocolAdapter(const Protocol *protocol, const IPcapCaptureAdapter *adapter);
void registerProtocolResolver(const Protocol *outerProtocol, const Protocol *captureProtocol);
void registerObservationAdapter(const char *key, const IPcapCaptureObservationAdapter *adapter);
const IPcapCaptureAdapter *findProtocolAdapter(const Protocol *protocol) const;
std::optional<std::tuple<const Protocol *, b, b>> tryResolveProtocol(const Protocol *outerProtocol, const Packet *packet, b frontOffset, b backOffset) const;
std::optional<std::tuple<const Protocol *, b, b, const IPcapCaptureAdapter *>> tryResolveProtocolWithAdapter(const Protocol *outerProtocol, const Packet *packet, b frontOffset, b backOffset) const;
std::optional<PcapCaptureObservation> tryCreateObservation(const cObject *object, Direction direction) const;

static PcapCaptureAdapterRegistry& getInstance();
};

} // namespace inet

#endif
Loading