Keep vanetza-idf in obu-firmware, so a plain clone builds the firmware

obu-firmware builds against the vanetza-idf C-ITS library, which until now
came from the colleague's microbu-esp32c5 tree beside the repository and was
not tracked here, so a clone of this repository could not build the firmware
it ships. The library alone is now part of obu-firmware, as
obu-firmware/external/vanetza-idf: their external/vanetza-idf at commit
cf4b99f, unchanged (9775 files; see its PROVENANCE.md). CMake takes it from
there by default; -DVANETZA_IDF_DIR still points the build elsewhere.

The rest of the colleague's tree (their own VAM firmware, PKI tooling,
station-link Python tools, the V2X2MAP bridge) stays out of this repository
and gitignored; nothing is pushed to their repository. NOTES.md, docs/06,
TODO.md and the pcap verifier's usage line point at the new location.
This commit is contained in:
Ashin Walpola
2026-09-24 10:56:05 +02:00
parent 2f60623e18
commit d107534eb2
9781 changed files with 1560475 additions and 17 deletions
@@ -0,0 +1,12 @@
set(CXX_SOURCES
data_indication.cpp
data_request.cpp
header.cpp
port_dispatcher.cpp
)
add_vanetza_component(btp ${CXX_SOURCES})
target_link_libraries(btp PUBLIC Boost::boost geonet)
add_test_subdirectory(tests)
@@ -0,0 +1,39 @@
#include "data_indication.hpp"
#include "header.hpp"
#include <vanetza/geonet/data_indication.hpp>
namespace vanetza
{
namespace btp
{
DataIndication::DataIndication()
{
}
DataIndication::DataIndication(const geonet::DataIndication& ind, const HeaderA& btp) :
source_port(btp.source_port),
destination_port(btp.destination_port),
destination(ind.destination),
its_aid(ind.its_aid),
permissions(ind.permissions),
source_position(ind.source_position),
traffic_class(ind.traffic_class),
remaining_packet_lifetime(ind.remaining_packet_lifetime)
{
}
DataIndication::DataIndication(const geonet::DataIndication& ind, const HeaderB& btp) :
destination_port(btp.destination_port),
destination_port_info(btp.destination_port_info),
destination(ind.destination),
its_aid(ind.its_aid),
permissions(ind.permissions),
source_position(ind.source_position),
traffic_class(ind.traffic_class),
remaining_packet_lifetime(ind.remaining_packet_lifetime)
{
}
} // namespace btp
} // namespace vanetza
@@ -0,0 +1,39 @@
#ifndef DATA_INDICATION_HPP_5YZM172D
#define DATA_INDICATION_HPP_5YZM172D
#include <vanetza/btp/header.hpp>
#include <vanetza/geonet/address.hpp>
#include <vanetza/geonet/areas.hpp>
#include <vanetza/geonet/data_indication.hpp>
#include <vanetza/geonet/lifetime.hpp>
#include <vanetza/geonet/position_vector.hpp>
#include <vanetza/geonet/traffic_class.hpp>
#include <boost/optional.hpp>
#include <boost/variant.hpp>
namespace vanetza
{
namespace btp
{
struct DataIndication
{
DataIndication();
DataIndication(const geonet::DataIndication&, const HeaderA&);
DataIndication(const geonet::DataIndication&, const HeaderB&);
boost::optional<port_type> source_port;
port_type destination_port;
boost::optional<decltype(HeaderB::destination_port_info)> destination_port_info;
decltype(geonet::DataIndication::destination) destination;
decltype(geonet::DataIndication::its_aid) its_aid;
decltype(geonet::DataIndication::permissions) permissions;
geonet::ShortPositionVector source_position;
geonet::TrafficClass traffic_class;
boost::optional<geonet::Lifetime> remaining_packet_lifetime;
};
} // namespace btp
} // namespace vanetza
#endif /* DATA_INDICATION_HPP_5YZM172D */
@@ -0,0 +1,33 @@
#ifndef DATA_INTERFACE_HPP_HKI9B5SC
#define DATA_INTERFACE_HPP_HKI9B5SC
#include <vanetza/btp/data_indication.hpp>
#include <vanetza/btp/data_request.hpp>
#include <vanetza/common/byte_order.hpp>
#include <vanetza/net/packet.hpp>
#include <memory>
namespace vanetza
{
namespace btp
{
class IndicationInterface
{
public:
virtual void indicate(const DataIndication&, std::unique_ptr<UpPacket>) = 0;
virtual ~IndicationInterface() {};
};
class RequestInterface
{
public:
virtual void request(const DataRequestB&, std::unique_ptr<DownPacket>) = 0;
virtual ~RequestInterface() {};
};
} // namespace btp
} // namespace vanetza
#endif /* DATA_INTERFACE_HPP_HKI9B5SC */
@@ -0,0 +1,21 @@
#include <vanetza/btp/data_request.hpp>
namespace vanetza
{
namespace btp
{
DataRequestA::DataRequestA() :
destination_port(0),
source_port(0)
{
}
DataRequestB::DataRequestB() :
destination_port(0),
destination_port_info(0)
{
}
} // namespace btp
} // namespace vanetza
@@ -0,0 +1,50 @@
#ifndef DATA_REQUEST_HPP_BSJC1VFV
#define DATA_REQUEST_HPP_BSJC1VFV
#include <vanetza/btp/header.hpp>
#include <vanetza/common/its_aid.hpp>
#include <vanetza/geonet/data_request.hpp>
#include <vanetza/geonet/destination_variant.hpp>
#include <vanetza/geonet/interface.hpp>
#include <vanetza/geonet/lifetime.hpp>
#include <vanetza/geonet/traffic_class.hpp>
namespace vanetza
{
namespace btp
{
struct DataRequestGeoNetParams
{
geonet::TransportType transport_type;
geonet::DestinationVariant destination;
geonet::CommunicationProfile communication_profile;
ItsAid its_aid;
boost::optional<geonet::Lifetime> maximum_lifetime;
boost::optional<unsigned> maximum_hop_limit;
boost::optional<geonet::DataRequest::Repetition> repetition;
geonet::TrafficClass traffic_class;
};
struct DataRequestA
{
DataRequestA();
decltype(HeaderA::destination_port) destination_port;
decltype(HeaderA::source_port) source_port;
DataRequestGeoNetParams gn;
};
struct DataRequestB
{
DataRequestB();
decltype(HeaderB::destination_port) destination_port;
decltype(HeaderB::destination_port_info) destination_port_info;
DataRequestGeoNetParams gn;
};
} // namespace btp
} // namespace vanetza
#endif /* DATA_REQUEST_HPP_BSJC1VFV */
+109
View File
@@ -0,0 +1,109 @@
#include "header.hpp"
#include <vanetza/common/serialization.hpp>
#include <vanetza/common/serialization_buffer.hpp>
namespace vanetza
{
namespace btp
{
using vanetza::serialize;
using vanetza::deserialize;
constexpr std::size_t HeaderA::length_bytes;
constexpr std::size_t HeaderB::length_bytes;
void serialize(OutputArchive& ar, const HeaderA& hdr)
{
serialize(ar, hdr.destination_port);
serialize(ar, hdr.source_port);
}
void deserialize(InputArchive& ar, HeaderA& hdr)
{
deserialize(ar, hdr.destination_port);
deserialize(ar, hdr.source_port);
}
void serialize(OutputArchive& ar, const HeaderB& hdr)
{
serialize(ar, hdr.destination_port);
serialize(ar, hdr.destination_port_info);
}
void deserialize(InputArchive& ar, HeaderB& hdr)
{
deserialize(ar, hdr.destination_port);
deserialize(ar, hdr.destination_port_info);
}
HeaderA parse_btp_a(CohesivePacket& packet)
{
HeaderA hdr;
deserialize_from_range(hdr, packet[OsiLayer::Transport]);
packet.set_boundary(OsiLayer::Transport, btp::HeaderA::length_bytes);
return hdr;
}
HeaderA parse_btp_a(ChunkPacket& packet)
{
HeaderA hdr;
ByteBuffer tmp;
packet[OsiLayer::Transport].convert(tmp);
deserialize_from_buffer(hdr, tmp);
return hdr;
}
HeaderA parse_btp_a(PacketVariant& packet)
{
struct parse_btp_visitor : public boost::static_visitor<HeaderA>
{
HeaderA operator()(CohesivePacket& packet) {
return parse_btp_a(packet);
}
HeaderA operator()(ChunkPacket& packet) {
return parse_btp_a(packet);
}
};
parse_btp_visitor visitor;
return boost::apply_visitor(visitor, packet);
}
HeaderB parse_btp_b(CohesivePacket& packet)
{
HeaderB hdr;
deserialize_from_range(hdr, packet[OsiLayer::Transport]);
packet.set_boundary(OsiLayer::Transport, btp::HeaderB::length_bytes);
return hdr;
}
HeaderB parse_btp_b(ChunkPacket& packet)
{
HeaderB hdr;
ByteBuffer tmp;
packet[OsiLayer::Transport].convert(tmp);
deserialize_from_buffer(hdr, tmp);
return hdr;
}
HeaderB parse_btp_b(PacketVariant& packet)
{
struct parse_btp_visitor : public boost::static_visitor<HeaderB>
{
HeaderB operator()(CohesivePacket& packet) {
return parse_btp_b(packet);
}
HeaderB operator()(ChunkPacket& packet) {
return parse_btp_b(packet);
}
};
parse_btp_visitor visitor;
return boost::apply_visitor(visitor, packet);
}
} // namespace btp
} // namespace vanetza
@@ -0,0 +1,53 @@
#ifndef HEADER_HPP_FNKGEM7C
#define HEADER_HPP_FNKGEM7C
#include <vanetza/common/byte_order.hpp>
#include <vanetza/common/serialization.hpp>
#include <vanetza/net/packet_variant.hpp>
#include <cstdint>
namespace vanetza
{
namespace btp
{
typedef uint16be_t port_type;
// interactive packet transport
struct HeaderA
{
static constexpr std::size_t length_bytes = 4;
port_type destination_port;
port_type source_port;
};
static_assert(sizeof(HeaderA) == HeaderA::length_bytes, "Wrong size");
void serialize(OutputArchive&, const HeaderA&);
void deserialize(InputArchive&, HeaderA&);
HeaderA parse_btp_a(ChunkPacket&);
HeaderA parse_btp_a(CohesivePacket&);
HeaderA parse_btp_a(PacketVariant&);
// non-interactive packet transport
struct HeaderB
{
static constexpr std::size_t length_bytes = 4;
port_type destination_port;
uint16be_t destination_port_info;
};
static_assert(sizeof(HeaderB) == HeaderB::length_bytes, "Wrong size");
void serialize(OutputArchive&, const HeaderB&);
void deserialize(InputArchive&, HeaderB&);
HeaderB parse_btp_b(ChunkPacket&);
HeaderB parse_btp_b(CohesivePacket&);
HeaderB parse_btp_b(PacketVariant&);
} // namepsace btp
} // namespace vanetza
#endif /* HEADER_HPP_FNKGEM7C */
@@ -0,0 +1,44 @@
#ifndef HEADER_CONVERSION_HPP_SQVEUMFE
#define HEADER_CONVERSION_HPP_SQVEUMFE
#include <vanetza/btp/header.hpp>
#include <vanetza/common/byte_buffer_convertible.hpp>
#include <vanetza/common/serialization_buffer.hpp>
namespace vanetza
{
namespace convertible
{
template<>
struct byte_buffer_impl<btp::HeaderA> : public byte_buffer
{
byte_buffer_impl(const btp::HeaderA& header) : m_header(header) {}
void convert(ByteBuffer& buffer) const override
{
buffer.clear();
serialize_into_buffer(m_header, buffer);
}
std::size_t size() const override { return btp::HeaderA::length_bytes; }
const btp::HeaderA m_header;
};
template<>
struct byte_buffer_impl<btp::HeaderB> : public byte_buffer
{
byte_buffer_impl(const btp::HeaderB& header) : m_header(header) {}
void convert(ByteBuffer& buffer) const override
{
buffer.clear();
serialize_into_buffer(m_header, buffer);
}
std::size_t size() const override { return btp::HeaderB::length_bytes; }
const btp::HeaderB m_header;
};
} // namespace convertible
} // namespace vanetza
#endif /* HEADER_CONVERSION_HPP_SQVEUMFE */
@@ -0,0 +1,94 @@
#include "port_dispatcher.hpp"
#include "data_indication.hpp"
#include <vanetza/geonet/data_indication.hpp>
#include <algorithm>
#include <cassert>
namespace vanetza
{
namespace btp
{
boost::optional<DataIndication> parse_btp_header(const geonet::DataIndication& gn_ind, PacketVariant& packet)
{
boost::optional<DataIndication> indication;
switch (gn_ind.upper_protocol) {
case geonet::UpperProtocol::BTP_A: {
HeaderA hdr = parse_btp_a(packet);
indication = DataIndication(gn_ind, hdr);
}
break;
case geonet::UpperProtocol::BTP_B: {
HeaderB hdr = parse_btp_b(packet);
indication = DataIndication(gn_ind, hdr);
}
break;
default:
// drop non-BTP packet
break;
}
return indication;
}
void PortDispatcher::add_promiscuous_hook(PromiscuousHook* hook)
{
if (hook != nullptr) {
auto it = std::find(m_promiscuous_hooks.begin(), m_promiscuous_hooks.end(), hook);
if (it == m_promiscuous_hooks.end()) {
m_promiscuous_hooks.push_back(hook);
}
}
}
void PortDispatcher::remove_promiscuous_hook(PromiscuousHook* hook)
{
m_promiscuous_hooks.remove(hook);
}
void PortDispatcher::set_interactive_handler(
port_type port,
IndicationInterface* handler)
{
m_interactive_handlers[port] = handler;
}
void PortDispatcher::set_non_interactive_handler(
port_type port,
IndicationInterface* handler)
{
m_non_interactive_handlers[port] = handler;
}
void PortDispatcher::indicate(
const geonet::DataIndication& gn_ind,
std::unique_ptr<UpPacket> packet)
{
assert(packet);
boost::optional<DataIndication> btp_ind = parse_btp_header(gn_ind, *packet);
IndicationInterface* handler = nullptr;
if (btp_ind) {
if (btp_ind->source_port) {
handler = m_interactive_handlers[btp_ind->destination_port];
} else {
handler = m_non_interactive_handlers[btp_ind->destination_port];
}
for (PromiscuousHook* hook : m_promiscuous_hooks) {
hook->tap_packet(*btp_ind, *packet);
}
if (handler) {
handler->indicate(*btp_ind, std::move(packet));
} else {
hook_undispatched(gn_ind, &btp_ind.get());
}
} else {
hook_undispatched(gn_ind, nullptr);
}
}
} // namespace btp
} // namespace vanetza
@@ -0,0 +1,86 @@
#ifndef PORT_DISPATCHER_HPP_YZ0UTAUF
#define PORT_DISPATCHER_HPP_YZ0UTAUF
#include <vanetza/btp/data_interface.hpp>
#include <vanetza/btp/header.hpp>
#include <vanetza/common/hook.hpp>
#include <vanetza/geonet/transport_interface.hpp>
#include <vanetza/net/packet.hpp>
#include <list>
#include <unordered_map>
namespace vanetza
{
namespace btp
{
class PortDispatcher : public geonet::TransportInterface
{
public:
class PromiscuousHook
{
public:
/**
* Called when a BTP packet is received, regardless of its destination port
* \param indication BTP data indication
* \param packet Read-only view of packet
*/
virtual void tap_packet(const DataIndication&, const UpPacket&) = 0;
virtual ~PromiscuousHook() {}
};
/**
* Register a handler for incoming BTP-A (interactive) packets
* \param port BTP-A destination port
* \param interface Handler for given destination port
* \note Use a nullptr as handler to disable dispatching of destination port
* \note Only one handler can be registered per port
*/
void set_interactive_handler(port_type, IndicationInterface*);
/**
* Register a handler for incoming BTP-B (non-interactive) packets
* \param port BTP-B destination port
* \param interface Handler for given destination port
* \note Use a nullptr as handler to disable dispatching of destination port
* \note Only one handler can be registered per port
*/
void set_non_interactive_handler(port_type, IndicationInterface*);
/**
* Add a hook listening for all incoming BTP packets
* \param hook A hook implementing the promiscuous hook interface
* \note Each registered hook is invoked only once per indication
*/
void add_promiscuous_hook(PromiscuousHook* hook);
/**
* Remove promiscuous hook
* \param hook pointer to previously added hook
*/
void remove_promiscuous_hook(PromiscuousHook*);
// Implementation of geonet::TransportInterface
void indicate(const geonet::DataIndication&, std::unique_ptr<UpPacket>) override;
/**
* Hook invoked for all indicated but undispatched packets
* \note BTP data indication can be nullptr
*/
Hook<const geonet::DataIndication&, const btp::DataIndication*> hook_undispatched;
private:
typedef std::unordered_map<port_type, IndicationInterface*> port_map;
typedef std::list<PromiscuousHook*> hook_list;
port_map m_interactive_handlers;
port_map m_non_interactive_handlers;
hook_list m_promiscuous_hooks;
};
} // namespace btp
} // namespace vanetza
#endif /* PORT_DISPATCHER_HPP_YZ0UTAUF */
+55
View File
@@ -0,0 +1,55 @@
#ifndef PORTS_HPP_T2IEFSSC
#define PORTS_HPP_T2IEFSSC
#include <vanetza/common/byte_order.hpp>
namespace vanetza
{
namespace btp
{
typedef uint16be_t port_type;
namespace ports
{
// Port numbers according to ETSI TS 103 248 v2.4.1 (2024-07)
static const port_type CAM = host_cast<uint16_t>(2001);
static const port_type DENM = host_cast<uint16_t>(2002);
static const port_type TOPO = host_cast<uint16_t>(2003);
static const port_type SPAT = host_cast<uint16_t>(2004);
static const port_type SAM = host_cast<uint16_t>(2005);
static const port_type IVIM = host_cast<uint16_t>(2006);
static const port_type SREM = host_cast<uint16_t>(2007);
static const port_type SSEM = host_cast<uint16_t>(2008);
static const port_type CPM = host_cast<uint16_t>(2009);
static const port_type EVCSN_POI = host_cast<uint16_t>(2010);
static const port_type TRM = host_cast<uint16_t>(2011);
static const port_type TCM = host_cast<uint16_t>(2011);
static const port_type VDRM = host_cast<uint16_t>(2011);
static const port_type VDPM = host_cast<uint16_t>(2011);
static const port_type EOFM = host_cast<uint16_t>(2011);
static const port_type EV_RSR = host_cast<uint16_t>(2012);
static const port_type RTCMEM = host_cast<uint16_t>(2013);
static const port_type CTLM = host_cast<uint16_t>(2014);
static const port_type CRLM = host_cast<uint16_t>(2015);
static const port_type EC_AT_REQUEST = host_cast<uint16_t>(2016);
static const port_type MCDM = host_cast<uint16_t>(2017);
static const port_type VAM = host_cast<uint16_t>(2018);
static const port_type IMZM = host_cast<uint16_t>(2019);
static const port_type DSM = host_cast<uint16_t>(2020);
static const port_type P2P_CRL = host_cast<uint16_t>(2021);
static const port_type P2P_DCTL = host_cast<uint16_t>(2022);
static const port_type MRM = host_cast<uint16_t>(2023);
static const port_type P2P_FCTL = host_cast<uint16_t>(2024);
static const port_type POIM_PA = host_cast<uint16_t>(2025);
static const port_type MIM = host_cast<uint16_t>(2026);
static const port_type MVM = host_cast<uint16_t>(2026);
} // namespace ports
} // namespace btp
} // namespace vanetza
#endif /* PORTS_HPP_T2IEFSSC */
@@ -0,0 +1,6 @@
include(UseGTest)
configure_gtest_directory(LINK_LIBRARIES btp)
add_gtest(BtpDataIndication data_indication.cpp)
add_gtest(BtpHeader header.cpp)
add_gtest(BtpPortDispatcher port_dispatcher.cpp)
@@ -0,0 +1,34 @@
#include <gtest/gtest.h>
#include <vanetza/btp/data_indication.hpp>
#include <vanetza/btp/header.hpp>
#include <vanetza/geonet/data_indication.hpp>
#include <boost/optional/optional_io.hpp>
using namespace vanetza;
TEST(BtpDataIndication, construct_from_header_b) {
geonet::DataIndication gn_ind;
gn_ind.transport_type = geonet::TransportType::GUC;
gn_ind.destination = geonet::Address { MacAddress { 1, 2, 3, 4, 5, 6 }};
gn_ind.traffic_class.store_carry_forward(true);
gn_ind.source_position.longitude =
static_cast<geonet::geo_angle_i32t>(43.8 * units::degree);
gn_ind.remaining_packet_lifetime =
geonet::Lifetime(geonet::Lifetime::Base::Ten_Seconds, 3);
btp::HeaderB hdr;
hdr.destination_port = host_cast<uint16_t>(0x1816);
hdr.destination_port_info = host_cast<uint16_t>(0x4711);;
btp::DataIndication btp_ind(gn_ind, hdr);
EXPECT_FALSE(btp_ind.source_port);
EXPECT_EQ(hdr.destination_port, btp_ind.destination_port);
EXPECT_EQ(hdr.destination_port_info, btp_ind.destination_port_info);
EXPECT_EQ(boost::get<geonet::Address>(gn_ind.destination),
boost::get<geonet::Address>(btp_ind.destination));
EXPECT_EQ(gn_ind.source_position, btp_ind.source_position);
EXPECT_EQ(gn_ind.traffic_class.raw(), btp_ind.traffic_class.raw());
ASSERT_TRUE(!!btp_ind.remaining_packet_lifetime);
EXPECT_EQ(gn_ind.remaining_packet_lifetime->raw(),
btp_ind.remaining_packet_lifetime->raw());
}
@@ -0,0 +1,39 @@
#include <gtest/gtest.h>
#include <vanetza/btp/header.hpp>
#include <vanetza/common/serialization_buffer.hpp>
using namespace vanetza;
const ByteBuffer ref = { 0x38, 0x84, 0x01, 0x40 };
TEST(BtpHeaderA, serialize) {
btp::HeaderA header;
header.destination_port = host_cast<uint16_t>(0x3884);
header.source_port = host_cast<uint16_t>(0x0140);
ByteBuffer buf;
serialize_into_buffer(header, buf);
EXPECT_EQ(ref, buf);
}
TEST(BtpHeaderA, deserialize) {
btp::HeaderA header;
deserialize_from_buffer(header, ref);
EXPECT_EQ(host_cast<uint16_t>(0x3884), header.destination_port);
EXPECT_EQ(host_cast<uint16_t>(0x0140), header.source_port);
}
TEST(BtpHeaderB, serialize) {
btp::HeaderB header;
header.destination_port = host_cast<uint16_t>(0x3884);
header.destination_port_info = host_cast<uint16_t>(0x0140);
ByteBuffer buf;
serialize_into_buffer(header, buf);
EXPECT_EQ(ref, buf);
}
TEST(BtpHeaderB, deserialize) {
btp::HeaderB header;
deserialize_from_buffer(header, ref);
EXPECT_EQ(host_cast<uint16_t>(0x3884), header.destination_port);
EXPECT_EQ(host_cast<uint16_t>(0x0140), header.destination_port_info);
}
@@ -0,0 +1,213 @@
#include <gtest/gtest.h>
#include <vanetza/btp/header_conversion.hpp>
#include <vanetza/btp/port_dispatcher.hpp>
#include <vanetza/btp/ports.hpp>
#include <vanetza/geonet/data_indication.hpp>
#include <vanetza/net/osi_layer.hpp>
#include <vanetza/net/packet.hpp>
using namespace vanetza;
using namespace vanetza::btp;
using vanetza::geonet::UpperProtocol;
class CounterIndicationInterface : public IndicationInterface
{
public:
void indicate(const DataIndication&, std::unique_ptr<UpPacket>) override
{
++counter;
}
unsigned counter = 0;
};
class CounterPromiscuousHook : public PortDispatcher::PromiscuousHook
{
public:
void tap_packet(const DataIndication&, const UpPacket&) override
{
++counter;
}
unsigned counter = 0;
};
class BtpPortDispatcherTest : public ::testing::Test
{
protected:
void SetUp() override
{
undispatched_counter = 0;
dispatcher.hook_undispatched =
[this](const geonet::DataIndication&, const DataIndication*)
{
++undispatched_counter;
};
}
std::unique_ptr<UpPacket> create_btp_packet(port_type destination);
std::unique_ptr<UpPacket> create_btp_packet(port_type destination, port_type source);
geonet::DataIndication create_gn_indication(UpperProtocol protocol);
using port_native = port_type::value_type;
const port_native port_min = std::numeric_limits<port_native>::min();
const port_native port_max = std::numeric_limits<port_native>::max();
PortDispatcher dispatcher;
std::size_t undispatched_counter;
};
TEST_F(BtpPortDispatcherTest, empty) {
std::size_t indication_counter = 0;
const auto port_range = port_max - port_min;
// create list of port numbers to test
std::list<port_native> ports;
ports.push_back(port_min - 1);
ports.push_back(port_min);
ports.push_back(port_min + 1);
ports.push_back(port_min + 0.2 * port_range);
ports.push_back(port_min + 0.3 * port_range);
ports.push_back(port_min + 0.5 * port_range);
ports.push_back(port_min + 0.9 * port_range);
ports.push_back(port_max - 1);
ports.push_back(port_max);
ports.push_back(port_max + 1);
for (port_native p : ports) {
dispatcher.indicate(
create_gn_indication(UpperProtocol::BTP_B),
create_btp_packet(host_cast(p))
);
EXPECT_EQ(undispatched_counter, ++indication_counter);
}
}
TEST_F(BtpPortDispatcherTest, interactive) {
ASSERT_EQ(undispatched_counter, 0);
CounterIndicationInterface handler;
const port_type dst_port { host_cast<uint16_t>(0x1234) };
const port_type src_port { host_cast<uint16_t>(0x1337) };
dispatcher.set_interactive_handler(dst_port, &handler);
dispatcher.indicate(create_gn_indication(UpperProtocol::BTP_A), create_btp_packet(dst_port, src_port));
EXPECT_EQ(1, handler.counter);
dispatcher.indicate(create_gn_indication(UpperProtocol::BTP_B), create_btp_packet(dst_port));
EXPECT_EQ(1, handler.counter);
}
TEST_F(BtpPortDispatcherTest, non_interactive) {
ASSERT_EQ(undispatched_counter, 0);
CounterIndicationInterface cam;
CounterIndicationInterface denm;
dispatcher.set_non_interactive_handler(ports::CAM, &cam);
dispatcher.set_non_interactive_handler(ports::DENM, &denm);
const geonet::DataIndication indication_this = create_gn_indication(UpperProtocol::BTP_B);
const geonet::DataIndication indication_other = create_gn_indication(UpperProtocol::BTP_A);
dispatcher.indicate(indication_other, create_btp_packet(ports::CAM));
EXPECT_EQ(1, undispatched_counter);
EXPECT_EQ(0, cam.counter);
EXPECT_EQ(0, denm.counter);
dispatcher.indicate(indication_this, create_btp_packet(ports::CAM));
EXPECT_EQ(1, undispatched_counter);
EXPECT_EQ(1, cam.counter);
EXPECT_EQ(0, denm.counter);
dispatcher.indicate(indication_this, create_btp_packet(ports::DENM));
EXPECT_EQ(1, undispatched_counter);
EXPECT_EQ(1, cam.counter);
EXPECT_EQ(1, denm.counter);
dispatcher.indicate(indication_this, create_btp_packet(ports::SPAT));
EXPECT_EQ(2, undispatched_counter);
EXPECT_EQ(1, cam.counter);
EXPECT_EQ(1, denm.counter);
}
TEST_F(BtpPortDispatcherTest, non_interactive_overwrite) {
ASSERT_EQ(undispatched_counter, 0);
CounterIndicationInterface cam1_interface;
CounterIndicationInterface cam2_interface;
const auto indication = create_gn_indication(UpperProtocol::BTP_B);
dispatcher.set_non_interactive_handler(ports::CAM, &cam1_interface);
dispatcher.indicate(indication, create_btp_packet(ports::CAM));
EXPECT_EQ(1, cam1_interface.counter);
EXPECT_EQ(0, cam2_interface.counter);
dispatcher.set_non_interactive_handler(ports::CAM, &cam2_interface);
dispatcher.indicate(indication, create_btp_packet(ports::CAM));
EXPECT_EQ(1, cam1_interface.counter);
EXPECT_EQ(1, cam2_interface.counter);
dispatcher.set_non_interactive_handler(ports::CAM, nullptr);
dispatcher.indicate(indication, create_btp_packet(ports::CAM));
EXPECT_EQ(1, cam2_interface.counter);
EXPECT_EQ(1, undispatched_counter);
}
TEST_F(BtpPortDispatcherTest, promiscuous_hook) {
CounterPromiscuousHook hook;
const auto ind = create_gn_indication(UpperProtocol::BTP_B);
dispatcher.indicate(ind, create_btp_packet(ports::CAM));
EXPECT_EQ(0, hook.counter);
dispatcher.add_promiscuous_hook(&hook);
dispatcher.indicate(ind, create_btp_packet(ports::CAM));
EXPECT_EQ(1, hook.counter);
dispatcher.indicate(ind, create_btp_packet(ports::DENM));
EXPECT_EQ(2, hook.counter);
CounterIndicationInterface cam;
dispatcher.set_non_interactive_handler(ports::CAM, &cam);
dispatcher.indicate(ind, create_btp_packet(ports::CAM));
EXPECT_EQ(1, cam.counter);
EXPECT_EQ(3, hook.counter);
dispatcher.add_promiscuous_hook(&hook);
dispatcher.indicate(ind, create_btp_packet(ports::TOPO));
EXPECT_EQ(4, hook.counter);
}
TEST_F(BtpPortDispatcherTest, remove_promiscuous_hook) {
CounterPromiscuousHook hook;
const auto ind = create_gn_indication(UpperProtocol::BTP_B);
dispatcher.add_promiscuous_hook(&hook);
dispatcher.indicate(ind, create_btp_packet(ports::CAM));
EXPECT_EQ(1, hook.counter);
dispatcher.remove_promiscuous_hook(&hook);
dispatcher.indicate(ind, create_btp_packet(ports::CAM));
EXPECT_EQ(1, hook.counter);
}
std::unique_ptr<UpPacket> BtpPortDispatcherTest::create_btp_packet(port_type destination)
{
ChunkPacket packet;
HeaderB header;
header.destination_port = destination;
packet[OsiLayer::Transport] = header;
return std::unique_ptr<UpPacket> { new UpPacket(packet) };
}
std::unique_ptr<UpPacket> BtpPortDispatcherTest::create_btp_packet(port_type destination, port_type source)
{
ChunkPacket packet;
HeaderA header;
header.destination_port = destination;
header.source_port = source;
packet[OsiLayer::Transport] = header;
return std::unique_ptr<UpPacket> { new UpPacket(packet) };
}
geonet::DataIndication BtpPortDispatcherTest::create_gn_indication(UpperProtocol protocol)
{
geonet::DataIndication indication;
indication.upper_protocol = protocol;
return indication;
}