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,31 @@
include(UseGTest)
configure_gtest_directory(LINK_LIBRARIES geonet)
add_gtest(Address address.cpp)
add_gtest(Areas areas.cpp)
add_gtest(BasicHeader basic_header.cpp)
add_gtest(CbfPacketBuffer cbf_packet_buffer.cpp)
add_gtest(CbrAggregator cbr_aggregator.cpp)
add_gtest(CommonHeader common_header.cpp)
add_gtest(DataConfirm data_confirm.cpp)
add_gtest(DataRequest data_request.cpp)
add_gtest(DccMcoField dcc_mco_field.cpp)
add_gtest(DuplicatePacketList duplicate_packet_list.cpp)
add_gtest(GbcGacHeader gbc_gac_header.cpp)
add_gtest(GbcMemory
SOURCES gbc_memory.cpp network_topology.cpp)
add_gtest(Lifetime lifetime.cpp)
add_gtest(LocationTable location_table.cpp)
add_gtest(PacketBuffer packet_buffer.cpp)
add_gtest(PositionUpdater position_updater.cpp)
add_gtest(PositionVector position_vector.cpp)
add_gtest(Repeater repeater.cpp)
add_gtest(Router router.cpp)
add_gtest(RouterIndicate router_indicate.cpp
INCLUDE_DIRECTORIES ${PROJECT_SOURCE_DIR}/vanetza/security/tests)
add_gtest(SequenceNumber sequence_number.cpp)
add_gtest(Timestamp timestamp.cpp)
add_gtest(TrafficClass traffic_class.cpp)
add_gtest(Routing
SOURCES network_topology.cpp routing.cpp)
add_gtest(RouterRequest router_request.cpp)
@@ -0,0 +1,51 @@
#include <gtest/gtest.h>
#include <vanetza/geonet/address.hpp>
#include <vanetza/geonet/serialization_buffer.hpp>
#include <vanetza/net/mac_address.hpp>
using namespace vanetza;
using namespace vanetza::geonet;
TEST(Address, ctor) {
Address a;
EXPECT_FALSE(a.is_manually_configured());
EXPECT_EQ(a.mid(), MacAddress());
EXPECT_EQ(a.station_type(), StationType::Unknown);
EXPECT_EQ(a.country_code(), 0);
}
TEST(Address, equality) {
Address a;
Address b({0x01, 0x02, 0x03, 0x04, 0x05, 0x06});
EXPECT_NE(a, b);
Address c = b;
EXPECT_EQ(b, c);
c.is_manually_configured(true);
EXPECT_NE(b, c);
Address d = c;
EXPECT_EQ(c, d);
d.station_type(StationType::Passenger_Car);
EXPECT_NE(c, d);
Address e = d;
EXPECT_EQ(d, e);
e.country_code(8);
EXPECT_NE(d, e);
a.mid(b.mid());
EXPECT_EQ(a, b);
}
TEST(Address, serialization) {
Address a({1, 2, 3, 4, 5, 6});
a.is_manually_configured(true);
a.station_type(StationType::Tram);
a.country_code(0x0333);
ByteBuffer buffer;
serialize_into_buffer(a, buffer);
EXPECT_EQ(Address::length_bytes, buffer.size());
Address b;
deserialize_from_buffer(b, buffer);
EXPECT_EQ(a, b);
}
@@ -0,0 +1,114 @@
#include <gtest/gtest.h>
#include <vanetza/geonet/areas.hpp>
#include <vanetza/units/length.hpp>
using namespace vanetza::geonet;
namespace units = vanetza::units;
using units::si::meter;
using units::degree;
using units::si::square_meter;
TEST(Areas, cartesian_substraction) {
CartesianPosition a(-3.4 * meter , 8.3 * meter);
CartesianPosition b(34.8 * meter, -14.8 * meter);
CartesianPosition c = a - b;
EXPECT_DOUBLE_EQ(c.x / meter, -38.2);
EXPECT_DOUBLE_EQ(c.y / meter, 23.1);
}
TEST(Areas, geodetic_distance) {
GeodeticPosition a(48.76714 * degree, 11.43263 * degree);
GeodeticPosition b(-25.41272 * degree, -49.24815 * degree);
units::Length d = distance(a, b);
const double expected_m = 10185367.442;
// accept less than 0.5% error for this large distance
EXPECT_NEAR(d / meter, expected_m, 0.005 * expected_m);
EXPECT_DOUBLE_EQ(0.0, distance(a, a).value());
}
TEST(Areas, geometric_function_circle) {
Circle c;
c.r = 38.4 * meter;
CartesianPosition p(0.0 * meter, 0.0 * meter);
EXPECT_TRUE(at_center_point(c, p));
EXPECT_TRUE(inside_shape(c, p));
EXPECT_FALSE(outside_shape(c, p));
EXPECT_FALSE(at_shape_border(c, p));
p.x = 15.0 * meter;
p.y = 36.0 * meter;
EXPECT_TRUE(outside_shape(c, p));
}
TEST(Areas, geometric_function_rectangle) {
Rectangle r;
r.a = 8.5 * meter;
r.b = 3.0 * meter;
CartesianPosition p(-3.5 * meter, 2.9 * meter);
EXPECT_TRUE(inside_shape(r, p));
p.x = -8.6 * meter;
EXPECT_TRUE(outside_shape(r, p));
}
TEST(Areas, geometric_function_ellipse) {
Ellipse e;
e.a = 8.4 * meter;
e.b = 6.5 * meter;
CartesianPosition p(-7.6 * meter, 1.3 * meter);
EXPECT_TRUE(inside_shape(e, p));
p.y = -4.5 * meter;
EXPECT_TRUE(outside_shape(e, p));
}
TEST(Areas, local_cartesian) {
GeodeticPosition origin(48.76714 * degree, 11.43263 * degree); // THI
GeodeticPosition datum(48.7656 * degree, 11.4296 * degree); // ZAF
CartesianPosition pos = local_cartesian(origin, datum);
EXPECT_NEAR(pos.x / meter, -222.74, 1.0);
EXPECT_NEAR(pos.y / meter, -171.25, 1.0);
}
TEST(Areas, canonicalize) {
CartesianPosition point(3.0 * meter, 2.0 * meter);
units::Angle azimuth = units::Angle(30.0 * degree);
CartesianPosition canonical_point = canonicalize(point, azimuth);
EXPECT_NEAR(canonical_point.x / meter, 3.23, 0.01);
EXPECT_NEAR(canonical_point.y / meter, -1.59, 0.01);
}
TEST(Areas, inside_or_at_border) {
Rectangle r;
r.a = 300.0 * meter;
r.b = 170.0 * meter;
Area a;
a.shape = r;
a.angle = units::Angle(90.0 * degree);
a.position = GeodeticPosition(48.7656 * degree, 11.4296 * degree);
GeodeticPosition ego(48.76714 * degree, 11.43263 * degree);
EXPECT_FALSE(inside_or_at_border(a, ego));
a.angle = units::Angle(45.0 * degree);
EXPECT_TRUE(inside_or_at_border(a, ego));
}
TEST(Areas, area_size) {
Circle c;
c.r = 18.3 * meter;
Rectangle r;
r.a = 393.0 * meter;
r.b = 140.8 * meter;
Ellipse e;
e.a = 393.0 * meter;
e.b = 140.8 * meter;
Area a;
a.shape = c;
EXPECT_NEAR(area_size(a) / square_meter, 1052.0880, 0.0001);
a.shape = r;
EXPECT_NEAR(area_size(a) / square_meter, 221337.6000, 0.0001);
a.shape = e;
EXPECT_NEAR(area_size(a) / square_meter, 173838.1445, 0.0001);
}
@@ -0,0 +1,43 @@
#include <gtest/gtest.h>
#include <vanetza/geonet/basic_header.hpp>
#include <vanetza/geonet/data_request.hpp>
#include <vanetza/geonet/tests/serialization.hpp>
using namespace vanetza::geonet;
using vanetza::units::si::seconds;
TEST(BasicHeader, ctor) {
MIB mib;
BasicHeader a(mib);
EXPECT_EQ(a.lifetime, mib.itsGnDefaultPacketLifetime);
EXPECT_EQ(a.hop_limit, mib.itsGnDefaultHopLimit);
DataRequest req(mib);
req.maximum_lifetime.encode(31.0 * seconds);
req.max_hop_limit = 4;
BasicHeader b(req, mib);
EXPECT_EQ(b.lifetime.decode(), 31.0 * seconds);
EXPECT_EQ(b.hop_limit, 4);
ShbDataRequest shb(mib);
BasicHeader c(shb, mib);
EXPECT_EQ(c.hop_limit, 1);
}
TEST(BasicHeader, serialization) {
BasicHeader a;
a.version = 2;
a.next_header = NextHeaderBasic::Secured;
a.reserved = 0xC3;
a.lifetime.raw(0x89);
a.hop_limit = 218;
BasicHeader b = serialize_roundtrip(a);
EXPECT_EQ(a.version, b.version);
EXPECT_EQ(a.next_header, b.next_header);
EXPECT_EQ(a.reserved, b.reserved);
EXPECT_EQ(a.lifetime, b.lifetime);
EXPECT_EQ(a.hop_limit, b.hop_limit);
EXPECT_EQ(BasicHeader::length_bytes, serialize_length(a));
}
@@ -0,0 +1,325 @@
#include <gtest/gtest.h>
#include <vanetza/common/manual_runtime.hpp>
#include <vanetza/geonet/cbf_counter.hpp>
#include <vanetza/geonet/cbf_packet_buffer.hpp>
#include <vanetza/geonet/mib.hpp>
#include <functional>
using namespace std::chrono;
using namespace vanetza;
using namespace vanetza::geonet;
static const size_t GbcPduLength = BasicHeader::length_bytes +
CommonHeader::length_bytes + GeoBroadcastHeader::length_bytes;
class CbfPacketBufferTest : public ::testing::Test
{
protected:
using PduPtr = std::unique_ptr<GbcPdu>;
using PayloadPtr = std::unique_ptr<DownPacket>;
using PendingPacketCbf = PendingPacket<GbcPdu>;
void SetUp() override
{
// lifetime of 3 seconds can be stored with 50 ms accuracy
mib.itsGnDefaultPacketLifetime.encode(3.0 * units::si::seconds);
runtime.reset(Clock::time_point { hours(42) });
calls = 0;
last_call_length = 0;
}
CbfPacket create_packet(const MacAddress&, SequenceNumber::value_type, std::size_t length = GbcPduLength) const;
CbfPacket create_packet(std::size_t length = GbcPduLength) const;
CbfPacketBuffer::TimerCallback callback();
std::unique_ptr<CbfCounter> counter();
MIB mib;
ManualRuntime runtime;
unsigned calls;
unsigned last_call_length;
};
CbfPacket CbfPacketBufferTest::create_packet(const MacAddress& mac, SequenceNumber::value_type sn, std::size_t size) const
{
PduPtr pdu { new CbfPacketBufferTest::PduPtr::element_type(mib) };
pdu->extended().source_position.gn_addr.mid(mac);
pdu->extended().sequence_number = SequenceNumber { sn };
PayloadPtr payload { new CbfPacketBufferTest::PayloadPtr::element_type() };
assert(get_length(*pdu) <= size);
const std::size_t payload_size = size - get_length(*pdu);
payload->layer(OsiLayer::Application) = ByteBuffer(payload_size);
PendingPacketCbf pending { std::make_tuple(std::move(pdu), std::move(payload)), [](PendingPacketCbf::Packet&&) {} };
return CbfPacket(std::move(pending), cBroadcastMacAddress);
}
CbfPacket CbfPacketBufferTest::create_packet(std::size_t length) const
{
static unsigned counter = 0;
return create_packet({0, 0, 0, 0, 0, 0}, ++counter, length);
}
CbfPacketBuffer::TimerCallback CbfPacketBufferTest::callback()
{
return [this](PendingPacketCbf&& data) {
++calls;
last_call_length = data.length();
};
}
std::unique_ptr<CbfCounter> CbfPacketBufferTest::counter()
{
return std::unique_ptr<CbfCounter> { new CbfCounterImmortal() };
}
TEST_F(CbfPacketBufferTest, identifier_hash)
{
std::hash<CbfPacketIdentifier> hasher;
CbfPacketIdentifier id1 { Address {{ 1, 2, 3, 4, 5, 6}}, SequenceNumber(2) };
CbfPacketIdentifier id2 { Address {{ 1, 2, 3, 4, 5, 6}}, SequenceNumber(3) };
CbfPacketIdentifier id3 { Address {{ 1, 2, 3, 4, 5, 6}}, SequenceNumber(2) };
CbfPacketIdentifier id4 { Address {{ 1, 2, 3, 4, 5, 7}}, SequenceNumber(3) };
EXPECT_EQ(id1, id3);
EXPECT_EQ(hasher(id1), hasher(id3));
EXPECT_NE(id1, id2);
EXPECT_NE(hasher(id1), hasher(id2));
EXPECT_NE(id2, id4);
EXPECT_NE(hasher(id2), hasher(id4));
}
TEST_F(CbfPacketBufferTest, packet_identifier)
{
const MacAddress mac { 1, 3, 5, 7, 9, 11 };
CbfPacket packet = create_packet(mac, 8);
EXPECT_EQ(mac, packet.source().mid());
EXPECT_EQ(SequenceNumber { 8 }, packet.sequence_number());
}
TEST_F(CbfPacketBufferTest, packet_lifetime)
{
CbfPacket packet = create_packet({}, 1);
// check initialization
using vanetza::units::clock_cast;
EXPECT_EQ(clock_cast(mib.itsGnDefaultPacketLifetime.decode()), packet.reduce_lifetime(Clock::duration::zero()));
// lifetime has to be modifiable
Clock::duration lifetime = packet.reduce_lifetime(Clock::duration::zero());
EXPECT_EQ(lifetime - milliseconds(50), packet.reduce_lifetime(milliseconds(50)));
// but negative reductions have no effect
EXPECT_EQ(lifetime - milliseconds(50), packet.reduce_lifetime(milliseconds(-100)));
// and lifetime does not go below zero
EXPECT_EQ(Clock::duration::zero(), packet.reduce_lifetime(milliseconds(6000)));
}
TEST_F(CbfPacketBufferTest, packet_length)
{
CbfPacket packet1 = create_packet({0, 1, 2, 3, 4, 5}, 3);
EXPECT_EQ(GbcPduLength, packet1.length());
CbfPacket packet2 = create_packet({0, 1, 2, 3, 4, 5}, 3, 64);
EXPECT_EQ(64, packet2.length());
}
TEST_F(CbfPacketBufferTest, find)
{
CbfPacketBuffer buffer(runtime, callback(), counter(), 8192);
auto found1 = buffer.find(identifier(Address {{1, 2, 3, 4, 5, 6}}, SequenceNumber(3)));
EXPECT_FALSE(found1);
auto packet1 = create_packet({1, 2, 3, 4, 5, 6}, 3);
buffer.add(std::move(packet1), seconds(5));
auto found2 = buffer.find(identifier(Address {{1, 2, 3, 4, 5, 6}}, SequenceNumber(4)));
EXPECT_FALSE(found2);
auto found3 = buffer.find(identifier(Address {{1, 2, 3, 4, 5, 6}}, SequenceNumber(3)));
ASSERT_TRUE(found3);
EXPECT_EQ(1, buffer.counter(identifier(*found3)));
EXPECT_EQ((MacAddress {1, 2, 3, 4, 5, 6}), found3->source().mid());
}
TEST_F(CbfPacketBufferTest, counter)
{
CbfPacket packet1 = create_packet({3, 8, 3, 8, 3, 8}, 10);
CbfPacketIdentifier id1 = identifier(packet1);
CbfPacketBuffer buffer(runtime, callback(), counter(), 8192);
EXPECT_EQ(0, buffer.counter(id1));
buffer.add(std::move(packet1), milliseconds(30));
EXPECT_EQ(1, buffer.counter(id1));
buffer.remove(id1);
EXPECT_EQ(1, buffer.counter(id1));
CbfPacket packet2 = create_packet({3, 8, 3, 8, 3, 8}, 11);
CbfPacketIdentifier id2 = identifier(packet2);
buffer.update(id2, milliseconds(30));
EXPECT_EQ(0, buffer.counter(id2));
buffer.add(std::move(packet2), milliseconds(30));
EXPECT_EQ(1, buffer.counter(id2));
buffer.update(id2, milliseconds(30));
EXPECT_EQ(2, buffer.counter(id2));
EXPECT_EQ(1, buffer.counter(id1));
}
TEST_F(CbfPacketBufferTest, fetch)
{
CbfPacketBuffer buffer(runtime, callback(), counter(), 8192);
auto found1 = buffer.fetch(identifier(Address {{1, 2, 3, 4, 5, 6}}, SequenceNumber(3)));
EXPECT_FALSE(!!found1);
auto packet1 = create_packet({1, 2, 3, 4, 5, 6}, 3);
buffer.add(std::move(packet1), milliseconds(500));
auto found2 = buffer.fetch(identifier(Address {{1, 2, 3, 4, 5, 6}}, SequenceNumber(4)));
EXPECT_FALSE(!!found2);
auto found3 = buffer.fetch(identifier(Address {{1, 2, 3, 4, 5, 6}}, SequenceNumber(3)));
ASSERT_TRUE(!!found3);
EXPECT_EQ((MacAddress {1, 2, 3, 4, 5, 6}), found3->source().mid());
auto found4 = buffer.fetch(identifier(Address {{1, 2, 3, 4, 5, 6}}, SequenceNumber(3)));
EXPECT_FALSE(!!found4);
}
TEST_F(CbfPacketBufferTest, fetch_reduce_lifetime)
{
CbfPacketBuffer buffer(runtime, callback(), counter(), 8192);
auto packet = create_packet({1, 2, 3, 4, 5, 6}, 1);
buffer.add(std::move(packet), milliseconds(500));
runtime.trigger(milliseconds(200));
auto found = buffer.fetch(identifier(Address {{1, 2, 3, 4, 5, 6}}, SequenceNumber(1)));
ASSERT_TRUE(!!found);
EXPECT_EQ(milliseconds(2800), found->reduce_lifetime(Clock::duration::zero()));
}
TEST_F(CbfPacketBufferTest, next_timer_expiry)
{
const Address addr {{1, 2, 3, 4, 5, 6}};
CbfPacketBuffer buffer(runtime, callback(), counter(), 8192);
EXPECT_EQ(Clock::time_point::max(), runtime.next());
buffer.add(create_packet(), seconds(3));
EXPECT_EQ(seconds(3), runtime.next() - runtime.now());
runtime.trigger(seconds(1));
buffer.add(create_packet(addr.mid(), 3), seconds(1));
EXPECT_EQ(seconds(1), runtime.next() - runtime.now());
buffer.add(create_packet(addr.mid(), 2), milliseconds(200));
EXPECT_EQ(milliseconds(200), runtime.next() - runtime.now());
runtime.trigger(milliseconds(100));
auto fetch = buffer.fetch(identifier(addr, SequenceNumber(2)));
EXPECT_TRUE(!!fetch);
EXPECT_EQ(milliseconds(900), runtime.next() - runtime.now());
bool dropped = buffer.remove(identifier(addr, SequenceNumber(3)));
EXPECT_TRUE(dropped);
EXPECT_EQ(milliseconds(1900), runtime.next() - runtime.now());
}
TEST_F(CbfPacketBufferTest, remove_sequence_number)
{
CbfPacketBuffer buffer(runtime, callback(), counter(), 8192);
const auto addr = Address {{1, 1, 1, 1, 1, 1}};
EXPECT_FALSE(buffer.remove(identifier(addr, SequenceNumber(3))));
auto packet = create_packet(addr.mid(), 8);
buffer.add(std::move(packet), milliseconds(400));
EXPECT_FALSE(buffer.remove(identifier(addr, SequenceNumber(7))));
EXPECT_FALSE(buffer.remove(identifier(addr, SequenceNumber(9))));
EXPECT_TRUE(buffer.remove(identifier(addr, SequenceNumber(8))));
EXPECT_FALSE(buffer.remove(identifier(addr, SequenceNumber(8))));
}
TEST_F(CbfPacketBufferTest, remove_drop_addr)
{
CbfPacketBuffer buffer(runtime, callback(), counter(), 8192);
const auto addr1 = Address {{1, 1, 1, 1, 1, 1}};
const auto addr2 = Address {{ 2, 2, 2, 2, 2, 2}};
auto packet = create_packet(addr1.mid(), 8);
buffer.add(std::move(packet), milliseconds(400));
EXPECT_FALSE(buffer.remove(identifier(addr2, SequenceNumber(8))));
EXPECT_TRUE(buffer.remove(identifier(addr1, SequenceNumber(8))));
EXPECT_FALSE(buffer.remove(identifier(addr1, SequenceNumber(8))));
}
TEST_F(CbfPacketBufferTest, remove_multiple_packets)
{
CbfPacketBuffer buffer(runtime, callback(), counter(), 8192);
const auto addr = Address {{1, 1, 1, 1, 1, 1}};
const auto timeout = milliseconds(400);
auto packet1 = create_packet(addr.mid(), 8);
buffer.add(std::move(packet1), timeout);
auto packet2 = create_packet(addr.mid(), 10);
buffer.add(std::move(packet2), timeout);
EXPECT_FALSE(buffer.remove(identifier(addr, SequenceNumber(9))));
EXPECT_TRUE(buffer.remove(identifier(addr, SequenceNumber(10))));
EXPECT_TRUE(buffer.remove(identifier(addr, SequenceNumber(8))));
}
TEST_F(CbfPacketBufferTest, capacity)
{
CbfPacketBuffer buffer(runtime, callback(), counter(), 256);
buffer.add(create_packet(128), seconds(1));
buffer.add(create_packet(128), seconds(1));
runtime.trigger(milliseconds(1010));
EXPECT_EQ(2, calls);
buffer.add(create_packet(157), seconds(1));
buffer.add(create_packet(100), seconds(1));
runtime.trigger(seconds(2));
EXPECT_EQ(3, calls);
EXPECT_EQ(100, last_call_length);
}
TEST_F(CbfPacketBufferTest, packets_to_send)
{
std::vector<PendingPacketCbf> packets;
auto cb = [&packets](PendingPacketCbf&& data) { packets.emplace_back(std::move(data)); };
CbfPacketBuffer buffer(runtime, cb, counter(), 8192);
runtime.trigger(minutes(42));
EXPECT_EQ(0, packets.size());
buffer.add(create_packet(110), milliseconds(2500));
runtime.trigger(seconds(1));
EXPECT_EQ(0, packets.size());
buffer.add(create_packet(120), seconds(1));
runtime.trigger(seconds(1));
ASSERT_EQ(1, packets.size());
EXPECT_EQ(120, packets[0].length());
runtime.trigger(milliseconds(500));
EXPECT_EQ(2, packets.size());
buffer.add(create_packet(130), seconds(1));
buffer.add(create_packet(140), milliseconds(1500));
runtime.trigger(seconds(2));
ASSERT_EQ(4, packets.size());
EXPECT_EQ(130, packets[2].length());
EXPECT_EQ(140, packets[3].length());
// check if lifetime is reduced by queuing time
auto packet = create_packet(150);
EXPECT_EQ(milliseconds(1000), packet.reduce_lifetime(milliseconds(2000)));
buffer.add(std::move(packet), milliseconds(72));
runtime.trigger(runtime.next());
ASSERT_EQ(5, packets.size());
// Lifetime can only be encoded in 50ms steps (in best case): 950 ms remaining lifetime
EXPECT_EQ((Lifetime {Lifetime::Base::Fifty_Milliseconds, 19}), packets[4].pdu().basic().lifetime);
}
@@ -0,0 +1,106 @@
#include <gtest/gtest.h>
#include <vanetza/common/manual_runtime.hpp>
#include <vanetza/geonet/cbr_aggregator.hpp>
#include <vanetza/geonet/location_table.hpp>
#include <vanetza/geonet/loctex_g5.hpp>
#include <vanetza/geonet/mib.hpp>
#include <vanetza/net/mac_address.hpp>
using namespace vanetza;
using namespace vanetza::geonet;
using vanetza::dcc::ChannelLoad;
using std::chrono::seconds;
class CbrAggregatorTest : public ::testing::Test
{
protected:
void SetUp() override
{
mib.reset(new MIB());
runtime.reset(new ManualRuntime(Clock::at("2017-05-20 17:36:00")));
location_table.reset(new LocationTable(*mib, *runtime));
}
void TearDown() override
{
location_table.reset();
runtime.reset();
mib.reset();
}
Timestamp timestamp_earlier(std::chrono::milliseconds ms)
{
Timestamp ts { runtime->now() };
ts -= Timestamp::duration_type { ms.count() * Timestamp::millisecond() };
return ts;
}
Address address(unsigned i)
{
Address addr;
addr.mid(create_mac_address(i));
return addr;
}
std::unique_ptr<LocTEX_G5> loctex_g5(Clock::duration age, double local, double one_hop)
{
std::unique_ptr<LocTEX_G5> entry { new LocTEX_G5() };
entry->local_update = Timestamp { runtime->now() - age };
entry->dcc_mco.local_cbr(ChannelLoad (local));
entry->dcc_mco.neighbour_cbr(ChannelLoad (one_hop));
return entry;
}
std::unique_ptr<Runtime> runtime;
std::unique_ptr<MIB> mib;
std::unique_ptr<LocationTable> location_table;
};
TEST_F(CbrAggregatorTest, init)
{
CbrAggregator cbra;
EXPECT_EQ(ChannelLoad(0.0), cbra.get_local_cbr());
EXPECT_EQ(ChannelLoad(0.0), cbra.get_one_hop_cbr());
EXPECT_EQ(ChannelLoad(0.0), cbra.get_two_hop_cbr());
EXPECT_EQ(ChannelLoad(0.0), cbra.get_global_cbr());
}
TEST_F(CbrAggregatorTest, local_cbr)
{
CbrAggregator cbra;
ChannelLoad cbr_target(0.6);
cbra.aggregate(ChannelLoad(0.1), *location_table, timestamp_earlier(seconds(5)), cbr_target);
EXPECT_EQ(ChannelLoad(0.1), cbra.get_local_cbr());
cbra.aggregate(ChannelLoad(0.2), *location_table, timestamp_earlier(seconds(5)), cbr_target);
EXPECT_EQ(ChannelLoad(0.2), cbra.get_local_cbr());
// no one-hop and two-hop values have been provided: previous local measurement should be maximum
EXPECT_EQ(ChannelLoad(0.1), cbra.get_global_cbr());
}
TEST_F(CbrAggregatorTest, shared_cbr)
{
location_table->get_or_create_entry(address(1)).extensions.insert(loctex_g5(seconds(3), 0.45, 0.5));
location_table->get_or_create_entry(address(2)).extensions.insert(loctex_g5(seconds(2), 0.4, 0.2));
location_table->get_or_create_entry(address(3)).extensions.insert(loctex_g5(seconds(4), 0.4, 0.45));
location_table->get_or_create_entry(address(4)).extensions.insert(loctex_g5(seconds(2), 0.35, 0.25));
CbrAggregator cbra;
cbra.aggregate(ChannelLoad(0.32), *location_table, timestamp_earlier(seconds(10)), ChannelLoad(0.6));
EXPECT_DOUBLE_EQ(0.32, cbra.get_local_cbr().value());
EXPECT_NEAR(0.4, cbra.get_one_hop_cbr().value(), 0.005); // second largest (one hop average = 0.4 < target = 0.6)
EXPECT_NEAR(0.45, cbra.get_two_hop_cbr().value(), 0.005); // second largest (two hop average = 0.35 < target = 0.6)
EXPECT_NEAR(0.45, cbra.get_global_cbr().value(), 0.005);
cbra.aggregate(ChannelLoad(0.34), *location_table, timestamp_earlier(seconds(10)), ChannelLoad(0.3));
EXPECT_DOUBLE_EQ(0.34, cbra.get_local_cbr().value());
EXPECT_NEAR(0.45, cbra.get_one_hop_cbr().value(), 0.005); // largest (two hop average above target)
EXPECT_NEAR(0.5, cbra.get_two_hop_cbr().value(), 0.005); // largest (two hop average above target)
EXPECT_NEAR(0.5, cbra.get_global_cbr().value(), 0.005);
cbra.aggregate(ChannelLoad(0.3), *location_table, timestamp_earlier(seconds(2)), ChannelLoad(0.3));
EXPECT_DOUBLE_EQ(0.3, cbra.get_local_cbr().value());
EXPECT_NEAR(0.4, cbra.get_one_hop_cbr().value(), 0.005); // average (0.375) above target: largest
EXPECT_NEAR(0.2, cbra.get_two_hop_cbr().value(), 0.005); // average (0.225) below target: second largest
EXPECT_NEAR(0.4, cbra.get_global_cbr().value(), 0.005);
}
@@ -0,0 +1,52 @@
#include <gtest/gtest.h>
#include <vanetza/geonet/common_header.hpp>
#include <vanetza/geonet/data_request.hpp>
#include <vanetza/geonet/tests/serialization.hpp>
using namespace vanetza::geonet;
TEST(CommonHeader, ctor) {
MIB mib;
CommonHeader a(mib);
EXPECT_EQ(a.traffic_class.raw(), mib.itsGnDefaultTrafficClass.raw());
EXPECT_EQ(a.maximum_hop_limit, mib.itsGnDefaultHopLimit);
EXPECT_EQ(a.payload, 0);
DataRequest req(mib);
req.upper_protocol = UpperProtocol::BTP_B;
req.max_hop_limit = 3;
req.traffic_class.store_carry_forward(true);
CommonHeader b(req, mib);
EXPECT_EQ(b.next_header, NextHeaderCommon::BTP_B);
EXPECT_EQ(b.maximum_hop_limit, 3);
EXPECT_TRUE(b.traffic_class.store_carry_forward());
ShbDataRequest shb(mib);
CommonHeader c(shb, mib);
EXPECT_EQ(c.header_type, HeaderType::TSB_Single_Hop);
EXPECT_EQ(c.maximum_hop_limit, 1);
}
TEST(CommonHeader, serialization) {
CommonHeader a;
a.next_header = NextHeaderCommon::IPv6;
a.reserved1 = 12;
a.header_type = HeaderType::GeoAnycast_Elip;
a.traffic_class = TrafficClass(0xAB);
a.flags = 0x18;
a.payload = 0x1234;
a.maximum_hop_limit = 0x78;
a.reserved2 = 0x56;
CommonHeader b = serialize_roundtrip(a);
EXPECT_EQ(a.next_header, b.next_header);
EXPECT_EQ(a.reserved1, b.reserved1);
EXPECT_EQ(a.header_type, b.header_type);
EXPECT_EQ(a.traffic_class.raw(), b.traffic_class.raw());
EXPECT_EQ(a.flags, b.flags);
EXPECT_EQ(a.payload, b.payload);
EXPECT_EQ(a.maximum_hop_limit, b.maximum_hop_limit);
EXPECT_EQ(a.reserved2, b.reserved2);
EXPECT_EQ(CommonHeader::length_bytes, serialize_length(a));
}
@@ -0,0 +1,89 @@
#include <gtest/gtest.h>
#include <vanetza/common/byte_buffer.hpp>
#include <vanetza/geonet/data_confirm.hpp>
#include <vanetza/geonet/data_request.hpp>
#include <vanetza/geonet/packet.hpp>
#include <algorithm>
using namespace vanetza::geonet;
using vanetza::units::si::seconds;
using vanetza::units::si::meter;
TEST(DataConfirm, ctor) {
DataConfirm a;
EXPECT_EQ(a.result_code, DataConfirm::ResultCode::Accepted);
DataConfirm b(DataConfirm::ResultCode::Rejected_Unspecified);
EXPECT_EQ(b.result_code, DataConfirm::ResultCode::Rejected_Unspecified);
}
TEST(DataConfirm, accepted_rejected) {
DataConfirm a(DataConfirm::ResultCode::Rejected_Max_Lifetime);
EXPECT_TRUE(a.rejected());
EXPECT_FALSE(a.accepted());
a.result_code = DataConfirm::ResultCode::Accepted;
EXPECT_FALSE(a.rejected());
EXPECT_TRUE(a.accepted());
}
TEST(DataConfirm, validate_data_request) {
MIB mib;
DataRequest req(mib);
EXPECT_EQ(validate_data_request(req, mib),
DataConfirm::ResultCode::Accepted);
DataRequest req_lt(req);
req_lt.maximum_lifetime.encode(mib.itsGnMaxPacketLifetime.decode() + 10.0 * seconds);
EXPECT_EQ(validate_data_request(req_lt, mib),
DataConfirm::ResultCode::Rejected_Max_Lifetime);
DataRequest req_rep(req);
req_rep.repetition = DataRequest::Repetition();
req_rep.repetition->interval = mib.itsGnMinPacketRepetitionInterval - 1 * seconds;
EXPECT_EQ(validate_data_request(req_rep, mib),
DataConfirm::ResultCode::Rejected_Min_Repetition_Interval);
}
TEST(DataConfirm, validate_data_request_with_area) {
MIB mib;
DataRequestWithArea req(mib);
EXPECT_EQ(validate_data_request(req, mib),
DataConfirm::ResultCode::Accepted);
Circle c;
// radius = magnitude of max area size -> circle area is much larger
c.r = vanetza::units::Length(mib.itsGnMaxGeoAreaSize / meter); // hack!
req.destination.shape = c;
EXPECT_EQ(validate_data_request(req, mib),
DataConfirm::ResultCode::Rejected_Max_Geo_Area_Size);
}
TEST(DataConfirm, validate_payload) {
MIB mib;
std::unique_ptr<DownPacket> no_payload;
std::unique_ptr<DownPacket> giant_payload(new DownPacket());
{
vanetza::ByteBuffer giant_buffer;
std::fill_n(std::back_inserter(giant_buffer), 2048, 0x0f);
(*giant_payload)[vanetza::OsiLayer::Link] = std::move(giant_buffer);
}
std::unique_ptr<DownPacket> ok_payload(new DownPacket());
EXPECT_EQ(validate_payload(no_payload, mib),
DataConfirm::ResultCode::Rejected_Unspecified);
EXPECT_EQ(validate_payload(giant_payload, mib),
DataConfirm::ResultCode::Rejected_Max_SDU_Size);
EXPECT_EQ(validate_payload(ok_payload, mib),
DataConfirm::ResultCode::Accepted);
}
TEST(DataConfirm, xor_op) {
DataConfirm a;
EXPECT_EQ(a.result_code, DataConfirm::ResultCode::Accepted);
a ^= DataConfirm::ResultCode::Rejected_Max_Lifetime;
EXPECT_EQ(a.result_code, DataConfirm::ResultCode::Rejected_Max_Lifetime);
a ^= DataConfirm::ResultCode::Accepted;
EXPECT_EQ(a.result_code, DataConfirm::ResultCode::Rejected_Max_Lifetime);
a ^= DataConfirm::ResultCode::Rejected_Unspecified;
EXPECT_EQ(a.result_code, DataConfirm::ResultCode::Rejected_Unspecified);
}
@@ -0,0 +1,69 @@
#include <gtest/gtest.h>
#include <vanetza/geonet/data_request.hpp>
#include <vanetza/units/time.hpp>
using namespace vanetza::geonet;
using vanetza::units::si::seconds;
TEST(DataRequest, repetition) {
MIB mib;
DataRequest r(mib);
EXPECT_FALSE(!!r.repetition);
r.repetition = DataRequest::Repetition();
EXPECT_TRUE(!!r.repetition);
}
TEST(DataRequest, has_further_repetition) {
DataRequest::Repetition repetition;
repetition.interval = 10.0 * seconds;
repetition.maximum = 0.0 * seconds;
EXPECT_FALSE(has_further_repetition(repetition));
repetition.interval = 0.0 * seconds;
EXPECT_FALSE(has_further_repetition(repetition));
repetition.maximum = -10.0 * seconds;
repetition.interval = -15.0 * seconds;
EXPECT_FALSE(has_further_repetition(repetition));
repetition.maximum = 30.0 * seconds;
repetition.interval = 10.0 * seconds;
EXPECT_TRUE(has_further_repetition(repetition));
repetition.interval = 0.0 * seconds;
EXPECT_FALSE(has_further_repetition(repetition));
}
TEST(DataRequest, decrement_by_one) {
DataRequest::Repetition repetition;
repetition.maximum = 30.0 * seconds;
repetition.interval = 10.0 * seconds;
decrement_by_one(repetition);
EXPECT_DOUBLE_EQ(repetition.interval / seconds, 10.0);
EXPECT_DOUBLE_EQ(repetition.maximum / seconds, 20.0);
repetition.interval = 5.0 * seconds;
decrement_by_one(repetition);
EXPECT_DOUBLE_EQ(repetition.maximum / seconds, 15.0);
repetition.interval = 20.0 * seconds;
decrement_by_one(repetition);
EXPECT_DOUBLE_EQ(repetition.maximum / seconds, 0.0);
EXPECT_DOUBLE_EQ(repetition.interval / seconds, 20.0);
repetition.maximum = -30.0 * seconds;
repetition.interval = -60.0 * seconds;
decrement_by_one(repetition);
EXPECT_DOUBLE_EQ(repetition.maximum / seconds, 0.0);
repetition.maximum = -30.0 * seconds;
repetition.interval = -10.0 * seconds;
decrement_by_one(repetition);
EXPECT_DOUBLE_EQ(repetition.maximum / seconds, 0.0);
repetition.maximum = 30.0 * seconds;
repetition.interval = -10.0 * seconds;
decrement_by_one(repetition);
EXPECT_DOUBLE_EQ(repetition.maximum / seconds, 0.0);
}
@@ -0,0 +1,46 @@
#include <gtest/gtest.h>
#include <vanetza/geonet/dcc_mco_field.hpp>
using namespace vanetza::geonet;
using vanetza::dcc::ChannelLoad;
TEST(DccMcoField, ctor)
{
DccMcoField mco;
EXPECT_EQ(ChannelLoad(0.0), mco.local_cbr());
EXPECT_EQ(ChannelLoad(0.0), mco.neighbour_cbr());
EXPECT_EQ(0, mco.output_power());
}
TEST(DccMcoField, uint32_view)
{
DccMcoField mco;
EXPECT_EQ(0, static_cast<uint32_t>(mco));
// last 11 bits are reserved for future use: masked zero
mco = DccMcoField(0x12345678);
EXPECT_EQ(0x12345000, static_cast<uint32_t>(mco));
}
TEST(DccMcoField, channel_load)
{
DccMcoField mco;
mco.local_cbr(ChannelLoad(0.5));
EXPECT_NEAR(0.5, mco.local_cbr().value(), 1.0 / 255.0);
mco.neighbour_cbr(ChannelLoad(0.25));
EXPECT_NEAR(0.25, mco.neighbour_cbr().value(), 1.0 / 255.0);
}
TEST(DccMcoField, output_power)
{
DccMcoField mco;
mco.output_power(10);
EXPECT_EQ(10, mco.output_power());
mco.output_power(31);
EXPECT_EQ(31, mco.output_power());
mco.output_power(32);
EXPECT_EQ(31, mco.output_power());
}
@@ -0,0 +1,35 @@
#include <gtest/gtest.h>
#include <vanetza/geonet/duplicate_packet_list.hpp>
using namespace vanetza::geonet;
TEST(DuplicatePacketList, check)
{
DuplicatePacketList dpl(3);
EXPECT_FALSE(dpl.check(SequenceNumber { 30 }));
EXPECT_TRUE(dpl.check(SequenceNumber { 30 }));
EXPECT_FALSE(dpl.check(SequenceNumber { 31 }));
EXPECT_TRUE(dpl.check(SequenceNumber { 30 }));
EXPECT_FALSE(dpl.check(SequenceNumber { 29 }));
EXPECT_TRUE(dpl.check(SequenceNumber { 29 }));
EXPECT_TRUE(dpl.check(SequenceNumber { 30 }));
EXPECT_FALSE(dpl.check(SequenceNumber { 36 }));
EXPECT_TRUE(dpl.check(SequenceNumber { 31 }));
EXPECT_FALSE(dpl.check(SequenceNumber { 30 }));
EXPECT_FALSE(dpl.check(SequenceNumber { 31 }));
EXPECT_TRUE(dpl.check(SequenceNumber { 36 }));
}
TEST(DuplicatePacketList, counter)
{
DuplicatePacketList dpl(3);
EXPECT_EQ(0, dpl.counter(SequenceNumber { 8 }));
EXPECT_FALSE(dpl.check(SequenceNumber { 8 }));
EXPECT_EQ(1, dpl.counter(SequenceNumber { 8 }));
EXPECT_TRUE(dpl.check(SequenceNumber { 8 }));
EXPECT_EQ(2, dpl.counter(SequenceNumber { 8 }));
EXPECT_FALSE(dpl.check(SequenceNumber { 1 }));
EXPECT_EQ(1, dpl.counter(SequenceNumber { 1 }));
EXPECT_EQ(2, dpl.counter(SequenceNumber { 8} ));
}
@@ -0,0 +1,46 @@
#ifndef FAKE_INTERFACES_HPP
#define FAKE_INTERFACES_HPP
#include <vanetza/dcc/data_request.hpp>
#include <vanetza/dcc/interface.hpp>
#include <vanetza/geonet/transport_interface.hpp>
#include <vanetza/geonet/data_indication.hpp>
#include <vanetza/geonet/data_confirm.hpp>
using namespace vanetza;
class FakeRequestInterface : public dcc::RequestInterface
{
public:
FakeRequestInterface() : m_requests(0) {}
void request(const dcc::DataRequest& req, std::unique_ptr<ChunkPacket> packet) override
{
++m_requests;
m_last_request = req;
m_last_packet = std::move(packet);
}
unsigned m_requests;
dcc::DataRequest m_last_request;
std::unique_ptr<ChunkPacket> m_last_packet;
};
class FakeTransportInterface : public geonet::TransportInterface
{
public:
FakeTransportInterface() : m_indications(0) {}
void indicate(const geonet::DataIndication& ind, std::unique_ptr<geonet::UpPacket> packet) override
{
++m_indications;
m_last_indication = ind;
m_last_packet = std::move(packet);
}
unsigned m_indications;
boost::optional<geonet::DataIndication> m_last_indication;
std::unique_ptr<geonet::UpPacket> m_last_packet;
};
#endif // FAKE_INTERFACES_HPP
@@ -0,0 +1,56 @@
#include <gtest/gtest.h>
#include <vanetza/geonet/gbc_gac_header.hpp>
#include <vanetza/geonet/tests/serialization.hpp>
#include <vanetza/units/angle.hpp>
using namespace vanetza::geonet;
using vanetza::geonet::detail::GbcGacHeader;
using vanetza::units::degree;
TEST(GbcGacHeader, ctor) {
GbcGacHeader hdr;
EXPECT_EQ(0, static_cast<SequenceNumber::value_type>(hdr.sequence_number));
EXPECT_EQ(0, hdr.reserved1);
EXPECT_EQ(LongPositionVector(), hdr.source_position);
EXPECT_EQ(0, hdr.geo_area_pos_latitude.value());
EXPECT_EQ(0, hdr.geo_area_pos_longitude.value());
EXPECT_EQ(0, hdr.distance_a.value());
EXPECT_EQ(0, hdr.distance_b.value());
EXPECT_EQ(0, hdr.angle.value());
EXPECT_EQ(0, hdr.reserved2);
}
TEST(GbcGacHeader, position) {
GbcGacHeader hdr;
GeodeticPosition pos { 12.3456789 * degree, 123.4567891 * degree };
hdr.position(pos);
EXPECT_EQ(123456789, hdr.geo_area_pos_latitude.value());
EXPECT_EQ(1234567891, hdr.geo_area_pos_longitude.value());
EXPECT_EQ(pos.latitude, hdr.position().latitude);
EXPECT_EQ(pos.longitude, hdr.position().longitude);
}
TEST(GbcGacHeader, serialization) {
GbcGacHeader a;
a.sequence_number = SequenceNumber(18);
a.reserved1 = 0x1234;
a.source_position.latitude = geo_angle_i32t::from_value(0xAABB);
a.source_position.longitude = geo_angle_i32t::from_value(0xCCDD);
a.distance_a = distance_u16t::from_value(0x1234);
a.distance_b = distance_u16t::from_value(0x4321);
a.angle = angle_u16t::from_value(0x1337);
a.reserved2 = 0x2020;
GbcGacHeader b = serialize_roundtrip(a);
EXPECT_EQ(a.sequence_number, b.sequence_number);
EXPECT_EQ(a.reserved1, b.reserved1);
EXPECT_EQ(a.source_position, b.source_position);
EXPECT_EQ(a.geo_area_pos_latitude, b.geo_area_pos_latitude);
EXPECT_EQ(a.geo_area_pos_longitude, b.geo_area_pos_longitude);
EXPECT_EQ(a.distance_a, b.distance_a);
EXPECT_EQ(a.distance_b, b.distance_b);
EXPECT_EQ(a.angle, b.angle);
EXPECT_EQ(a.reserved2, b.reserved2);
EXPECT_EQ(GbcGacHeader::length_bytes, serialize_length(a));
}
@@ -0,0 +1,135 @@
#include <vanetza/geonet/gbc_memory.hpp>
#include <vanetza/geonet/data_confirm.hpp>
#include <vanetza/geonet/tests/network_topology.hpp>
#include <gtest/gtest.h>
using namespace vanetza;
using namespace vanetza::geonet;
vanetza::units::Length operator""_m(long double length)
{
return vanetza::units::Length(length * vanetza::units::si::meters);
}
static GbcMemory::PacketIdentifier make_identifier(int station, std::uint16_t sn)
{
Address addr;
addr.mid(vanetza::create_mac_address(station));
return std::make_tuple(addr, SequenceNumber {sn});
}
TEST(GbcMemory, size)
{
GbcMemory mem;
EXPECT_EQ(0, mem.size());
mem.remember(make_identifier(1, 1));
EXPECT_EQ(1, mem.size());
mem.capacity(3);
EXPECT_EQ(1, mem.size());
mem.remember(make_identifier(1, 1));
EXPECT_EQ(1, mem.size());
mem.remember(make_identifier(1, 2));
mem.remember(make_identifier(1, 1));
EXPECT_EQ(2, mem.size());
mem.remember(make_identifier(1, 3));
mem.remember(make_identifier(1, 4));
EXPECT_EQ(3, mem.size());
}
TEST(GbcMemory, capacity)
{
GbcMemory mem;
mem.capacity(8);
for (int i = 0; i < 10; ++i) {
mem.remember(make_identifier(1, i));
}
EXPECT_EQ(8, mem.size());
mem.capacity(2);
EXPECT_EQ(2, mem.size());
EXPECT_FALSE(mem.knows(make_identifier(1, 7)));
EXPECT_TRUE(mem.knows(make_identifier(1, 8)));
EXPECT_TRUE(mem.knows(make_identifier(1, 9)));
}
TEST(GbcMemory, knows)
{
GbcMemory mem;
mem.capacity(3);
EXPECT_FALSE(mem.knows(make_identifier(2, 8)));
EXPECT_FALSE(mem.remember(make_identifier(2, 8)));
EXPECT_TRUE(mem.knows(make_identifier(2, 8)));
}
TEST(GbcMemory, remember)
{
GbcMemory mem;
mem.capacity(2);
EXPECT_FALSE(mem.remember(make_identifier(2, 8)));
EXPECT_TRUE(mem.remember(make_identifier(2, 8)));
EXPECT_FALSE(mem.remember(make_identifier(12, 25)));
EXPECT_FALSE(mem.remember(make_identifier(2, 5)));
EXPECT_EQ(2, mem.size());
EXPECT_FALSE(mem.knows(make_identifier(2, 8)));
}
TEST(GbcMemory, network)
{
NetworkTopology net;
net.get_mib().vanetzaGbcMemoryCapacity = 10;
net.get_mib().vanetzaDisableBeaconing = true;
net.get_mib().vanetzaFadingCbfCounter = true;
net.set_network_delay(std::chrono::milliseconds(4));
MacAddress car1 = create_mac_address(1);
net.add_router(car1);
net.set_position(car1, CartesianPosition(0.0_m, 0.0_m));
MacAddress car2 = create_mac_address(2);
net.add_router(car2);
net.set_position(car2, CartesianPosition(0.0_m, 100.0_m));
MacAddress car3 = create_mac_address(3);
net.add_router(car3);
net.set_position(car3, CartesianPosition(0.0_m, 50.0_m));
MacAddress car4 = create_mac_address(4);
net.add_router(car4);
net.set_position(car4, CartesianPosition(0.0_m, -75.0_m));
net.build_fully_meshed_reachability();
EXPECT_EQ(0, net.get_interface(car1)->transmissions);
EXPECT_EQ(0, net.get_transport(car2)->counter);
GbcDataRequest gbc_request(net.get_mib());
gbc_request.destination = circle_dest_area(150.0_m, 0.0_m, 0.0_m);
gbc_request.upper_protocol = UpperProtocol::IPv6;
std::unique_ptr<DownPacket> gbc_payload { new DownPacket() };
gbc_payload->layer(OsiLayer::Transport) = ByteBuffer(42);
auto gbc_confirm = net.get_router(car1)->request(gbc_request, std::move(gbc_payload));
ASSERT_TRUE(gbc_confirm.accepted());
net.advance_time(std::chrono::milliseconds(5));
EXPECT_EQ(1, net.get_interface(car1)->transmissions);
EXPECT_EQ(1, net.get_transport(car2)->counter);
// spend some time for packet forwarding operations
net.advance_time(std::chrono::seconds(1));
// explicitly repeat the last transmission of car1 without delay
//net.set_network_delay(std::chrono::seconds(0));
net.get_interface(car1)->transmit();
net.dispatch();
// no duplicate passed to transport layer
EXPECT_EQ(1, net.get_transport(car2)->counter);
// though more packets have been transmitted on link layer
EXPECT_LE(2, net.get_interface(car1)->transmissions);
}
@@ -0,0 +1,87 @@
#include <gtest/gtest.h>
#include <vanetza/geonet/lifetime.hpp>
using namespace vanetza::geonet;
using vanetza::units::si::seconds;
TEST(Lifetime, ctor) {
Lifetime a;
EXPECT_EQ(a.raw(), 0);
Lifetime b(Lifetime::Base::One_Second, 34);
EXPECT_EQ(b.raw(), 0x89);
}
TEST(Lifetime, set) {
Lifetime a;
a.set(Lifetime::Base::One_Second, 30);
EXPECT_EQ(a.raw(), 0x79);
}
TEST(Lifetime, less) {
Lifetime a(Lifetime::Base::Ten_Seconds, 6);
Lifetime b(Lifetime::Base::Ten_Seconds, 5);
Lifetime c(Lifetime::Base::One_Second, 55);
EXPECT_LT(b, a);
EXPECT_LT(b, c);
EXPECT_LT(c, a);
}
TEST(Lifetime, equality) {
Lifetime a(Lifetime::Base::One_Second, 30);
Lifetime b(Lifetime::Base::Ten_Seconds, 3);
Lifetime c(Lifetime::Base::One_Second, 29);
Lifetime d(Lifetime::Base::Ten_Seconds, 3);
EXPECT_EQ(a, b);
EXPECT_NE(a, c);
EXPECT_EQ(b, d);
}
TEST(Lifetime, decode) {
Lifetime a;
a.set(Lifetime::Base::Ten_Seconds, 43);
EXPECT_DOUBLE_EQ(a.decode() / seconds, 430.0);
Lifetime b;
b.set(Lifetime::Base::Fifty_Milliseconds, 3);
EXPECT_DOUBLE_EQ(b.decode() / seconds, 0.150);
Lifetime c;
c.set(Lifetime::Base::One_Second, 15);
EXPECT_DOUBLE_EQ(c.decode() / seconds, 15.0);
Lifetime d;
d.set(Lifetime::Base::Hundred_Seconds, 63);
EXPECT_DOUBLE_EQ(d.decode() / seconds, 6300.0);
}
TEST(Lifetime, encode) {
std::pair<double, double> pairs[] = {
{0.050, 0.0},
{0.075, 0.025},
{0.100, 0.0},
{0.158, 0.08},
{1.43, 0.07},
{3.00, 0.0},
{3.5, 0.5},
{16.3, 0.3},
{52.0, 0.0},
{64.0, 4.0},
{78.3, 1.7},
{138.0, 2.0},
{612.0, 2.0},
{700.0, 0.0},
{780.0, 20.0}
};
Lifetime a;
const double rel_error = 0.0000001; // fine enough, lifetime is not better than 50 ms
for (auto pair : pairs) {
a.encode(pair.first * seconds);
EXPECT_NEAR(a.decode() / seconds, pair.first, pair.second + rel_error);
}
}
TEST(Lifetime, zero) {
const Lifetime a = Lifetime::zero();
EXPECT_EQ(0.0 * seconds, a.decode());
}
@@ -0,0 +1,250 @@
#include <gtest/gtest.h>
#include <vanetza/common/manual_runtime.hpp>
#include <vanetza/geonet/address.hpp>
#include <vanetza/geonet/location_table.hpp>
#include <vanetza/geonet/mib.hpp>
#include <vanetza/geonet/position_vector.hpp>
#include <algorithm>
#include <chrono>
#include <cmath>
#include <memory>
#include <unordered_set>
using namespace vanetza::geonet;
using vanetza::Clock;
using vanetza::ManualRuntime;
class LocationTableTest : public ::testing::Test
{
protected:
virtual void SetUp() override
{
mib.reset(new MIB());
mib->itsGnLifetimeLocTE = 10 * vanetza::units::si::seconds;
runtime.reset(new ManualRuntime());
loct.reset(new LocationTable(*mib, *runtime));
}
virtual void TearDown() override
{
loct.reset();
runtime.reset();
loct.reset();
}
std::unique_ptr<MIB> mib;
std::unique_ptr<ManualRuntime> runtime;
std::unique_ptr<LocationTable> loct;
};
TEST_F(LocationTableTest, has_entry) {
Address a;
a.mid({1, 2, 3, 4, 5, 6});
EXPECT_FALSE(loct->has_entry(a));
LongPositionVector pv;
pv.gn_addr = a;
loct->update(pv);
EXPECT_TRUE(loct->has_entry(a));
// only MID of GN_ADDR should be used for look-up
a.country_code(42);
EXPECT_TRUE(loct->has_entry(a));
a.mid({0, 2, 3, 4, 5, 6});
EXPECT_FALSE(loct->has_entry(a));
}
TEST_F(LocationTableTest, position_vector) {
Address a;
a.mid({1, 2, 3, 4, 5, 6});
EXPECT_FALSE(loct->get_position(a));
LongPositionVector pv;
pv.gn_addr = a;
loct->update(pv);
auto retrieved_pv = loct->get_position(a);
ASSERT_TRUE(!!retrieved_pv);
EXPECT_EQ(pv, *retrieved_pv);
}
TEST_F(LocationTableTest, neighbourhood) {
EXPECT_FALSE(loct->has_neighbours());
Address addr_a;
addr_a.mid({1, 2, 3, 4, 5 ,6});
LongPositionVector pv_a;
pv_a.gn_addr = addr_a;
loct->update(pv_a);
EXPECT_FALSE(loct->has_neighbours());
loct->update(pv_a).set_neighbour(true);
EXPECT_TRUE(loct->has_neighbours());
Address addr_b;
addr_b.mid({2, 2, 2, 2, 2, 2});
LongPositionVector pv_b;
pv_b.gn_addr = addr_b;
loct->update(pv_b);
loct->update(pv_a).set_neighbour(false);
EXPECT_FALSE(loct->has_neighbours());
loct->update(pv_a).set_neighbour(true);
loct->update(pv_b).set_neighbour(true);
auto neighbours = loct->neighbours();
EXPECT_EQ(2, std::distance(neighbours.begin(), neighbours.end()));
EXPECT_TRUE(std::any_of(neighbours.begin(), neighbours.end(),
[&pv_a](const LocationTableEntry& e) {
return e.get_position_vector() == pv_a;
}));
EXPECT_TRUE(std::any_of(neighbours.begin(), neighbours.end(),
[&pv_b](const LocationTableEntry& e) {
return e.get_position_vector() == pv_b;
}));
loct->update(pv_a).set_neighbour(false);
neighbours = loct->neighbours();
EXPECT_EQ(1, std::distance(neighbours.begin(), neighbours.end()));
EXPECT_EQ(pv_b, neighbours.begin()->get_position_vector());
}
TEST_F(LocationTableTest, update_pdr) {
Address addr;
addr.mid({1, 0, 1, 0, 1, 0});
LongPositionVector pv;
pv.gn_addr = addr;
using std::chrono::milliseconds;
auto& entry = loct->update(pv);
EXPECT_TRUE(std::isnan(entry.get_pdr()));
runtime->trigger(milliseconds(100));
entry.update_pdr(30);
EXPECT_DOUBLE_EQ(0.0, entry.get_pdr());
runtime->trigger(milliseconds(100));
entry.update_pdr(10);
EXPECT_DOUBLE_EQ(50.0, entry.get_pdr());
runtime->trigger(milliseconds(1000));
entry.update_pdr(480);
EXPECT_DOUBLE_EQ(265.0, entry.get_pdr());
runtime->trigger(milliseconds(600));
entry.update_pdr(312);
EXPECT_DOUBLE_EQ(392.5, entry.get_pdr());
}
TEST_F(LocationTableTest, expire) {
const auto almost_expire = std::chrono::seconds(9);
const auto jiffy_expire = std::chrono::seconds(2);
LongPositionVector pv;
Address addr;
addr.mid({3, 7, 3, 7, 3, 7});
pv.gn_addr = addr;
loct->update(pv);
EXPECT_TRUE(loct->has_entry(addr));
runtime->trigger(almost_expire);
EXPECT_TRUE(loct->has_entry(addr));
runtime->trigger(jiffy_expire);
EXPECT_FALSE(loct->has_entry(addr));
loct->update(pv);
EXPECT_TRUE(loct->has_entry(addr));
runtime->trigger(almost_expire);
EXPECT_TRUE(loct->has_entry(addr));
loct->update(pv); // no refresh with same PV
runtime->trigger(jiffy_expire);
EXPECT_FALSE(loct->has_entry(addr));
loct->update(pv);
runtime->trigger(almost_expire);
EXPECT_TRUE(loct->has_entry(addr));
pv.timestamp += 20 * Timestamp::millisecond();
loct->update(pv); // refresh with updated PV
runtime->trigger(jiffy_expire);
EXPECT_TRUE(loct->has_entry(addr));
}
TEST_F(LocationTableTest, update_creates_entry) {
LongPositionVector pv;
pv.gn_addr.mid({ 0, 0, 0, 0, 0, 1 });
EXPECT_FALSE(loct->has_entry(pv.gn_addr));
loct->update(pv);
const LongPositionVector* pv_loct = loct->get_position(pv.gn_addr);
ASSERT_TRUE(pv_loct);
EXPECT_EQ(pv, *pv_loct);
EXPECT_TRUE(loct->has_entry(pv.gn_addr));
}
TEST_F(LocationTableTest, visit) {
using vanetza::MacAddress;
std::unordered_set<Address> addresses = {
Address { MacAddress {0, 0, 0, 0, 0, 1}},
Address { MacAddress {0, 0, 0, 0, 0, 2}},
Address { MacAddress {0, 0, 0, 0, 0, 3}},
Address { MacAddress {0, 0, 0, 0, 0, 4}},
Address { MacAddress {0, 0, 0, 0, 0, 5}}
};
for (auto& addr : addresses) {
LongPositionVector pv;
pv.gn_addr = addr;
loct->update(pv);
}
std::unordered_set<Address> visited_addresses;
loct->visit([&visited_addresses](const MacAddress&, const LocationTableEntry& entry) {
visited_addresses.insert(entry.geonet_address());
});
EXPECT_EQ(addresses.size(), visited_addresses.size());
}
TEST(LocationTableEntry, update_position_vector)
{
ManualRuntime rt;
LocationTableEntry locte(rt);
LongPositionVector lpv;
EXPECT_FALSE(locte.update_position_vector(lpv));
lpv.latitude = geo_angle_i32t::from_value(91 * 10 * 1000 * 1000);
EXPECT_FALSE(locte.update_position_vector(lpv));
lpv.latitude = geo_angle_i32t::from_value(90 * 10 * 1000 * 1000);
EXPECT_TRUE(locte.update_position_vector(lpv));
}
TEST(LocationTableEntry, init)
{
ManualRuntime rt;
LocationTableEntry locte(rt);
EXPECT_FALSE(locte.has_position_vector());
EXPECT_TRUE(std::isnan(locte.get_pdr()));
EXPECT_FALSE(locte.is_neighbour());
}
TEST(LocationTableEntry, neighbour)
{
ManualRuntime rt;
LocationTableEntry locte(rt);
locte.set_neighbour(true);
EXPECT_TRUE(locte.is_neighbour());
locte.set_neighbour(false);
EXPECT_FALSE(locte.is_neighbour());
locte.set_neighbour(true, std::chrono::seconds(5));
rt.trigger(std::chrono::seconds(4));
EXPECT_TRUE(locte.is_neighbour());
rt.trigger(std::chrono::seconds(1));
EXPECT_FALSE(locte.is_neighbour());
locte.set_neighbour(false, std::chrono::seconds(1));
EXPECT_FALSE(locte.is_neighbour());
rt.trigger(std::chrono::seconds(2));
EXPECT_FALSE(locte.is_neighbour());
}
@@ -0,0 +1,352 @@
#include <vanetza/dcc/data_request.hpp>
#include <vanetza/dcc/interface.hpp>
#include <vanetza/geonet/areas.hpp>
#include <vanetza/geonet/data_confirm.hpp>
#include <vanetza/geonet/data_indication.hpp>
#include <vanetza/geonet/mib.hpp>
#include <vanetza/geonet/packet.hpp>
#include <vanetza/geonet/router.hpp>
#include <vanetza/geonet/timestamp.hpp>
#include <vanetza/geonet/tests/network_topology.hpp>
#include <vanetza/net/mac_address.hpp>
#include <boost/optional.hpp>
#include <list>
#include <stdexcept>
#include <unordered_map>
namespace vanetza
{
namespace geonet
{
std::unique_ptr<UpPacket> duplicate_copy_construct(const ChunkPacket& packet)
{
return std::unique_ptr<UpPacket> { new UpPacket(packet) };
}
std::unique_ptr<UpPacket> duplicate_serialize(const ChunkPacket& packet)
{
ByteBuffer buf_packet;
for (auto layer : osi_layer_range<OsiLayer::Network, OsiLayer::Application>()) {
ByteBuffer buf_layer;
packet[layer].convert(buf_layer);
buf_packet.insert(buf_packet.end(), buf_layer.begin(), buf_layer.end());
}
assert(buf_packet.size() == packet.size(OsiLayer::Network, OsiLayer::Application));
return std::unique_ptr<UpPacket> { new UpPacket(CohesivePacket(std::move(buf_packet), OsiLayer::Network)) };
}
NetworkTopology::RequestInterface::RequestInterface(NetworkTopology& network, const MacAddress& mac) :
network(network), address(mac)
{
}
void NetworkTopology::RequestInterface::request(const dcc::DataRequest& req, std::unique_ptr<ChunkPacket> packet)
{
++requests;
last_request = req;
last_request.source = address;
last_packet = std::move(packet);
transmit();
}
void NetworkTopology::RequestInterface::reset()
{
requests = 0;
transmissions = 0;
last_request = dcc::DataRequest {};
last_packet.reset();
}
void NetworkTopology::RequestInterface::transmit()
{
if (last_packet) {
++transmissions;
network.save_request(last_request, std::unique_ptr<ChunkPacket> { new ChunkPacket(*last_packet) });
}
}
void NetworkTopology::TransportHandler::indicate(const DataIndication& ind, std::unique_ptr<UpPacket> packet)
{
++counter;
last_indication = ind;
last_packet = std::move(packet);
}
void NetworkTopology::TransportHandler::reset()
{
counter = 0;
last_indication = DataIndication {};
last_packet.reset();
}
NetworkTopology::RouterContext::RouterContext(NetworkTopology& network) :
request_interface(network, mac_address),
runtime(network.now),
security(runtime),
router(runtime, network.get_mib())
{
router.set_access_interface(&request_interface);
router.set_security_entity(&security.entity());
router.set_transport_handler(UpperProtocol::IPv6, &transport_interface);
set_position_accuracy_indicator(true);
router.packet_dropped = [](Router::PacketDropReason pdr) {
throw std::runtime_error("packet dropped unexpectedly: " + stringify(pdr));
};
}
void NetworkTopology::RouterContext::set_position_accuracy_indicator(bool flag)
{
const double pai_scaling = flag ? 0.25 : 0.75;
position.confidence.semi_minor = pai_scaling * router.get_mib().itsGnPaiInterval;
position.confidence.semi_major = pai_scaling * router.get_mib().itsGnPaiInterval;
router.update_position(position);
assert(router.get_local_position_vector().position_accuracy_indicator == flag);
}
NetworkTopology::NetworkTopology() : now(Clock::at("2016-02-29 23:59"))
{
set_duplication_mode(PacketDuplicationMode::Copy_Construct);
assert(fn_duplicate);
}
boost::optional<NetworkTopology::RouterContext&> NetworkTopology::get_host(const MacAddress& addr)
{
boost::optional<RouterContext&> context;
auto found = hosts.find(addr);
if (found != hosts.end())
context = *found->second;
return context;
}
boost::optional<Router&> NetworkTopology::get_router(const MacAddress& addr)
{
boost::optional<Router&> router;
auto context = get_host(addr);
if (context)
router = context->router;
return router;
}
boost::optional<NetworkTopology::RequestInterface&> NetworkTopology::get_interface(const MacAddress& addr)
{
boost::optional<NetworkTopology::RequestInterface&> interface;
auto context = get_host(addr);
if (context)
interface = context->request_interface;
return interface;
}
boost::optional<NetworkTopology::TransportHandler&> NetworkTopology::get_transport(const MacAddress& addr)
{
boost::optional<NetworkTopology::TransportHandler&> transport;
auto context = get_host(addr);
if (context)
transport = context->transport_interface;
return transport;
}
const unsigned& NetworkTopology::get_counter_requests(const MacAddress& addr)
{
return counter_requests[addr];
}
void NetworkTopology::add_router(const MacAddress& addr)
{
std::unique_ptr<RouterContext> context { new RouterContext(*this) };
context->mac_address = addr;
context->router.set_address(Address(context->mac_address));
hosts.emplace(addr, std::move(context));
}
void NetworkTopology::add_reachability(const MacAddress& addr, std::initializer_list<MacAddress> new_reachables)
{
// save reachable routers in reachability map
std::set<MacAddress>& reachables = reachability[addr];
for (const MacAddress& new_reachable : new_reachables) {
reachables.insert(new_reachable);
}
}
void NetworkTopology::save_request(const dcc::DataRequest& req, std::unique_ptr<ChunkPacket> packet)
{
// save request with packet in list requests
requests.emplace_back(now + network_delay, req, std::move(packet));
// increment request counter
counter_requests[req.source]++;
}
void NetworkTopology::dispatch()
{
// process a stable sequence of saved requests
decltype(requests) current_requests;
std::swap(current_requests, requests);
decltype(requests) skipped_requests;
for (auto& tuple: current_requests) {
// postpone transmission if its time has not yet come
auto& timepoint = std::get<0>(tuple);
if (timepoint > now) {
skipped_requests.emplace_back(std::move(tuple));
continue;
}
// extract request and packet from tuple
auto& req = std::get<1>(tuple);
auto& packet = std::get<2>(tuple);
auto neighbours = reachability[req.source];
// broadcast packet to all reachable routers
if (req.destination == cBroadcastMacAddress) {
for (auto& mac: neighbours) {
auto router = get_router(mac);
if (router) {
send(*router, req.source, req.destination, *packet);
}
}
}
// send packet only to specific destination router
else if (neighbours.find(req.destination) != neighbours.end()) {
auto router = get_router(req.destination);
if (router) {
send(*router, req.source, req.destination, *packet);
}
}
}
// move all skipped requests to head of pending requests
requests.splice(requests.begin(), std::move(skipped_requests));
}
void NetworkTopology::send(Router& receiver, const MacAddress& sender, const MacAddress& destination, const ChunkPacket& packet)
{
assert(sender != destination);
counter_indications++;
std::unique_ptr<UpPacket> packet_up = fn_duplicate(packet);
receiver.indicate(std::move(packet_up), sender, destination);
}
void NetworkTopology::set_position(const MacAddress& addr, CartesianPosition c)
{
// convert cartesian to geodetic position
GeodeticPosition pos = convert_cartesian_geodetic(c);
auto host = get_host(addr);
if (host) {
host->position.timestamp = now;
host->position.latitude = pos.latitude;
host->position.longitude = pos.longitude;
host->router.update_position(host->position);
host->security.set_accurate_position(host->position.latitude, host->position.longitude);
}
}
void NetworkTopology::advance_time(Clock::duration t)
{
do {
auto next = next_event();
const auto step = std::min(t, next - now);
now += step;
t -= step;
// update timestamp for every router
for (auto& kv : hosts) {
RouterContext& host = *kv.second;
host.runtime.trigger(now);
host.position.timestamp = now;
host.router.update_position(host.position);
}
dispatch();
} while (t.count() > 0);
}
Clock::time_point NetworkTopology::next_event() const
{
// next event may be pending link layer request
Clock::time_point next = requests.empty() ? Clock::time_point::max() : std::get<0>(requests.front());
for (auto& kv : hosts) {
RouterContext& host = *kv.second;
if (host.runtime.next() > now && host.runtime.next() < next) {
next = host.runtime.next();
}
}
return next;
}
void NetworkTopology::reset_counters()
{
counter_indications = 0;
counter_requests.clear();
requests.clear();
for (auto& host : hosts) {
RouterContext* ctx = std::get<1>(host).get();
ctx->request_interface.reset();
ctx->transport_interface.reset();
}
}
void NetworkTopology::set_duplication_mode(PacketDuplicationMode mode)
{
switch (mode) {
case PacketDuplicationMode::Copy_Construct:
fn_duplicate = &duplicate_copy_construct;
break;
case PacketDuplicationMode::Serialize:
fn_duplicate = &duplicate_serialize;
break;
default:
throw std::runtime_error("Invalid PacketDuplicationMode");
break;
}
}
void NetworkTopology::set_network_delay(Clock::duration delay)
{
network_delay = delay;
}
void NetworkTopology::build_fully_meshed_reachability()
{
reachability.clear();
for (auto& outer : hosts) {
for (auto& inner : hosts) {
if (outer != inner) {
reachability[outer.first].insert(inner.first);
}
}
}
}
GeodeticPosition convert_cartesian_geodetic(const CartesianPosition& cart)
{
// simple equirectangular reverse projection is sufficient for testing
static const units::Length earth_radius = 6371000.0 * units::si::meter;
units::Angle lat = cart.y / earth_radius * units::si::radians;
units::Angle lon = cart.x / earth_radius * units::si::radians;
return GeodeticPosition(units::GeoAngle(lat), units::GeoAngle(lon));
}
Area circle_dest_area(units::Length radius, units::Length midpoint_x, units::Length midpoint_y)
{
using namespace vanetza::units;
using namespace vanetza::units::si;
Area dest_area;
Circle c;
c.r = radius;
dest_area.shape = c;
dest_area.position = convert_cartesian_geodetic(CartesianPosition(midpoint_x, midpoint_y));
return dest_area;
}
} // namespace geonet
} // namespace vanetza
@@ -0,0 +1,119 @@
#include <vanetza/common/clock.hpp>
#include <vanetza/common/manual_runtime.hpp>
#include <vanetza/common/position_fix.hpp>
#include <vanetza/dcc/data_request.hpp>
#include <vanetza/dcc/interface.hpp>
#include <vanetza/geonet/areas.hpp>
#include <vanetza/geonet/data_indication.hpp>
#include <vanetza/geonet/mib.hpp>
#include <vanetza/geonet/packet.hpp>
#include <vanetza/geonet/router.hpp>
#include <vanetza/geonet/transport_interface.hpp>
#include <vanetza/geonet/tests/security_context.hpp>
#include <vanetza/net/mac_address.hpp>
#include <vanetza/units/length.hpp>
#include <boost/optional.hpp>
#include <functional>
#include <initializer_list>
#include <list>
#include <set>
#include <unordered_map>
namespace vanetza
{
namespace geonet
{
class NetworkTopology
{
public:
enum class PacketDuplicationMode
{
Copy_Construct,
Serialize
};
class RequestInterface : public dcc::RequestInterface
{
public:
RequestInterface(NetworkTopology&, const MacAddress&);
void request(const dcc::DataRequest&, std::unique_ptr<ChunkPacket>) override;
void reset();
void transmit();
unsigned requests = 0;
unsigned transmissions = 0;
dcc::DataRequest last_request;
std::unique_ptr<ChunkPacket> last_packet;
private:
NetworkTopology& network;
const MacAddress& address;
};
class TransportHandler : public TransportInterface
{
public:
void indicate(const DataIndication&, std::unique_ptr<UpPacket>) override;
void reset();
unsigned counter = 0;
DataIndication last_indication;
std::unique_ptr<UpPacket> last_packet;
};
class RouterContext
{
public:
RouterContext(NetworkTopology&);
void set_position_accuracy_indicator(bool flag);
MacAddress mac_address;
RequestInterface request_interface;
TransportHandler transport_interface;
ManualRuntime runtime;
PositionFix position;
SecurityContext security;
Router router;
};
NetworkTopology();
boost::optional<RouterContext&> get_host(const MacAddress&);
boost::optional<Router&> get_router(const MacAddress&);
boost::optional<RequestInterface&> get_interface(const MacAddress&);
boost::optional<TransportHandler&> get_transport(const MacAddress&);
const unsigned& get_counter_requests(const MacAddress&);
const unsigned& get_counter_indications() const { return counter_indications; }
ManagementInformationBase& get_mib() { return mib; }
void add_router(const MacAddress&);
void add_reachability(const MacAddress&, std::initializer_list<MacAddress>);
void save_request(const dcc::DataRequest&, std::unique_ptr<ChunkPacket>);
void dispatch();
void send(Router&, const MacAddress&, const MacAddress&, const ChunkPacket&);
void set_position(const MacAddress&, CartesianPosition);
void advance_time(Clock::duration t);
void reset_counters();
void set_duplication_mode(PacketDuplicationMode);
void set_network_delay(Clock::duration delay);
void build_fully_meshed_reachability();
private:
Clock::time_point next_event() const;
using PendingTransmission = std::tuple<Clock::time_point, dcc::DataRequest, std::unique_ptr<ChunkPacket>>;
std::unordered_map<MacAddress, unsigned> counter_requests;
std::unordered_map<MacAddress, std::unique_ptr<RouterContext>> hosts;
std::unordered_map<MacAddress, std::set<MacAddress>> reachability;
std::list<PendingTransmission> requests;
Clock::duration network_delay = Clock::duration::zero();
Clock::time_point now;
ManagementInformationBase mib;
unsigned counter_indications;
std::function<std::unique_ptr<UpPacket>(const ChunkPacket&)> fn_duplicate;
};
GeodeticPosition convert_cartesian_geodetic(const CartesianPosition&);
Area circle_dest_area(units::Length radius, units::Length midpoint_x, units::Length midpoint_y);
} // namespace geonet
} // namespace vanetza
@@ -0,0 +1,117 @@
#include <gtest/gtest.h>
#include <vanetza/geonet/basic_header.hpp>
#include <vanetza/geonet/extended_pdu.hpp>
#include <vanetza/geonet/packet_buffer.hpp>
#include <vanetza/geonet/shb_header.hpp>
#include <chrono>
using std::chrono::seconds;
using std::chrono::milliseconds;
using namespace vanetza;
using namespace vanetza::geonet;
struct FakePacket
{
FakePacket(unsigned id) : id(id) {}
const unsigned id = 0;
std::size_t length = 0;
Clock::duration lifetime = Clock::duration::zero();
};
class FakeData : public packet_buffer::Data
{
public:
FakeData(unsigned id, std::list<FakePacket>& flushed) : m_flushed(flushed), m_packet(id) {}
FakePacket& packet() { return m_packet; }
std::size_t length() const override { return m_packet.length; }
Clock::duration reduce_lifetime(Clock::duration d) override { m_packet.lifetime -= d; return m_packet.lifetime; }
void flush() override { m_flushed.push_back(m_packet); }
private:
std::list<FakePacket>& m_flushed;
FakePacket m_packet;
};
class PacketBufferTest : public ::testing::Test
{
protected:
void SetUp() override
{
counter = 0;
now = Clock::time_point { std::chrono::minutes(1234) };
}
std::unique_ptr<FakeData> create_valid_length(std::size_t length)
{
std::unique_ptr<FakeData> data { new FakeData(++counter, flushed) };
data->packet().length = length;
return data;
}
std::unique_ptr<FakeData> create_valid_lifetime(Clock::duration d)
{
std::unique_ptr<FakeData> data { new FakeData(++counter, flushed) };
data->packet().length = 100;
data->packet().lifetime = d;
return data;
}
Clock::time_point now;
unsigned counter;
std::list<FakePacket> flushed;
};
TEST_F(PacketBufferTest, push)
{
PacketBuffer buffer(8192);
EXPECT_TRUE(buffer.push(create_valid_length(5000), now));
EXPECT_TRUE(buffer.push(create_valid_length(5000), now));
EXPECT_FALSE(buffer.push(create_valid_length(8200), now));
}
TEST_F(PacketBufferTest, flush_head_drop)
{
PacketBuffer buffer(8192);
buffer.push(create_valid_length(2000), now);
buffer.push(create_valid_length(3000), now);
buffer.push(create_valid_length(4000), now);
EXPECT_EQ(0, flushed.size());
buffer.flush(now);
ASSERT_EQ(2, flushed.size());
EXPECT_EQ(2, flushed.front().id);
EXPECT_EQ(3, flushed.back().id);
// buffer shall be empty now (flushed list remains empty)
flushed.clear();
buffer.flush(now);
EXPECT_EQ(0, flushed.size());
}
TEST_F(PacketBufferTest, flush_expired)
{
PacketBuffer buffer(8192);
buffer.push(create_valid_lifetime(milliseconds(3200)), now);
now += seconds(3);
buffer.push(create_valid_lifetime(milliseconds(10300)), now);
buffer.push(create_valid_lifetime(seconds(1)), now);
now += seconds(2);
buffer.flush(now);
ASSERT_EQ(1, flushed.size());
EXPECT_EQ(2, flushed.front().id);
}
TEST_F(PacketBufferTest, update_lifetime)
{
PacketBuffer buffer(8192);
buffer.push(create_valid_lifetime(milliseconds(2300)), now);
now += milliseconds(150);
buffer.flush(now);
ASSERT_EQ(1, flushed.size());
EXPECT_EQ(milliseconds(2150), flushed.front().lifetime);
}
@@ -0,0 +1,109 @@
#include <gtest/gtest.h>
#include <vanetza/common/manual_runtime.hpp>
#include <vanetza/common/position_provider.hpp>
#include <vanetza/geonet/mib.hpp>
#include <vanetza/geonet/position_updater.hpp>
#include <vanetza/geonet/router.hpp>
#include <chrono>
using namespace vanetza;
using namespace vanetza::geonet;
using namespace std::chrono;
class IncrementalPositionProvider : public PositionProvider
{
public:
const PositionFix& position_fix() override
{
next_position();
return position;
}
private:
void next_position()
{
++updates;
position.latitude = updates * 3.0 * units::degree;
position.longitude = updates * -1.5 * units::degree;
position.confidence.semi_minor = 25.0 * units::si::meter;
position.confidence.semi_major = 25.0 * units::si::meter;
}
unsigned updates = 0;
PositionFix position;
};
class PositionUpdaterTest : public ::testing::Test
{
public:
PositionUpdaterTest() :
router(runtime, mib), updater(runtime, positioning, router)
{
}
void SetUp() override
{
mib.itsGnSecurity = false; /*< no security entity required */
mib.itsGnMinimumUpdateFrequencyEPV = 1.0 * units::si::hertz;
}
unsigned lpv_updates()
{
units::GeoAngle latitude { router.get_local_position_vector().latitude };
return latitude.value() / 3;
}
protected:
ManualRuntime runtime;
MIB mib;
Router router;
IncrementalPositionProvider positioning;
PositionUpdater updater;
};
TEST_F(PositionUpdaterTest, default_update_rate)
{
EXPECT_EQ(0, lpv_updates());
runtime.trigger(milliseconds(950));
EXPECT_EQ(0, lpv_updates());
runtime.trigger(milliseconds(60));
EXPECT_EQ(1, lpv_updates());
runtime.trigger(seconds(1));
EXPECT_EQ(2, lpv_updates());
runtime.trigger(seconds(1));
EXPECT_EQ(3, lpv_updates());
}
TEST_F(PositionUpdaterTest, custom_update_interval)
{
updater.update_rate(seconds(10));
runtime.trigger(seconds(9));
EXPECT_EQ(0, lpv_updates());
runtime.trigger(seconds(1));
EXPECT_EQ(1, lpv_updates());
runtime.trigger(seconds(10));
EXPECT_EQ(2, lpv_updates());
}
TEST_F(PositionUpdaterTest, custom_update_frequency)
{
updater.update_rate(4.0 * units::si::hertz);
runtime.trigger(milliseconds(250));
EXPECT_EQ(1, lpv_updates());
runtime.trigger(milliseconds(250));
EXPECT_EQ(2, lpv_updates());
}
TEST_F(PositionUpdaterTest, zero_hertz)
{
updater.update_rate(0.0 * units::si::hertz);
runtime.trigger(seconds(60));
EXPECT_EQ(0, lpv_updates());
}
TEST_F(PositionUpdaterTest, negative_interval)
{
updater.update_rate(milliseconds(-200));
runtime.trigger(seconds(60));
EXPECT_EQ(0, lpv_updates());
}
@@ -0,0 +1,72 @@
#include <gtest/gtest.h>
#include <vanetza/geonet/position_vector.hpp>
#include <vanetza/geonet/serialization_buffer.hpp>
#include <vanetza/units/angle.hpp>
using vanetza::ByteBuffer;
using vanetza::MacAddress;
using namespace vanetza::geonet;
using vanetza::units::degree;
TEST(PositionVector, lpv_serialization)
{
LongPositionVector lpv1;
lpv1.gn_addr.is_manually_configured(true);
lpv1.gn_addr.station_type(StationType::Motorcycle);
lpv1.gn_addr.country_code(0x0333);
lpv1.gn_addr.mid(MacAddress { 1, 2, 3, 0xa, 0xb, 0xc });
lpv1.timestamp += 4321 * Timestamp::millisecond();
lpv1.latitude = geo_angle_i32t::from_value(0x1234);
lpv1.longitude = geo_angle_i32t::from_value(0xabcd);
lpv1.position_accuracy_indicator = false;
lpv1.speed = LongPositionVector::speed_u15t::from_value(12345);
lpv1.heading.from_value(0xef);
ByteBuffer buffer;
serialize_into_buffer(lpv1, buffer);
EXPECT_EQ(LongPositionVector::length_bytes, buffer.size());
LongPositionVector lpv2;
deserialize_from_buffer(lpv2, buffer);
EXPECT_EQ(lpv1.gn_addr, lpv2.gn_addr);
EXPECT_EQ(lpv1, lpv2);
}
TEST(PositionVector, zero_initialized)
{
// "At start-up, all data elements of the LPV shall be initialized
// with 0 to indicate an unknown value." (EN 302 636-4-1 V1.2.0)
LongPositionVector lpv;
EXPECT_TRUE(is_empty(lpv));
ByteBuffer buffer;
serialize_into_buffer(lpv, buffer);
ByteBuffer zero;
zero.assign(LongPositionVector::length_bytes, 0x00);
EXPECT_EQ(zero, buffer);
}
TEST(PositionVector, is_valid)
{
LongPositionVector lpv;
EXPECT_FALSE(is_valid(lpv));
lpv.latitude = geo_angle_i32t { 30.0 * degree };
EXPECT_TRUE(is_valid(lpv));
lpv.heading = heading_u16t { 361.0 * degree };
EXPECT_FALSE(is_valid(lpv));
lpv.heading = heading_u16t { 180.0 * degree };
EXPECT_TRUE(is_valid(lpv));
lpv.latitude = geo_angle_i32t { -91.0 * degree };
EXPECT_FALSE(is_valid(lpv));
lpv.latitude = geo_angle_i32t::from_value(0);
lpv.longitude = geo_angle_i32t { 175.0 * degree };
EXPECT_TRUE(is_valid(lpv));
lpv.longitude = geo_angle_i32t { 185.0 * degree };
EXPECT_FALSE(is_valid(lpv));
}
@@ -0,0 +1,123 @@
#include <gtest/gtest.h>
#include <vanetza/common/manual_runtime.hpp>
#include <vanetza/geonet/data_request.hpp>
#include <vanetza/geonet/mib.hpp>
#include <vanetza/geonet/packet.hpp>
#include <vanetza/geonet/repeater.hpp>
using namespace vanetza;
using namespace vanetza::geonet;
struct FakeRepetitionDispatcher : public boost::static_visitor<>
{
FakeRepetitionDispatcher(Repeater& _repeater, const DownPacket& _packet) :
repeater(_repeater), packet(std::move(_packet))
{
}
template<typename REQUEST>
void operator()(const REQUEST& request)
{
repeater.add(request, std::move(packet));
}
Repeater& repeater;
const DownPacket& packet;
};
class RepeaterTest : public ::testing::Test
{
protected:
RepeaterTest() : repeater(runtime, repetition_callback()), dispatch_counter(0)
{
}
void SetUp() override
{
runtime.trigger(std::chrono::hours(23));
dispatch_counter = 0;
}
void dispatch_repetition(const DataRequestVariant& request, std::unique_ptr<DownPacket> packet)
{
++dispatch_counter;
FakeRepetitionDispatcher dispatcher(repeater, *packet);
boost::apply_visitor(dispatcher, request);
}
Repeater::Callback repetition_callback()
{
namespace ph = std::placeholders;
return std::bind(&RepeaterTest::dispatch_repetition, this, ph::_1, ph::_2);
}
MIB mib;
ManualRuntime runtime;
Repeater repeater;
unsigned dispatch_counter;
const DownPacket packet;
};
TEST_F(RepeaterTest, no_repetition) {
runtime.trigger(std::chrono::seconds(3));
EXPECT_EQ(0, dispatch_counter);
ShbDataRequest shb(mib);
EXPECT_FALSE(!!shb.repetition);
repeater.add(shb, packet);
runtime.trigger(std::chrono::seconds(3));
EXPECT_EQ(0, dispatch_counter);
DataRequest::Repetition repetition;
repetition.interval = 5.0 * units::si::seconds;
repetition.maximum = 4.9 * units::si::seconds;
shb.repetition = repetition;
repeater.add(shb, packet);
EXPECT_EQ(0, dispatch_counter);
runtime.trigger(std::chrono::seconds(10));
EXPECT_EQ(0, dispatch_counter);
}
TEST_F(RepeaterTest, single_repetition) {
ShbDataRequest shb(mib);
DataRequest::Repetition repetition;
repetition.interval = 1.0 * units::si::seconds;
repetition.maximum = 1.0 * units::si::seconds;
shb.repetition = repetition;
repeater.add(shb, packet);
EXPECT_EQ(0, dispatch_counter);
runtime.trigger(runtime.now());
EXPECT_EQ(0, dispatch_counter);
runtime.trigger(std::chrono::milliseconds(900));
EXPECT_EQ(0, dispatch_counter);
runtime.trigger(std::chrono::seconds(1));
EXPECT_EQ(1, dispatch_counter);
runtime.trigger(std::chrono::seconds(5));
EXPECT_EQ(1, dispatch_counter);
}
TEST_F(RepeaterTest, multiple_repetition) {
ShbDataRequest shb(mib);
DataRequest::Repetition repetition;
repetition.interval = 2.0 * units::si::seconds;
repetition.maximum = 9.0 * units::si::seconds;
shb.repetition = repetition;
repeater.add(shb, packet);
EXPECT_EQ(0, dispatch_counter);
runtime.trigger(runtime.now());
EXPECT_EQ(0, dispatch_counter);
runtime.trigger(std::chrono::milliseconds(1900));
EXPECT_EQ(0, dispatch_counter);
runtime.trigger(std::chrono::milliseconds(100));
EXPECT_EQ(1, dispatch_counter);
runtime.trigger(std::chrono::seconds(2));
EXPECT_EQ(2, dispatch_counter);
// When triggered too slowly (large time difference), still just one repetition is triggered
runtime.trigger(std::chrono::seconds(5));
EXPECT_EQ(3, dispatch_counter);
runtime.trigger(std::chrono::seconds(10));
EXPECT_EQ(3, dispatch_counter);
}
@@ -0,0 +1,82 @@
#include <gtest/gtest.h>
#include <vanetza/common/manual_runtime.hpp>
#include <vanetza/geonet/mib.hpp>
#include <vanetza/geonet/router.hpp>
#include <boost/optional.hpp>
#include <vanetza/geonet/tests/fake_interfaces.hpp>
using namespace vanetza;
using namespace vanetza::geonet;
TEST(Router, shb_round_trip)
{
// mock interfaces
FakeRequestInterface req_ifc;
FakeTransportInterface ind_ifc;
// init router
ManagementInformationBase mib;
ManualRuntime runtime;
Router router(runtime, mib);
Address gn_addr;
gn_addr.mid(MacAddress { 0, 0, 0, 0, 0, 1});
router.set_address(gn_addr);
router.set_access_interface(&req_ifc);
router.set_transport_handler(UpperProtocol::IPv6, &ind_ifc);
// initialize shb request
ShbDataRequest shb_request(mib);
shb_request.upper_protocol = UpperProtocol::IPv6;
// create down packet
const ByteBuffer send_payload { 89, 27, 1, 4, 18, 85 };
std::unique_ptr<DownPacket> packet_down { new DownPacket() };
packet_down->layer(OsiLayer::Transport) = ByteBuffer(send_payload);
// get count of current requests in buffer
const auto requests_before = req_ifc.m_requests;
// add request with our test down packet
auto confirm = router.request(shb_request, std::move(packet_down));
// check expected behavior
ASSERT_TRUE(confirm.accepted());
EXPECT_EQ(requests_before + 1, req_ifc.m_requests);
// extract payload from last received request/packet
ASSERT_TRUE(req_ifc.m_last_packet.get() != nullptr);
ByteBuffer net_payload;
for (const auto layer : osi_layer_range<OsiLayer::Network, OsiLayer::Application>()) {
ByteBuffer tmp;
req_ifc.m_last_packet->layer(layer).convert(tmp);
std::copy(tmp.begin(), tmp.end(), std::back_inserter(net_payload));
}
ASSERT_EQ(req_ifc.m_last_packet->size(OsiLayer::Network, OsiLayer::Application), net_payload.size());
// create up packet
std::unique_ptr<UpPacket> packet_up { new UpPacket(CohesivePacket(net_payload, OsiLayer::Network)) };
ASSERT_EQ(
size(*req_ifc.m_last_packet, min_osi_layer(), max_osi_layer()),
size(*packet_up, OsiLayer::Network)
);
// get count of current indications
const auto indications_before = ind_ifc.m_indications;
// indicate up packet
router.indicate(std::move(packet_up), {1, 2, 3, 4, 5, 6}, cBroadcastMacAddress);
EXPECT_EQ(indications_before + 1, ind_ifc.m_indications);
// get indicated packet
ASSERT_NE(nullptr, ind_ifc.m_last_packet.get());
CohesivePacket* received_payload_ptr = boost::get<CohesivePacket>(ind_ifc.m_last_packet.get());
// check expected behavior
ASSERT_NE(nullptr, received_payload_ptr);
auto received_payload_range = (*received_payload_ptr)[OsiLayer::Transport];
const ByteBuffer received_payload = ByteBuffer {
received_payload_range.begin(), received_payload_range.end()
};
EXPECT_EQ(send_payload, received_payload);
}
@@ -0,0 +1,518 @@
#include <gtest/gtest.h>
#include <vanetza/btp/header.hpp>
#include <vanetza/common/its_aid.hpp>
#include <vanetza/common/manual_runtime.hpp>
#include <vanetza/geonet/router.hpp>
#include <vanetza/geonet/tests/fake_interfaces.hpp>
#include <vanetza/geonet/tests/security_context.hpp>
#include "printer.hpp"
using namespace vanetza;
class RouterIndicate : public ::testing::Test
{
public:
RouterIndicate() :
// AT certificate is valid at this time
runtime(Clock::at("2019-11-21 14:27:53")), security(runtime), router(runtime, mib), packet_drop_occurred(false) {}
protected:
virtual void SetUp() override
{
runtime.trigger(Clock::at("2019-11-21 14:27:54"));
geonet::Address gn_addr;
gn_addr.mid(MacAddress { 0, 0, 0, 0, 0, 1});
router.set_address(gn_addr);
router.set_access_interface(&req_ifc);
router.set_transport_handler(geonet::UpperProtocol::IPv6, &ind_ifc);
router.set_security_entity(&security.entity());
packet_drop_occurred = false;
router.packet_dropped = [this](geonet::Router::PacketDropReason r) {
drop_reason = r;
packet_drop_occurred = true;
};
test_payload_trans = {47, 11, 1, 4, 42, 85};
test_payload_sess = {55, 1, 16, 45, 2, 65};
test_payload_pres = {33, 2, 6, 27, 75, 1};
send_payload.insert(send_payload.end(), test_payload_trans.begin(), test_payload_trans.end());
send_payload.insert(send_payload.end(), test_payload_sess.begin(), test_payload_sess.end());
send_payload.insert(send_payload.end(), test_payload_pres.begin(), test_payload_pres.end());
}
std::unique_ptr<geonet::DownPacket> create_packet()
{
std::unique_ptr<geonet::DownPacket> packet { new geonet::DownPacket() };
packet->layer(OsiLayer::Transport) = ByteBuffer(test_payload_trans);
packet->layer(OsiLayer::Session) = ByteBuffer(test_payload_sess);
packet->layer(OsiLayer::Presentation) = ByteBuffer(test_payload_pres);
return packet;
}
std::unique_ptr<geonet::UpPacket> get_up_packet(const ByteBuffer& sec_packet_buffer)
{
// parse the data into UpPacket
std::unique_ptr<geonet::UpPacket> up_packet(new geonet::UpPacket(CohesivePacket(sec_packet_buffer, OsiLayer::Network)));
return up_packet;
}
ByteBuffer create_secured_packet()
{
// enable security
mib.itsGnSecurity = true;
// create ShbDataRequest
geonet::ShbDataRequest request(mib, aid::CA);
request.upper_protocol = geonet::UpperProtocol::IPv6;
// Router handles request
auto confirm = router.request(request, create_packet());
assert(confirm.accepted());
// secured packet on network layer
ByteBuffer sec_packet_buffer;
req_ifc.m_last_packet->layer(OsiLayer::Network).convert(sec_packet_buffer);
assert(req_ifc.m_last_packet->size(OsiLayer::Transport, max_osi_layer()) == 0);
assert(!sec_packet_buffer.empty());
return sec_packet_buffer;
}
ByteBuffer create_plain_packet()
{
// disable security
mib.itsGnSecurity = false;
// create ShbDataRequest
geonet::ShbDataRequest request(mib, aid::CA);
request.upper_protocol = geonet::UpperProtocol::IPv6;
// Router handles request
auto confirm = router.request(request, create_packet());
assert(confirm.accepted());
// secured packet on network layer
ByteBuffer plain_packet_buffer;
for (auto layer : osi_layer_range<OsiLayer::Network, max_osi_layer()>()) {
ByteBuffer layer_buffer;
req_ifc.m_last_packet->layer(layer).convert(layer_buffer);
plain_packet_buffer.insert(plain_packet_buffer.end(), layer_buffer.begin(), layer_buffer.end());
}
assert(!plain_packet_buffer.empty());
return plain_packet_buffer;
}
bool test_and_reset_packet_drop()
{
bool result = packet_drop_occurred;
packet_drop_occurred = false;
return result;
}
MacAddress mac_address_sender = {0x01, 0x02, 0x03, 0x04, 0x05, 0x06};
MacAddress mac_address_destination = {0x07, 0x08, 0x09, 0x00, 0x01, 0x02};
geonet::ManagementInformationBase mib;
ManualRuntime runtime;
SecurityContext security;
geonet::Router router;
geonet::Router::PacketDropReason drop_reason;
FakeRequestInterface req_ifc;
FakeTransportInterface ind_ifc;
ByteBuffer test_payload_trans;
ByteBuffer test_payload_sess;
ByteBuffer test_payload_pres;
ByteBuffer send_payload;
private:
bool packet_drop_occurred;
};
TEST_F(RouterIndicate, shb_unsecured_equal_payload)
{
// create shb-up-packet by calling request
ByteBuffer sec_packet_buffer = create_plain_packet();
std::unique_ptr<geonet::UpPacket> packet_up = get_up_packet(sec_packet_buffer);
// call indicate
router.indicate(std::move(packet_up), mac_address_sender, mac_address_destination);
// check hook, it shouldn't have been called
EXPECT_FALSE(test_and_reset_packet_drop());
// check if packet was not discarded
ASSERT_NE(nullptr, ind_ifc.m_last_packet.get());
// prepare a packet to check it's payload
CohesivePacket* received_payload_ptr = boost::get<CohesivePacket>(ind_ifc.m_last_packet.get());
ASSERT_NE(nullptr, received_payload_ptr);
// extract received payload
auto received_payload_range = (*received_payload_ptr)[OsiLayer::Transport];
const ByteBuffer received_payload = ByteBuffer(received_payload_range.begin(), received_payload_range.end());
// check payload
EXPECT_EQ(send_payload, received_payload);
}
TEST_F(RouterIndicate, shb_secured_equal_payload)
{
// create shb-up-packet by calling request
ByteBuffer sec_packet_buffer = create_secured_packet();
std::unique_ptr<geonet::UpPacket> packet_up = get_up_packet(sec_packet_buffer);
// call indicate
router.indicate(std::move(packet_up), mac_address_sender, mac_address_destination);
// check hook, it shouldn't have been called
EXPECT_FALSE(test_and_reset_packet_drop()) << "Packet drop reason: " << static_cast<int>(drop_reason);
// check if packet was not discarded
ASSERT_NE(nullptr, ind_ifc.m_last_packet.get());
ASSERT_TRUE(ind_ifc.m_last_indication);
// prepare a packet to check it's payload
CohesivePacket* received_payload_ptr = boost::get<CohesivePacket>(ind_ifc.m_last_packet.get());
ASSERT_NE(nullptr, received_payload_ptr);
// extract received payload
auto received_payload_range = (*received_payload_ptr)[OsiLayer::Transport];
const ByteBuffer received_payload = ByteBuffer(received_payload_range.begin(), received_payload_range.end());
// check payload
EXPECT_EQ(send_payload, received_payload);
// check permissions are exposed correctly, these are set by NaiveCertificateProvider for the aid::CA
ASSERT_TRUE(ind_ifc.m_last_indication.get().its_aid);
ASSERT_TRUE(ind_ifc.m_last_indication.get().permissions);
EXPECT_EQ(ind_ifc.m_last_indication.get().its_aid.get(), aid::CA);
EXPECT_EQ(ind_ifc.m_last_indication.get().permissions.get(), ByteBuffer({ 1, 0, 0 }));
}
TEST_F(RouterIndicate, shb_secured_hook_its_protocol_version)
{
// modify up_packet for negative test
ByteBuffer broken_packet_buffer = create_secured_packet();
broken_packet_buffer[0] ^= 0xff;
std::unique_ptr<geonet::UpPacket> broken_packet_up = get_up_packet(broken_packet_buffer);
router.indicate(std::move(broken_packet_up), mac_address_sender, mac_address_destination);
// check hook
EXPECT_TRUE(test_and_reset_packet_drop());
EXPECT_EQ(geonet::Router::PacketDropReason::ITS_Protocol_Version, drop_reason);
// check if packet was dropped
EXPECT_EQ(nullptr, ind_ifc.m_last_packet.get());
}
TEST_F(RouterIndicate, shb_secured_hook_parse_basic_header)
{
// modify up_packet for negative test
ByteBuffer broken_packet_buffer = create_secured_packet();
broken_packet_buffer.erase(broken_packet_buffer.begin() + 3, broken_packet_buffer.end());
std::unique_ptr<geonet::UpPacket> broken_packet_up = get_up_packet(broken_packet_buffer);
router.indicate(std::move(broken_packet_up), mac_address_sender, mac_address_destination);
// check hook
EXPECT_TRUE(test_and_reset_packet_drop());
EXPECT_EQ(geonet::Router::PacketDropReason::Parse_Basic_Header, drop_reason);
// check if packet was dropped
EXPECT_EQ(nullptr, ind_ifc.m_last_packet.get());
}
TEST_F(RouterIndicate, shb_secured_hook_parse_secured_header)
{
// modify up_packet for negative test
ByteBuffer broken_packet_buffer = create_secured_packet();
broken_packet_buffer[geonet::BasicHeader::length_bytes] = 0x01;
std::unique_ptr<geonet::UpPacket> broken_packet_up = get_up_packet(broken_packet_buffer);
router.indicate(std::move(broken_packet_up), mac_address_sender, mac_address_destination);
// check hook
EXPECT_TRUE(test_and_reset_packet_drop());
EXPECT_EQ(geonet::Router::PacketDropReason::Parse_Secured_Header, drop_reason);
// check if packet was dropped
EXPECT_EQ(nullptr, ind_ifc.m_last_packet.get());
}
TEST_F(RouterIndicate, shb_secured_hook_decap_unsuccessful_non_strict)
{
mib.itsGnSnDecapResultHandling = geonet::SecurityDecapHandling::Non_Strict;
// modify up_packet for positive test
ByteBuffer broken_packet_buffer = create_secured_packet();
broken_packet_buffer[broken_packet_buffer.size() - 1] ^= 0xff;
std::unique_ptr<geonet::UpPacket> broken_packet_up = get_up_packet(broken_packet_buffer);
router.indicate(std::move(broken_packet_up), mac_address_sender, mac_address_destination);
// check hook
EXPECT_FALSE(test_and_reset_packet_drop());
// check if packet arrived at transport layer
EXPECT_EQ(1, ind_ifc.m_indications);
EXPECT_NE(nullptr, ind_ifc.m_last_packet.get());
}
TEST_F(RouterIndicate, shb_secured_hook_decap_unsuccessful_strict)
{
mib.itsGnSnDecapResultHandling = geonet::SecurityDecapHandling::Strict;
// modify up_packet for negative test
ByteBuffer broken_packet_buffer = create_secured_packet();
broken_packet_buffer[broken_packet_buffer.size() - 1] ^= 0xff;
std::unique_ptr<geonet::UpPacket> broken_packet_up = get_up_packet(broken_packet_buffer);
router.indicate(std::move(broken_packet_up), mac_address_sender, mac_address_destination);
// check hook
EXPECT_TRUE(test_and_reset_packet_drop());
EXPECT_EQ(geonet::Router::PacketDropReason::Decap_Unsuccessful_Strict, drop_reason);
// check if packet was dropped
EXPECT_EQ(nullptr, ind_ifc.m_last_packet.get());
}
TEST_F(RouterIndicate, shb_secured_hook_parse_extended_header)
{
// modify up_packet for negative test
ByteBuffer broken_packet_buffer = create_plain_packet();
// cut extended header partly off (18 bytes payload)
ASSERT_LT(32, broken_packet_buffer.size());
broken_packet_buffer.erase(broken_packet_buffer.end() - 32, broken_packet_buffer.end());
std::unique_ptr<geonet::UpPacket> broken_packet_up = get_up_packet(broken_packet_buffer);
router.indicate(std::move(broken_packet_up), mac_address_sender, mac_address_destination);
// check hook
EXPECT_TRUE(test_and_reset_packet_drop());
EXPECT_EQ(geonet::Router::PacketDropReason::Parse_Extended_Header, drop_reason);
// check if packet was dropped
EXPECT_EQ(nullptr, ind_ifc.m_last_packet.get());
}
TEST_F(RouterIndicate, shb_secured_hook_payload_size)
{
// modify up_packet for negative test
ByteBuffer broken_packet_buffer = create_plain_packet();
// cut payload partly off (18 bytes payload)
ASSERT_LT(7, broken_packet_buffer.size());
broken_packet_buffer.erase(broken_packet_buffer.end() - 7, broken_packet_buffer.end());
std::unique_ptr<geonet::UpPacket> broken_packet_up = get_up_packet(broken_packet_buffer);
router.indicate(std::move(broken_packet_up), mac_address_sender, mac_address_destination);
// check hook
EXPECT_TRUE(test_and_reset_packet_drop());
EXPECT_EQ(geonet::Router::PacketDropReason::Payload_Size, drop_reason);
// check if packet was dropped
EXPECT_EQ(nullptr, ind_ifc.m_last_packet.get());
}
TEST_F(RouterIndicate, shb_secured_hook_hop_limit)
{
// modify up_packet for negative test
ByteBuffer broken_packet_buffer = create_secured_packet();
// resest hop limit in basic header
broken_packet_buffer[geonet::BasicHeader::length_bytes - 1] = mib.itsGnDefaultHopLimit + 1;
std::unique_ptr<geonet::UpPacket> broken_packet_up = get_up_packet(broken_packet_buffer);
router.indicate(std::move(broken_packet_up), mac_address_sender, mac_address_destination);
// check hook
EXPECT_TRUE(test_and_reset_packet_drop());
EXPECT_EQ(geonet::Router::PacketDropReason::Hop_Limit, drop_reason);
// check if packet was dropped
EXPECT_EQ(nullptr, ind_ifc.m_last_packet.get());
}
TEST_F(RouterIndicate, shb_secured_hook_unsecured_packet)
{
// modify up_packet for negative test
ByteBuffer packet_buffer = create_plain_packet();
// enable security after create_plain_packet() disabled it
mib.itsGnSecurity = true;
std::unique_ptr<geonet::UpPacket> packet_up = get_up_packet(packet_buffer);
router.indicate(std::move(packet_up), mac_address_sender, mac_address_destination);
// check hook
EXPECT_TRUE(test_and_reset_packet_drop());
EXPECT_EQ(geonet::Router::PacketDropReason::Decap_Unsuccessful_Strict, drop_reason);
// check if packet was dropped
EXPECT_EQ(nullptr, ind_ifc.m_last_packet.get());
}
TEST_F(RouterIndicate, shb_secured_hook_unsecured_packet_nonstrict)
{
// modify up_packet for negative test
ByteBuffer packet_buffer = create_plain_packet();
// enable security after create_plain_packet() disabled it
mib.itsGnSecurity = true;
mib.itsGnSnDecapResultHandling = geonet::SecurityDecapHandling::Non_Strict;
std::unique_ptr<geonet::UpPacket> packet_up = get_up_packet(packet_buffer);
router.indicate(std::move(packet_up), mac_address_sender, mac_address_destination);
// check hook
EXPECT_FALSE(test_and_reset_packet_drop());
// check if packet was not discarded
ASSERT_NE(nullptr, ind_ifc.m_last_packet.get());
ASSERT_TRUE(ind_ifc.m_last_indication);
// prepare a packet to check it's payload
CohesivePacket* received_payload_ptr = boost::get<CohesivePacket>(ind_ifc.m_last_packet.get());
ASSERT_NE(nullptr, received_payload_ptr);
// extract received payload
auto received_payload_range = (*received_payload_ptr)[OsiLayer::Transport];
const ByteBuffer received_payload = ByteBuffer(received_payload_range.begin(), received_payload_range.end());
// check payload
EXPECT_EQ(send_payload, received_payload);
}
TEST_F(RouterIndicate, shb_secured_v3_message_digest)
{
mac_address_sender = MacAddress { 0xfe, 0x38, 0x4c, 0xe0, 0xb8, 0x90 };
mac_address_destination = MacAddress { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff};
const ByteBuffer gn_buffer = {
0x12, 0x00, 0x05, 0x01, 0x03, 0x81, 0x00, 0x40,
0x03, 0x80, 0x56, 0x20, 0x50, 0x02, 0x80, 0x00,
0x32, 0x01, 0x00, 0x14, 0x00, 0xfe, 0x38, 0x4c,
0xe0, 0xb8, 0x90, 0xbf, 0x6b, 0x2b, 0x74, 0x1f,
0x45, 0x28, 0x68, 0x06, 0x64, 0x09, 0x65, 0x80,
0x42, 0x03, 0xbf, 0x00, 0x00, 0xa0, 0x00, 0x07,
0xd1, 0x00, 0x00, 0x02, 0x02, 0x4c, 0xe0, 0xb8,
0x90, 0x2e, 0x6b, 0x00, 0x5a, 0x9d, 0x42, 0x2b,
0xce, 0x35, 0xbb, 0x82, 0x02, 0x3c, 0x23, 0x06,
0xda, 0x35, 0x96, 0xd4, 0x58, 0x3b, 0xe1, 0x20,
0x6d, 0x03, 0x02, 0x96, 0x8a, 0xcb, 0x33, 0xe6,
0x61, 0xff, 0xaa, 0x10, 0x3f, 0xe0, 0x14, 0x19,
0x80, 0x40, 0x01, 0x24, 0x00, 0x01, 0xc8, 0x0b,
0xba, 0xad, 0xa0, 0x64, 0x80, 0x12, 0x7c, 0xff,
0x38, 0x4c, 0xe0, 0xb8, 0x90, 0x80, 0x82, 0x9d,
0xee, 0xde, 0x15, 0x9a, 0x66, 0x08, 0x1d, 0x03,
0x6f, 0x7b, 0x28, 0x2d, 0x8f, 0xf0, 0x43, 0xc6,
0x35, 0x5f, 0x51, 0x07, 0x65, 0xb1, 0x42, 0x77,
0xb7, 0x72, 0x27, 0x15, 0x59, 0x0c, 0x9e, 0x47,
0x82, 0xfc, 0xbe, 0xe3, 0x3a, 0xbc, 0x51, 0x93,
0xfb, 0xbd, 0xa7, 0xf0, 0x4f, 0xde, 0xb2, 0xfe,
0x88, 0xa5, 0x19, 0x5e, 0xa7, 0x03, 0xca, 0xf4,
0x12, 0x21, 0x32, 0x37, 0xe8, 0x5d, 0xda
};
// gn_buffer contains a CAM using BTP-B transport
router.set_transport_handler(geonet::UpperProtocol::BTP_B, &ind_ifc);
router.set_transport_handler(geonet::UpperProtocol::IPv6, nullptr);
// message with digest will not be accepted because its certificate is unknown
EXPECT_EQ(security.certificate_cache_v3().size(), 0);
router.indicate(get_up_packet(gn_buffer), mac_address_sender, mac_address_destination);
EXPECT_TRUE(test_and_reset_packet_drop());
// add certificate manually to certificate cache
const ByteBuffer certificate_buffer = {
0x80, 0x03, 0x00, 0x80, 0x56, 0xdf, 0xd6, 0xd6,
0x27, 0xa3, 0x62, 0xdc, 0x10, 0x83, 0x00, 0x00,
0x00, 0x00, 0x00, 0x1d, 0xdf, 0xf7, 0xb5, 0x84,
0x00, 0xa8, 0x01, 0x02, 0x80, 0x01, 0x24, 0x81,
0x04, 0x03, 0x01, 0x00, 0x00, 0x80, 0x01, 0x25,
0x81, 0x05, 0x04, 0x01, 0x90, 0x1a, 0x25, 0x80,
0x80, 0x82, 0x04, 0x27, 0xbb, 0x27, 0xc9, 0x98,
0xc1, 0xec, 0xa2, 0xb1, 0x0e, 0x71, 0x07, 0x98,
0x02, 0x44, 0x51, 0x8b, 0x3c, 0x50, 0xa3, 0xa3,
0x27, 0xb5, 0xb1, 0x90, 0xd0, 0x90, 0xf1, 0x45,
0x1f, 0x3d, 0x80, 0x80, 0x83, 0xc2, 0xf3, 0xca,
0xeb, 0xc7, 0xfa, 0x35, 0x94, 0x5c, 0x03, 0x0a,
0x5a, 0xe0, 0x1a, 0x41, 0x7a, 0xdf, 0x6d, 0xff,
0xd5, 0x41, 0xcc, 0xd2, 0xd9, 0x2b, 0xfe, 0xb6,
0x3d, 0xc1, 0x56, 0x89, 0xcb, 0xd6, 0xb8, 0xe3,
0x2b, 0xd5, 0xe8, 0x66, 0xd9, 0xfa, 0xa2, 0xfe,
0x55, 0x95, 0xe2, 0xdb, 0xb9, 0xbe, 0x3e, 0x96,
0x5a, 0x70, 0x94, 0x25, 0x8b, 0x4a, 0x24, 0x9d,
0xfb, 0x75, 0x8a, 0x07
};
security::v3::Certificate certificate;
EXPECT_TRUE(certificate.decode(certificate_buffer));
security.certificate_cache_v3().store(certificate);
EXPECT_EQ(security.certificate_cache_v3().size(), 1);
// expect that same message is now accepted
router.indicate(get_up_packet(gn_buffer), mac_address_sender, mac_address_destination);
EXPECT_FALSE(test_and_reset_packet_drop()) << "Packet drop reason: " << static_cast<int>(drop_reason);
// assure that packet has been passed to transport layer
ASSERT_TRUE(ind_ifc.m_last_indication);
EXPECT_EQ(ind_ifc.m_last_indication->upper_protocol, geonet::UpperProtocol::BTP_B);
EXPECT_EQ(ind_ifc.m_last_indication->security_report, security::VerificationReport::Success);
EXPECT_EQ(ind_ifc.m_last_indication->its_aid, aid::CA);
ASSERT_TRUE(ind_ifc.m_last_packet);
}
TEST_F(RouterIndicate, shb_secured_v3_message_certificate)
{
mac_address_sender = MacAddress { 0xfe, 0x38, 0x4c, 0xe0, 0xb8, 0x90 };
mac_address_destination = MacAddress { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff};
const ByteBuffer gn_buffer = {
0x12, 0x00, 0x05, 0x01, 0x03, 0x81, 0x00, 0x40,
0x03, 0x80, 0x56, 0x20, 0x50, 0x02, 0x80, 0x00,
0x32, 0x01, 0x00, 0x14, 0x00, 0xfe, 0x38, 0x4c,
0xe0, 0xb8, 0x90, 0xbf, 0x6b, 0x33, 0x44, 0x1f,
0x45, 0x28, 0x40, 0x06, 0x64, 0x0c, 0x70, 0x81,
0xae, 0x03, 0xbd, 0x00, 0x00, 0xa0, 0x00, 0x07,
0xd1, 0x00, 0x00, 0x02, 0x02, 0x4c, 0xe0, 0xb8,
0x90, 0x35, 0x71, 0x00, 0x5a, 0x9d, 0x42, 0x25,
0xee, 0x35, 0xbb, 0xfc, 0x82, 0x4a, 0x24, 0x46,
0xd8, 0x35, 0xa3, 0x54, 0x58, 0x3b, 0xe1, 0x21,
0x00, 0x83, 0x02, 0x96, 0x8a, 0xaf, 0x33, 0xf0,
0x81, 0xfe, 0x9a, 0x10, 0x3f, 0xa0, 0x14, 0x19,
0x80, 0x40, 0x01, 0x24, 0x00, 0x01, 0xc8, 0x0b,
0xba, 0xc9, 0x16, 0xae, 0x81, 0x01, 0x01, 0x80,
0x03, 0x00, 0x80, 0x56, 0xdf, 0xd6, 0xd6, 0x27,
0xa3, 0x62, 0xdc, 0x10, 0x83, 0x00, 0x00, 0x00,
0x00, 0x00, 0x1d, 0xdf, 0xf7, 0xb5, 0x84, 0x00,
0xa8, 0x01, 0x02, 0x80, 0x01, 0x24, 0x81, 0x04,
0x03, 0x01, 0x00, 0x00, 0x80, 0x01, 0x25, 0x81,
0x05, 0x04, 0x01, 0x90, 0x1a, 0x25, 0x80, 0x80,
0x82, 0x04, 0x27, 0xbb, 0x27, 0xc9, 0x98, 0xc1,
0xec, 0xa2, 0xb1, 0x0e, 0x71, 0x07, 0x98, 0x02,
0x44, 0x51, 0x8b, 0x3c, 0x50, 0xa3, 0xa3, 0x27,
0xb5, 0xb1, 0x90, 0xd0, 0x90, 0xf1, 0x45, 0x1f,
0x3d, 0x80, 0x80, 0x83, 0xc2, 0xf3, 0xca, 0xeb,
0xc7, 0xfa, 0x35, 0x94, 0x5c, 0x03, 0x0a, 0x5a,
0xe0, 0x1a, 0x41, 0x7a, 0xdf, 0x6d, 0xff, 0xd5,
0x41, 0xcc, 0xd2, 0xd9, 0x2b, 0xfe, 0xb6, 0x3d,
0xc1, 0x56, 0x89, 0xcb, 0xd6, 0xb8, 0xe3, 0x2b,
0xd5, 0xe8, 0x66, 0xd9, 0xfa, 0xa2, 0xfe, 0x55,
0x95, 0xe2, 0xdb, 0xb9, 0xbe, 0x3e, 0x96, 0x5a,
0x70, 0x94, 0x25, 0x8b, 0x4a, 0x24, 0x9d, 0xfb,
0x75, 0x8a, 0x07, 0x80, 0x82, 0xf4, 0x4c, 0xc3,
0xc3, 0xb1, 0x0c, 0xf7, 0x7c, 0xd9, 0x0c, 0x40,
0xfe, 0xe7, 0x30, 0x40, 0xad, 0x0b, 0xb4, 0xf8,
0x34, 0x55, 0x81, 0x37, 0xa6, 0x96, 0x81, 0x78,
0xe0, 0x53, 0x09, 0x06, 0xf7, 0x4f, 0x14, 0x43,
0x46, 0x88, 0x29, 0x6e, 0x22, 0xfe, 0xbb, 0x6f,
0x8e, 0x21, 0xad, 0x51, 0x7e, 0xb0, 0x81, 0x9a,
0x39, 0xf2, 0xaa, 0xd3, 0x37, 0x51, 0xf3, 0xab,
0xde, 0xdd, 0x69, 0xfe, 0xaf
};
// gn_buffer contains a CAM using BTP-B transport
router.set_transport_handler(geonet::UpperProtocol::BTP_B, &ind_ifc);
router.set_transport_handler(geonet::UpperProtocol::IPv6, nullptr);
router.indicate(get_up_packet(gn_buffer), mac_address_sender, mac_address_destination);
// assure that packet has not been dropped
EXPECT_FALSE(test_and_reset_packet_drop()) << "Packet drop reason: " << static_cast<int>(drop_reason);
// assure that packet has been passed to transport layer
ASSERT_TRUE(ind_ifc.m_last_indication);
EXPECT_EQ(ind_ifc.m_last_indication->upper_protocol, geonet::UpperProtocol::BTP_B);
EXPECT_EQ(ind_ifc.m_last_indication->security_report, security::VerificationReport::Success);
EXPECT_EQ(ind_ifc.m_last_indication->its_aid, aid::CA);
ASSERT_TRUE(ind_ifc.m_last_packet);
}
@@ -0,0 +1,200 @@
#include <gtest/gtest.h>
#include <vanetza/common/its_aid.hpp>
#include <vanetza/common/manual_runtime.hpp>
#include <vanetza/geonet/tests/fake_interfaces.hpp>
#include <vanetza/geonet/tests/security_context.hpp>
#include <vanetza/geonet/pdu_conversion.hpp>
#include <vanetza/geonet/pdu_variant.hpp>
#include <vanetza/geonet/router.hpp>
#include <vanetza/geonet/serialization_buffer.hpp>
#include <boost/variant/get.hpp>
using namespace vanetza;
using namespace vanetza::geonet;
class RouterRequest : public ::testing::Test
{
public:
RouterRequest() :
security(runtime), router(runtime, mib) {}
protected:
virtual void SetUp() override
{
mib.itsGnSecurity = true;
router.set_access_interface(&req_ifc);
router.set_security_entity(&security.entity());
router.set_transport_handler(geonet::UpperProtocol::IPv6, &ind_ifc);
test_payload[OsiLayer::Application] = ByteBuffer {47, 11, 1, 4, 42, 85};
}
std::unique_ptr<geonet::DownPacket> create_packet()
{
std::unique_ptr<DownPacket> packet { new DownPacket(test_payload) };
return packet;
}
ManagementInformationBase mib;
ManualRuntime runtime;
SecurityContext security;
Router router;
FakeRequestInterface req_ifc;
FakeTransportInterface ind_ifc;
ChunkPacket test_payload;
};
TEST_F(RouterRequest, router_request)
{
// create ShbDataRequest
ShbDataRequest request(mib, aid::CA);
request.upper_protocol = UpperProtocol::IPv6;
// Router handles request
auto confirm = router.request(request, create_packet());
EXPECT_TRUE(confirm.accepted());
// get the data from the fake network
ByteBuffer net_payload;
for (const auto layer : osi_layer_range<OsiLayer::Network, OsiLayer::Application>()) {
ByteBuffer tmp;
req_ifc.m_last_packet->layer(layer).convert(tmp);
std::copy(tmp.begin(), tmp.end(), std::back_inserter(net_payload));
}
UpPacket packet_up { CohesivePacket(net_payload, OsiLayer::Network) };
DownPacket packet_mac = *req_ifc.m_last_packet;
// all data should be in network layer: payload is encapsulated by secured message
EXPECT_EQ(packet_mac.size(), packet_mac[OsiLayer::Network].size());
// prepare access to network layer's PDU
using pdu_convertible = convertible::byte_buffer_impl<std::unique_ptr<Pdu>>;
pdu_convertible* pdu_conv = dynamic_cast<pdu_convertible*>(packet_mac[OsiLayer::Network].ptr());
ASSERT_TRUE(pdu_conv);
auto pdu = pdu_conv->m_pdu.get();
ASSERT_TRUE(pdu);
auto pdu_ext = dynamic_cast<ShbPdu*>(pdu);
ASSERT_TRUE(pdu_ext);
// check if packet has secured part
EXPECT_EQ(NextHeaderBasic::Secured, pdu->basic().next_header);
EXPECT_TRUE(pdu_ext->secured());
auto secured = *pdu_ext->secured();
// check payload of packet
auto secured2 = boost::get<security::v2::SecuredMessage>(&secured);
ASSERT_TRUE(secured2);
EXPECT_EQ(security::v2::PayloadType::Signed, secured2->payload.type);
EXPECT_EQ(test_payload.size(), pdu->common().payload);
const size_t payload_header_length = CommonHeader::length_bytes + ShbHeader::length_bytes;
EXPECT_EQ(payload_header_length, size(secured2->payload.data, OsiLayer::Network));
EXPECT_EQ(test_payload.size(), size(secured2->payload.data, OsiLayer::Transport, OsiLayer::Application));
ChunkPacket sec_payload = boost::get<ChunkPacket>(secured2->payload.data);
ChunkPacket sec_header = sec_payload.extract(OsiLayer::Network, OsiLayer::Network);
ByteBuffer actual_payload, expected_payload;
serialize_into_buffer(sec_payload, actual_payload);
serialize_into_buffer(test_payload, expected_payload);
EXPECT_EQ(expected_payload, actual_payload);
ByteBuffer actual_payload_header, expected_payload_header;
geonet::serialize_into_buffer(pdu_ext->common(), expected_payload_header);
geonet::serialize_into_buffer(pdu_ext->extended(), expected_payload_header);
sec_header[OsiLayer::Network].convert(actual_payload_header);
EXPECT_EQ(expected_payload_header, actual_payload_header);
}
TEST_F(RouterRequest, modified_request_maximum_lifetime)
{
// create ShbDataRequest
ShbDataRequest request(mib, aid::CA);
// create new Lifetime that is larger than the itsGnMaxPacketLifetime of mib
Lifetime large_lifetime(Lifetime::Base::Hundred_Seconds, 9);
request.maximum_lifetime = large_lifetime;
request.upper_protocol = UpperProtocol::IPv6;
// Router handles request
auto confirm = router.request(request, create_packet());
EXPECT_EQ(DataConfirm::ResultCode::Rejected_Max_Lifetime, confirm.result_code);
}
TEST_F(RouterRequest, modified_request_repetition)
{
// create ShbDataRequest
ShbDataRequest request(mib, aid::CA);
// create durations that will fail in data_confirm
auto rep_faulty_int = 0.0 * units::si::seconds; // this has to be lower than mib.itsGnMinPacketRepetitionInterval
auto rep_max = 99.0 * units::si::seconds;
// create Repetition with faulty interval
DataRequest::Repetition rep;
rep.interval = rep_faulty_int;
rep.maximum = rep_max;
request.repetition = rep;
request.upper_protocol = UpperProtocol::IPv6;
// Router handles request
auto confirm = router.request(request, create_packet());
EXPECT_EQ(DataConfirm::ResultCode::Rejected_Min_Repetition_Interval, confirm.result_code);
}
TEST_F(RouterRequest, modified_request_payload_null)
{
// create ShbDataRequest
ShbDataRequest request(mib, aid::CA);
request.upper_protocol = UpperProtocol::IPv6;
// Router handles request
auto confirm = router.request(request, nullptr);
EXPECT_EQ(DataConfirm::ResultCode::Rejected_Unspecified, confirm.result_code);
}
TEST_F(RouterRequest, modified_request_large_payload)
{
std::unique_ptr<geonet::DownPacket> packet { new geonet::DownPacket() };
// create too large payload
ByteBuffer payload_large;
for (int i = 0; i < 1000; i++) {
ByteBuffer tmp = {0,1,2,3,4,5,6,7,8,9};
payload_large.insert(payload_large.end(), tmp.begin(), tmp.end());
}
// insert payload in packet
packet->layer(OsiLayer::Transport) = ByteBuffer(payload_large);
// create ShbDataRequest
ShbDataRequest request(mib, aid::CA);
request.upper_protocol = UpperProtocol::IPv6;
// Router handles request
auto confirm = router.request(request, std::move(packet));
EXPECT_EQ(DataConfirm::ResultCode::Rejected_Max_SDU_Size, confirm.result_code);
}
TEST_F(RouterRequest, shb_repetition)
{
ShbDataRequest request(mib, aid::CA);
request.repetition = DataRequest::Repetition {
0.1 * units::si::seconds, 10.0 * units::si::seconds
};
EXPECT_EQ(0, req_ifc.m_requests);
auto confirm = router.request(request, create_packet());
EXPECT_EQ(DataConfirm::ResultCode::Accepted, confirm.result_code);
EXPECT_EQ(1, req_ifc.m_requests);
ASSERT_TRUE(!!req_ifc.m_last_packet);
// length of network layer is excluded because of varying secured message header fields
const auto payload_size = size(*req_ifc.m_last_packet, OsiLayer::Transport, OsiLayer::Application);
// trigger five repetitions
for (unsigned i = 0; i < 5; ++i) {
runtime.trigger(std::chrono::milliseconds(100));
}
EXPECT_EQ(6, req_ifc.m_requests);
EXPECT_EQ(payload_size, size(*req_ifc.m_last_packet, OsiLayer::Transport, OsiLayer::Application));
}
@@ -0,0 +1,579 @@
#include <vanetza/geonet/data_confirm.hpp>
#include <vanetza/geonet/data_request.hpp>
#include <vanetza/geonet/packet.hpp>
#include <vanetza/geonet/tests/network_topology.hpp>
#include <vanetza/net/mac_address.hpp>
#include <gtest/gtest.h>
#include <list>
#include <tuple>
using namespace vanetza;
using namespace vanetza::geonet;
// user literal for convenient length definition
vanetza::units::Length operator""_m(long double length)
{
return vanetza::units::Length(length * vanetza::units::si::meters);
}
using RoutingParam = std::tuple<NetworkTopology::PacketDuplicationMode, bool>;
class Routing : public ::testing::TestWithParam<RoutingParam>
{
protected:
virtual void SetUp() override
{
net.set_duplication_mode(std::get<0>(GetParam()));
net.get_mib().itsGnNonAreaForwardingAlgorithm = UnicastForwarding::Greedy;
net.get_mib().itsGnAreaForwardingAlgorithm = BroadcastForwarding::Advanced;
net.get_mib().vanetzaCbfMaxCounter = 3;
net.get_mib().itsGnSecurity = std::get<1>(GetParam());
cars[0] = {0x00, 0x02, 0x03, 0x04, 0x05, 0x06};
cars[1] = {0x01, 0x02, 0x03, 0x04, 0x05, 0x06};
cars[2] = {0x02, 0x02, 0x03, 0x04, 0x05, 0x06};
cars[3] = {0x03, 0x02, 0x03, 0x04, 0x05, 0x06};
cars[4] = {0x04, 0x02, 0x03, 0x04, 0x05, 0x06};
cars[5] = {0x05, 0x02, 0x03, 0x04, 0x05, 0x06};
// add all routers
for (auto& car : cars) {
net.add_router(car.second);
}
// add reachability for all routers
net.add_reachability(cars[0], {cars[1], cars[2], cars[3], cars[5]});
net.add_reachability(cars[1], {cars[0], cars[2]});
net.add_reachability(cars[2], {cars[0], cars[1], cars[3], cars[5]});
net.add_reachability(cars[3], {cars[0], cars[2], cars[4]});
net.add_reachability(cars[4], {cars[3]});
net.add_reachability(cars[5], {cars[2], cars[0]});
// positioning of cars
net.set_position(cars[0], CartesianPosition(0.0_m, 0.0_m));
net.set_position(cars[1], CartesianPosition(2.0_m, 0.0_m));
net.set_position(cars[2], CartesianPosition(6.0_m, 0.0_m));
net.set_position(cars[3], CartesianPosition(6.0_m, 4.0_m));
net.set_position(cars[4], CartesianPosition(20.0_m, 4.0_m));
net.set_position(cars[5], CartesianPosition(2.0_m, -1.0_m));
/**
* [rough map] (3) (4)
*
*
*
* -----
* (0) (1) (2)
* -----
* (5)
*/
// advance time so Beacons have been exchanged
net.advance_time(std::chrono::seconds::zero());
net.reset_counters();
}
std::unique_ptr<DownPacket> create_packet(ByteBuffer&& payload = {47, 11, 1, 4, 42, 85})
{
std::unique_ptr<DownPacket> packet { new DownPacket() };
packet->layer(OsiLayer::Transport) = ByteBuffer(std::move(payload));
return packet;
}
std::unordered_map<int, MacAddress> cars;
NetworkTopology net;
};
/**
* Check location table entries after initialisation
* Expectation: Entries should reflect defined network reachability
*/
TEST_P(Routing, beacon_location_table)
{
auto& sender_table = net.get_router(cars[0])->get_location_table();
EXPECT_FALSE(sender_table.has_entry(Address { cars[0] }));
EXPECT_TRUE(sender_table.has_entry(Address { cars[1] }));
EXPECT_TRUE(sender_table.has_entry(Address { cars[2] }));
EXPECT_TRUE(sender_table.has_entry(Address { cars[3] }));
EXPECT_FALSE(sender_table.has_entry(Address { cars[4] }));
ASSERT_TRUE(sender_table.has_entry(Address { cars[5] }));
const LocationTableEntry* entry5 = sender_table.get_entry(Address { cars[5] });
ASSERT_TRUE(entry5);
EXPECT_LT(0, entry5->get_position_vector().longitude.value());
EXPECT_GT(0, entry5->get_position_vector().latitude.value());
}
/**
* No GN Beacon shall ever be transmitted when beaconing has been disabled explicitly.
*/
TEST_P(Routing, disabled_beaconing)
{
net.get_mib().vanetzaDisableBeaconing = true;
net.advance_time(std::chrono::minutes(1));
EXPECT_EQ(0, net.get_interface(cars[0])->requests);
EXPECT_EQ(0, size(net.get_router(cars[0])->get_location_table().neighbours()));
}
/*
* Preconditions:
* - source router inside destination area
* - packet not yet in CBF packet buffer (P not in B)
* Expectation: immediate broadcast (area forwarding, not greedy forwarding)
*/
TEST_P(Routing, advanced_forwarding_source_inside_destination)
{
GbcDataRequest gbc_request(net.get_mib());
gbc_request.destination = circle_dest_area(3.0_m, 2.0_m, 0.0_m);
gbc_request.upper_protocol = UpperProtocol::IPv6;
auto confirm = net.get_router(cars[0])->request(gbc_request, create_packet());
ASSERT_TRUE(confirm.accepted());
EXPECT_EQ(1, net.get_interface(cars[0])->requests);
EXPECT_EQ(cBroadcastMacAddress, net.get_interface(cars[0])->last_request.destination);
}
/*
* Preconditions:
* - receiving router inside destination area (forwarder operations)
* - packet not yet in CBF packet buffer (P not in B)
* - LL address of receiver is not LL destination address
* Expectation: contention based forwarding by receiver
*/
TEST_P(Routing, advanced_forwarding_receiver_inside_destination_cbf)
{
GbcDataRequest gbc_request(net.get_mib(), aid::IPV6_ROUTING);
gbc_request.destination = circle_dest_area(5.0_m, 2.0_m, 0.0_m);
gbc_request.upper_protocol = UpperProtocol::IPv6;
auto confirm = net.get_router(cars[1])->request(gbc_request, create_packet());
ASSERT_TRUE(confirm.accepted());
EXPECT_EQ(1, net.get_interface(cars[1])->requests);
EXPECT_EQ(cBroadcastMacAddress, net.get_interface(cars[1])->last_request.destination);
net.dispatch();
// node 1 (source) broadcasted to reachable nodes 0 and 2
EXPECT_EQ(1, net.get_transport(cars[0])->counter);
EXPECT_EQ(1, net.get_transport(cars[2])->counter);
EXPECT_EQ(0, net.get_transport(cars[5])->counter);
// nodes 0 and 2 have not forwarded anything yet
EXPECT_EQ(0, net.get_interface(cars[0])->requests);
EXPECT_EQ(0, net.get_interface(cars[2])->requests);
// node 2 forwards first (CBF timer ~99.6ms)
net.advance_time(std::chrono::microseconds(99650));
EXPECT_EQ(1, net.get_interface(cars[2])->requests);
EXPECT_EQ(0, net.get_interface(cars[0])->requests);
// node 0 forwards second (initial CBF timer ~99.8ms)
// CBF timer (~99.4ms) of node 0 has been restarted by node 2's forwarding !
// Note: node 0 is outside sectorial area of node 1 (source) and node 2 (forwarder)
net.advance_time(std::chrono::microseconds(200));
EXPECT_EQ(0, net.get_interface(cars[0])->requests);
net.advance_time(std::chrono::microseconds(99450));
EXPECT_EQ(1, net.get_interface(cars[0])->requests);
// make sure forwarding was to broadcast address
EXPECT_EQ(cBroadcastMacAddress, net.get_interface(cars[0])->last_request.destination);
// node 5 received packet twice by now
EXPECT_EQ(2, net.get_transport(cars[5])->counter);
// nodes 3 and 4 are outside of destination area
EXPECT_EQ(0, net.get_transport(cars[3])->counter);
EXPECT_EQ(0, net.get_transport(cars[4])->counter);
}
/*
* Preconditions:
* - source and receiver are inside destination area
* - source and sender are identical -> receiver is "outside" sectorial area
* - packet is is addded to CBF packet buffer
* Expectations:
* - remove packet from buffer when counter limit is reached
* - stop timer
* - discard packet
*/
TEST_P(Routing, advanced_forwarding_max_counter_exceeded)
{
GbcDataRequest gbc_request(net.get_mib(), aid::IPV6_ROUTING);
gbc_request.destination = circle_dest_area(3.0_m, 2.0_m, 0.0_m);
gbc_request.upper_protocol = UpperProtocol::IPv6;
auto confirm = net.get_router(cars[0])->request(gbc_request, create_packet());
ASSERT_TRUE(confirm.accepted());
EXPECT_EQ(cBroadcastMacAddress, net.get_interface(cars[0])->last_request.destination);
net.dispatch();
auto& car1_cbf = net.get_router(cars[1])->get_cbf_buffer();
auto found = car1_cbf.find(identifier(Address { cars[0] }, SequenceNumber(0)));
ASSERT_TRUE(found);
EXPECT_EQ(1, car1_cbf.counter(identifier(*found)));
const int max_counter = net.get_mib().vanetzaCbfMaxCounter;
for (int i = 1; i < max_counter; ++i) {
// repeat (transmit & dispatch) car0's last link layer transmission
net.get_interface(cars[0])->transmit();
net.dispatch();
auto found = car1_cbf.find(identifier(Address { cars[0] }, SequenceNumber(0)));
ASSERT_TRUE(found);
EXPECT_EQ(i + 1, car1_cbf.counter(identifier(*found)));
}
// repeat (transmit & dispatch) car0's last link layer transmission
net.get_interface(cars[0])->transmit();
net.dispatch();
found = net.get_router(cars[1])->get_cbf_buffer().find(identifier(Address { cars[0] }, SequenceNumber(0)));
EXPECT_FALSE(found);
}
/**
* Preconditions:
* - source is outside destination area
* - receiver is inside destination area
* Expectations:
* - receiver adds packet to CBF buffer
* - receiver forwards packet immediately (received via GF)
* - receiver does not broadcast packet again after CBF max time
*/
TEST_P(Routing, advanced_forwarding_avoid_double_broadcast)
{
// greedy forwarding stops at car 1 (optimum) -> broadcast
auto& car1_cbf = net.get_router(cars[1])->get_cbf_buffer();
auto& car1_ifc = net.get_interface(cars[1]).get();
ASSERT_EQ(0, car1_ifc.requests);
ASSERT_FALSE(car1_cbf.find(identifier(Address { cars[0] }, SequenceNumber(0))));
GbcDataRequest gbc_request(net.get_mib(), aid::IPV6_ROUTING);
gbc_request.destination = circle_dest_area(1.0_m, 2.0_m, 0.0_m);
gbc_request.upper_protocol = UpperProtocol::IPv6;
auto confirm = net.get_router(cars[0])->request(gbc_request, create_packet());
ASSERT_TRUE(confirm.accepted());
net.dispatch();
// GBC has been sent by source using greedy forwarding (GF)
EXPECT_EQ(cars[1], net.get_interface(cars[0])->last_request.destination);
// receiver has enqueued packet in its CBF buffer
ASSERT_TRUE(car1_cbf.find(identifier(Address { cars[0] }, SequenceNumber(0))));
// receiver forwarded packet immediately
EXPECT_EQ(1, car1_ifc.requests);
// no further forwarding by receiver
net.advance_time(units::clock_cast(net.get_mib().itsGnCbfMaxTime));
EXPECT_FALSE(car1_cbf.find(identifier(Address { cars[0] }, SequenceNumber(0))));
EXPECT_EQ(1, car1_ifc.requests);
}
/*
* Preconditions:
* - source (0), forwarder (5) and receiver (2) inside destination area
* - distinct source and forwarder spanning sectorial area
* - receiver inside of sectorial area
* - packet in CBF packet buffer (P in B)
* Expectation: remove packet from buffer, stop timer, discard packet
*/
TEST_P(Routing, advanced_forwarding_inside_sectorial_area)
{
EXPECT_FALSE(net.get_router(cars[5])->outside_sectorial_contention_area(cars[0], cars[2]));
GbcDataRequest gbc_request(net.get_mib(), aid::IPV6_ROUTING);
gbc_request.destination = circle_dest_area(7.0_m, 0.0_m, 0.0_m);
gbc_request.upper_protocol = UpperProtocol::IPv6;
auto confirm = net.get_router(cars[0])->request(gbc_request, create_packet());
ASSERT_TRUE(confirm.accepted());
EXPECT_EQ(cBroadcastMacAddress, net.get_interface(cars[0])->last_request.destination);
net.dispatch();
// receiver contends on first packet reception (precondition)
auto& cbf5 = net.get_router(cars[5])->get_cbf_buffer();
auto found = cbf5.find(identifier(Address { cars[0] }, SequenceNumber(0)));
ASSERT_TRUE(found);
EXPECT_EQ(1, cbf5.counter(identifier(*found)));
// forwarder's timer expires after ~99.4 ms
ASSERT_EQ(0, net.get_interface(cars[2])->requests);
net.advance_time(std::chrono::microseconds(99450));
EXPECT_EQ(1, net.get_interface(cars[2])->requests);
// receiver is inside sectorial area and stops contending
found = net.get_router(cars[5])->get_cbf_buffer().find(identifier(Address { cars[0] }, SequenceNumber(0)));
EXPECT_FALSE(found);
}
/*
* Preconditions:
* - source (0), forwarder (5) and receiver (2) inside destination area
* - distinct source and forwarder spanning sectorial area
* - receiver outside of sectorial area
* - packet in CBF packet buffer (P in B)
* Expectation: packet is buffered with incremented counter
*/
TEST_P(Routing, advanced_forwarding_outside_sectorial_area)
{
net.set_position(cars[5], CartesianPosition(2.0_m, -2.0_m));
net.advance_time(std::chrono::seconds(5)); /*< let Beacons update location tables */
net.reset_counters();
EXPECT_TRUE(net.get_router(cars[5])->outside_sectorial_contention_area(cars[0], cars[2]));
GbcDataRequest gbc_request(net.get_mib(), aid::IPV6_ROUTING);
gbc_request.destination = circle_dest_area(7.0_m, 0.0_m, 0.0_m);
gbc_request.upper_protocol = UpperProtocol::IPv6;
auto confirm = net.get_router(cars[0])->request(gbc_request, create_packet());
ASSERT_TRUE(confirm.accepted());
EXPECT_EQ(cBroadcastMacAddress, net.get_interface(cars[0])->last_request.destination);
net.dispatch();
// receiver contends on first packet reception (precondition)
auto& cbf5 = net.get_router(cars[5])->get_cbf_buffer();
auto found = cbf5.find(identifier(Address { cars[0] }, SequenceNumber(0)));
ASSERT_TRUE(found);
EXPECT_EQ(1, cbf5.counter(identifier(*found)));
// forwarder's timer expires after ~99.4 ms
ASSERT_EQ(0, net.get_interface(cars[2])->requests);
net.advance_time(std::chrono::microseconds(99450));
EXPECT_EQ(1, net.get_interface(cars[2])->requests);
// receiver is outside sectorial area and increments counter
found = cbf5.find(identifier(Address { cars[0] }, SequenceNumber(0)));
ASSERT_TRUE(found);
EXPECT_EQ(2, cbf5.counter(identifier(*found)));
}
/*
* Preconditions:
* - source (1) is inside destination area
* - sender (2), forwarder (0), and receiver (5) as well
* note: receiver (5) gets packet from (2) for the first time
* - receiver is in sectorial area of sender (2) and forwarder (0)
* - sender is different to GBC source
* Expectation: (5) removes packet from buffer, stops timer, discards packet
*/
TEST_P(Routing, advanced_routing_distinct_sender_sectorial_area)
{
EXPECT_FALSE(net.get_router(cars[5])->outside_sectorial_contention_area(cars[2], cars[0]));
GbcDataRequest gbc_request(net.get_mib(), aid::IPV6_ROUTING);
gbc_request.destination = circle_dest_area(4.5_m, 2.0_m, 0.0_m);
gbc_request.upper_protocol = UpperProtocol::IPv6;
auto confirm = net.get_router(cars[1])->request(gbc_request, create_packet());
ASSERT_TRUE(confirm.accepted());
EXPECT_EQ(cBroadcastMacAddress, net.get_interface(cars[1])->last_request.destination);
net.dispatch();
// sender forwards after ~99.6 ms -> receivers starts contending
net.advance_time(std::chrono::microseconds(99650));
EXPECT_EQ(1, net.get_interface(cars[2])->requests);
EXPECT_EQ(0, net.get_interface(cars[0])->requests);
auto found = net.get_router(cars[5])->get_cbf_buffer().find(identifier(Address { cars[1] }, SequenceNumber(0)));
ASSERT_TRUE(found);
EXPECT_EQ(1, net.get_router(cars[5])->get_cbf_buffer().counter(identifier(*found)));
// forwarder's timer expires after ~99.4 ms
EXPECT_EQ(0, net.get_interface(cars[0])->requests);
net.advance_time(std::chrono::microseconds(99450));
EXPECT_EQ(1, net.get_interface(cars[0])->requests);
// receiver stopped contending
found = net.get_router(cars[5])->get_cbf_buffer().find(identifier(Address { cars[1] }, SequenceNumber(0)));
EXPECT_FALSE(found);
}
/*
* Preconditions:
* - source outside target area (non-area forwarding)
* - source has known neighbours with progress to destination
* Expectation: unicast greedy forwarding
*/
TEST_P(Routing, greedy_forwarding_unicast)
{
GbcDataRequest gbc_request(net.get_mib(), aid::IPV6_ROUTING);
gbc_request.destination = circle_dest_area(1.0_m, 2.0_m, 2.0_m);
gbc_request.upper_protocol = UpperProtocol::IPv6;
auto confirm = net.get_router(cars[0])->request(gbc_request, create_packet());
ASSERT_TRUE(confirm.accepted());
EXPECT_EQ(cars[1], net.get_interface(cars[0])->last_request.destination);
gbc_request.destination = circle_dest_area(1.0_m, 6.0_m, -2.0_m);
confirm = net.get_router(cars[0])->request(gbc_request, create_packet());
ASSERT_TRUE(confirm.accepted());
EXPECT_EQ(cars[2], net.get_interface(cars[0])->last_request.destination);
gbc_request.destination = circle_dest_area(1.0_m, 6.0_m, 8.0_m);
confirm = net.get_router(cars[0])->request(gbc_request, create_packet());
ASSERT_TRUE(confirm.accepted());
EXPECT_EQ(cars[3], net.get_interface(cars[0])->last_request.destination);
gbc_request.destination = circle_dest_area(1.0_m, 2.0_m, 2.0_m);
confirm = net.get_router(cars[0])->request(gbc_request, create_packet());
ASSERT_TRUE(confirm.accepted());
EXPECT_EQ(cars[1], net.get_interface(cars[0])->last_request.destination);
gbc_request.destination = circle_dest_area(1.0_m, 20.0_m, 0.0_m);
confirm = net.get_router(cars[0])->request(gbc_request, create_packet());
ASSERT_TRUE(confirm.accepted());
EXPECT_EQ(cars[2], net.get_interface(cars[0])->last_request.destination);
}
/*
* Preconditions:
* - source outside target area (non-area forwarding)
* - no known neighbour with progress towards destination
* - traffic class has SCF disabled
* Expectation: broadcast
*/
TEST_P(Routing, greedy_forwarding_broadcast)
{
net.get_mib().itsGnDefaultTrafficClass.store_carry_forward(false);
GbcDataRequest gbc_request(net.get_mib(), aid::IPV6_ROUTING);
gbc_request.upper_protocol = UpperProtocol::IPv6;
gbc_request.destination = circle_dest_area(1.0_m, -2.0_m, 0.0_m);
auto confirm = net.get_router(cars[0])->request(gbc_request, create_packet());
ASSERT_TRUE(confirm.accepted());
EXPECT_EQ(cBroadcastMacAddress, net.get_interface(cars[0])->last_request.destination);
}
/*
* Preconditions:
* - source outside target area (non-area forwarding)
* - no known neighbour with progress towards destination
* - traffic class has SCF enabled
* Expectation: queue packet in broadcast buffer
*/
TEST_P(Routing, greedy_forwarding_scf)
{
net.get_mib().itsGnDefaultTrafficClass.store_carry_forward(true);
GbcDataRequest gbc_request(net.get_mib(), aid::IPV6_ROUTING);
gbc_request.upper_protocol = UpperProtocol::IPv6;
gbc_request.destination = circle_dest_area(1.0_m, -2.0_m, 0.0_m);
auto confirm = net.get_router(cars[0])->request(gbc_request, create_packet());
ASSERT_TRUE(confirm.accepted());
EXPECT_EQ(0, net.get_interface(cars[0])->requests);
// let's age the the lifetime of the buffered packet a little bit
net.advance_time(units::clock_cast(net.get_mib().itsGnDefaultPacketLifetime.decode() * 0.5));
net.reset_counters(); /*< ignore Beacon transmissions */
// move one station to become a forwarder and propagate its new position via SHB
net.set_position(cars[5], CartesianPosition(-1.0_m, 0.0_m));
ShbDataRequest shb_request(net.get_mib(), aid::IPV6_ROUTING);
shb_request.upper_protocol = UpperProtocol::IPv6;
ASSERT_TRUE(net.get_router(cars[5])->request(shb_request, create_packet()).accepted());
EXPECT_EQ(0, net.get_interface(cars[0])->requests);
EXPECT_EQ(1, net.get_interface(cars[5])->requests);
net.dispatch(); /*< dispatches SHB */
// need to trigger common header processing again
EXPECT_EQ(0, net.get_interface(cars[0])->requests);
net.get_router(cars[5])->request(shb_request, create_packet());
net.dispatch();
// now SCF buffered packet should be forwarded
EXPECT_EQ(1, net.get_interface(cars[0])->requests);
EXPECT_EQ(cars[5], net.get_interface(cars[0])->last_request.destination);
}
/*
* Preconditions:
* - receiver outside target area
* - sender inside target area
* - position of sender is accurate (PAI)
* Expectation: receivers located outside discard packet
*/
TEST_P(Routing, forwarding_selection_discard)
{
GbcDataRequest gbc_request(net.get_mib(), aid::IPV6_ROUTING);
gbc_request.destination = circle_dest_area(3.0_m, 0.0_m, 0.0_m);
gbc_request.upper_protocol = UpperProtocol::IPv6;
auto confirm = net.get_router(cars[0])->request(gbc_request, create_packet());
ASSERT_TRUE(confirm.accepted());
net.dispatch();
// all four neighbours of car0 received the GBC packet
EXPECT_EQ(4, net.get_counter_indications());
// but only 1 and 5 buffer the packet (i.e. they are inside target area)
auto found1 = net.get_router(cars[1])->get_cbf_buffer().find(identifier(Address { cars[0] }, SequenceNumber(0)));
EXPECT_TRUE(found1);
auto found5 = net.get_router(cars[5])->get_cbf_buffer().find(identifier(Address { cars[0] }, SequenceNumber(0)));
EXPECT_TRUE(found5);
// nodes 2 and 3 have not buffered packet and did no non-area forwarding either
auto found2 = net.get_router(cars[2])->get_cbf_buffer().find(identifier(Address { cars[0] }, SequenceNumber(0)));
EXPECT_FALSE(found2);
EXPECT_EQ(0, net.get_interface(cars[2])->requests);
auto found3 = net.get_router(cars[3])->get_cbf_buffer().find(identifier(Address { cars[0] }, SequenceNumber(0)));
EXPECT_FALSE(found3);
EXPECT_EQ(0, net.get_interface(cars[3])->requests);
}
/*
* Preconditions:
* - receiver outside target area
* - sender inside target area
* - position of sender is not accurate (!PAI)
* Expectation: receivers located outside start area forwarding
*/
TEST_P(Routing, forwarding_selection_inaccurate_position)
{
net.get_host(cars[0])->set_position_accuracy_indicator(false);
net.advance_time(std::chrono::seconds(4));
net.reset_counters();
GbcDataRequest gbc_request(net.get_mib(), aid::IPV6_ROUTING);
gbc_request.destination = circle_dest_area(3.0_m, 0.0_m, 0.0_m);
gbc_request.upper_protocol = UpperProtocol::IPv6;
auto confirm = net.get_router(cars[0])->request(gbc_request, create_packet());
ASSERT_TRUE(confirm.accepted());
net.dispatch();
// all four neighbours of car0 received the GBC packet
EXPECT_EQ(4, net.get_counter_indications());
// nodes 2 and 3 start greedy forwarding (they are unsure about sender's position)
// (greedy forwarding does not care about PAI, so car[0] is a valid selection)
EXPECT_EQ(1, net.get_interface(cars[2])->requests);
EXPECT_EQ(cars[0], net.get_interface(cars[2])->last_request.destination);
EXPECT_EQ(1, net.get_interface(cars[3])->requests);
EXPECT_EQ(cars[0], net.get_interface(cars[3])->last_request.destination);
}
/*
* Packet lifetime reported to access layer's request interface
* shall be reduced by GN forwarders as accurately as possible.
* Note: The reported lifetime is only as accurate as GN Lifetime field can encode it.
* Even in the best case, lifetime is not reduced finer than in 50ms steps.
*/
TEST_P(Routing, forwarding_remaining_lifetime)
{
GbcDataRequest gbc_request(net.get_mib(), aid::DEN);
gbc_request.destination = circle_dest_area(18.0_m, 20.0_m, 4.0_m);
gbc_request.upper_protocol = UpperProtocol::BTP_B;
gbc_request.maximum_lifetime = Lifetime { Lifetime::Base::One_Second, 3 };
auto confirm = net.get_router(cars[4])->request(gbc_request, create_packet());
ASSERT_TRUE(confirm.accepted());
EXPECT_EQ(std::chrono::seconds(3), net.get_interface(cars[4])->last_request.lifetime);
EXPECT_EQ(0, net.get_interface(cars[3])->requests);
net.advance_time(std::chrono::seconds(1));
EXPECT_EQ(1, net.get_interface(cars[3])->requests);
auto forwarding_remaining_lifetime = net.get_interface(cars[3])->last_request.lifetime;
EXPECT_GE(forwarding_remaining_lifetime, std::chrono::milliseconds(2900));
EXPECT_LT(forwarding_remaining_lifetime, std::chrono::seconds(3));
}
static const auto PacketHandlingValues = ::testing::Combine(
::testing::Values(
NetworkTopology::PacketDuplicationMode::Copy_Construct,
NetworkTopology::PacketDuplicationMode::Serialize),
::testing::Bool());
std::string printPacketHandlingValue(const ::testing::TestParamInfo<Routing::ParamType>& value)
{
std::string print;
switch (std::get<0>(value.param)) {
case NetworkTopology::PacketDuplicationMode::Copy_Construct:
print = "Copy";
break;
case NetworkTopology::PacketDuplicationMode::Serialize:
print = "Serialize";
break;
}
print += std::get<1>(value.param) ? "WithSecurity" : "WithoutSecurity";
return print;
}
INSTANTIATE_TEST_SUITE_P(RoutingPacketHandling, Routing, PacketHandlingValues, printPacketHandlingValue);
@@ -0,0 +1,112 @@
#ifndef SECURITY_CONTEXT_HPP_FEYZW1RS
#define SECURITY_CONTEXT_HPP_FEYZW1RS
#include <vanetza/common/runtime.hpp>
#include <vanetza/common/stored_position_provider.hpp>
#include <vanetza/security/backend.hpp>
#include <vanetza/security/delegating_security_entity.hpp>
#include <vanetza/security/straight_verify_service.hpp>
#include <vanetza/security/v2/certificate_cache.hpp>
#include <vanetza/security/v2/default_certificate_validator.hpp>
#include <vanetza/security/v2/naive_certificate_provider.hpp>
#include <vanetza/security/v2/sign_header_policy.hpp>
#include <vanetza/security/v2/sign_service.hpp>
#include <vanetza/security/v2/trust_store.hpp>
#include <vanetza/security/v3/certificate_validator.hpp>
#include <vanetza/security/v3/location_checker.hpp>
#include <vanetza/security/v3/naive_certificate_provider.hpp>
#include <vanetza/security/v3/sign_header_policy.hpp>
namespace vanetza
{
class SecurityContext
{
public:
SecurityContext(Runtime& rt) :
backend(security::create_backend("default")),
certificate_provider(rt),
cert_cache(rt),
certificate_validator(*backend, cert_cache, trust_store),
sign_header_policy(rt, position_provider),
certificate_provider_v3(rt),
sign_header_policy_v3(rt, position_provider, certificate_provider_v3),
security(build_sign_service(), build_verify_service(rt))
{
trust_store.insert(certificate_provider.root_certificate());
for (auto cert : certificate_provider.own_chain()) {
cert_cache.insert(cert);
}
// wire up v3 certificate validator
certificate_validator_v3.use_runtime(&rt);
certificate_validator_v3.use_position_provider(&position_provider);
certificate_validator_v3.use_location_checker(&location_checker);
}
security::SecurityEntity& entity()
{
return security;
}
void set_accurate_position(units::GeoAngle latitude, units::GeoAngle longitude)
{
PositionFix position_fix;
position_fix.latitude = latitude;
position_fix.longitude = longitude;
position_fix.confidence.semi_major = 25.0 * units::si::meter;
position_fix.confidence.semi_minor = 25.0 * units::si::meter;
assert(position_fix.confidence);
position_provider.position_fix(position_fix);
}
security::v3::CertificateCache& certificate_cache_v3()
{
return certificate_provider_v3.cache();
}
private:
std::unique_ptr<security::VerifyService> build_verify_service(Runtime& rt)
{
std::unique_ptr<security::StraightVerifyService> service {
new security::StraightVerifyService(rt, *backend, position_provider)
};
service->use_certificate_cache(&cert_cache);
service->use_certificate_provider(&certificate_provider);
service->use_certificate_validator(&certificate_validator);
service->use_sign_header_policy(&sign_header_policy);
service->use_certificate_provider(&certificate_provider_v3);
service->use_certificate_validator(&certificate_validator_v3);
service->use_sign_header_policy(&sign_header_policy_v3);
return service;
}
std::unique_ptr<security::SignService> build_sign_service()
{
return std::unique_ptr<security::SignService> {
new security::v2::StraightSignService(certificate_provider, *backend, sign_header_policy)
};
}
StoredPositionProvider position_provider;
security::v3::DefaultLocationChecker location_checker;
std::unique_ptr<security::Backend> backend;
security::v2::NaiveCertificateProvider certificate_provider;
std::vector<security::v2::Certificate> roots;
security::v2::TrustStore trust_store;
security::v2::CertificateCache cert_cache;
security::v2::DefaultCertificateValidator certificate_validator;
security::v2::DefaultSignHeaderPolicy sign_header_policy;
security::v3::NaiveCertificateProvider certificate_provider_v3;
security::v3::DefaultSignHeaderPolicy sign_header_policy_v3;
security::v3::DefaultCertificateValidator certificate_validator_v3;
security::DelegatingSecurityEntity security;
};
} // namespace vanetza
#endif /* SECURITY_CONTEXT_HPP_FEYZW1RS */
@@ -0,0 +1,64 @@
#include <gtest/gtest.h>
#include <vanetza/geonet/sequence_number.hpp>
#include <vanetza/geonet/tests/serialization.hpp>
using namespace vanetza::geonet;
TEST(SequenceNumber, ctor) {
SequenceNumber a;
EXPECT_EQ(a, SequenceNumber(0));
SequenceNumber b(348);
EXPECT_EQ(b, SequenceNumber(348));
}
TEST(SequenceNumber, less) {
SequenceNumber a(384);
SequenceNumber b(348);
EXPECT_LT(b, a);
EXPECT_GT(a, b);
SequenceNumber c(33151);
EXPECT_LT(a, c);
EXPECT_GT(b, c);
}
TEST(SequenceNumber, equality) {
SequenceNumber a(2348);
SequenceNumber b(a);
SequenceNumber c(2334);
EXPECT_EQ(a, b);
EXPECT_NE(a, c);
}
TEST(SequenceNumber, addition) {
SequenceNumber a(348);
SequenceNumber b(568);
a += b;
EXPECT_EQ(a, SequenceNumber(916));
EXPECT_EQ(static_cast<uint16_t>(b), 568);
a += SequenceNumber(SequenceNumber::max);
EXPECT_EQ(a, SequenceNumber(915));
}
TEST(SequenceNumber, subtration) {
SequenceNumber a(348);
SequenceNumber b(568);
a -= b;
EXPECT_EQ(a, SequenceNumber(65316));
a -= b;
EXPECT_EQ(a, SequenceNumber(64748));
EXPECT_EQ(b, SequenceNumber(568));
}
TEST(SequenceNumber, increment) {
SequenceNumber a(348);
SequenceNumber b = a++;
EXPECT_EQ(static_cast<uint16_t>(b), 348);
EXPECT_EQ(static_cast<uint16_t>(a), 349);
}
TEST(SequenceNumber, serialization) {
const SequenceNumber a(321);
SequenceNumber b = serialize_roundtrip(a);
EXPECT_EQ(a, b);
}
@@ -0,0 +1,49 @@
#ifndef SERIALIZATION_HPP_ZFDJQSWI
#define SERIALIZATION_HPP_ZFDJQSWI
#include <vanetza/common/archives.hpp>
#include <vanetza/common/serialization.hpp>
#include <sstream>
namespace vanetza
{
namespace geonet
{
/**
* \brief Serialize and deserialize an object
*
* Source object is serialized and deserialized
* object form this binary representation is returned.
*
* \tparam T the type of the result
* \param source serialize from this object
* \return deserialized object (should be equal to source)
*/
template<typename T>
T serialize_roundtrip(const T& source)
{
std::stringstream stream;
OutputArchive oa(stream);
serialize(source, oa);
T result;
InputArchive ia(stream);
deserialize(result, ia);
return result;
}
template<typename T>
std::size_t serialize_length(const T& source)
{
std::stringstream stream;
OutputArchive oa(stream);
serialize(source, oa);
return stream.tellp();
}
} // namespace geonet
} // namespace vanetza
#endif /* SERIALIZATION_HPP_ZFDJQSWI */
@@ -0,0 +1,101 @@
#include <gtest/gtest.h>
#include <vanetza/geonet/timestamp.hpp>
#include <limits>
using namespace vanetza::geonet;
const Timestamp::absolute_unit_type abs_ms;
const auto max = std::numeric_limits<Timestamp::value_type>::max();
TEST(Timestamp, ctor) {
Timestamp a;
EXPECT_EQ(a.raw(), 0);
Timestamp b(Timestamp::time_type::from_value(832));
EXPECT_EQ(b.raw(), 832);
boost::posix_time::ptime p =
boost::posix_time::time_from_string("2004-01-01 00:00:00.000");
Timestamp c(p);
EXPECT_EQ(c.raw(), 0);
boost::posix_time::ptime q = p + boost::posix_time::milliseconds(5234);
Timestamp d(q);
EXPECT_EQ(d.raw(), 5234);
}
TEST(Timestamp, equality) {
Timestamp a(413 * abs_ms);
Timestamp b(412 * abs_ms);
EXPECT_NE(a, b);
Timestamp c(a);
EXPECT_EQ(a, c);
}
TEST(Timestamp, max) {
Timestamp a(895723 * abs_ms);
Timestamp b((895723 + max + 1) * abs_ms);
EXPECT_EQ(a, b);
}
TEST(Timestamp, is_greater) {
Timestamp a(238 * abs_ms);
Timestamp b(513 * abs_ms);
EXPECT_TRUE(is_greater(b, a));
EXPECT_FALSE(is_greater(a, b));
Timestamp c((513 + max / 2) * abs_ms);
EXPECT_TRUE(is_greater(c, b));
Timestamp d((514 + max / 2) * abs_ms);
EXPECT_FALSE(is_greater(d, b));
Timestamp e(Timestamp::time_type::from_value(max));
EXPECT_FALSE(is_greater(e, a));
Timestamp f(Timestamp::time_type::from_value(max/2));
EXPECT_FALSE(is_greater(e, f));
Timestamp g(Timestamp::time_type::from_value(max/2 + 1));
EXPECT_TRUE(is_greater(e, g));
Timestamp h(513 * abs_ms);
EXPECT_EQ(b, h);
EXPECT_FALSE(is_greater(b, h));
}
TEST(Timestamp, less) {
// operator< uses is_greater: we don't have to test a lot of combinations here
Timestamp a(582 * abs_ms);
Timestamp b(54884 * abs_ms);
EXPECT_LT(a, b);
Timestamp c(54884 * abs_ms);
EXPECT_FALSE(c < b);
}
TEST(Timestamp, addition) {
Timestamp a(89 * abs_ms);
a += 348 * Timestamp::millisecond();
EXPECT_EQ(a.raw(), 437);
Timestamp b = a + 3483 * Timestamp::millisecond();
EXPECT_EQ(a.raw(), 437);
EXPECT_EQ(b.raw(), 3920);
}
TEST(Timestamp, substraction) {
Timestamp a(3483 * abs_ms);
a -= 89 * Timestamp::millisecond();
EXPECT_EQ(a.raw(), 3394);
a -= 5000 * Timestamp::millisecond();
EXPECT_EQ(a.raw(), 4294965690);
Timestamp b = a - 23 * Timestamp::millisecond();
EXPECT_EQ(a.raw(), 4294965690);
EXPECT_EQ(b.raw(), 4294965667);
}
TEST(Timestamp, difference) {
Timestamp a(329 * abs_ms);
Timestamp b(394 * abs_ms);
EXPECT_EQ((a - b).value(), 4294967231);
EXPECT_EQ((b - a).value(), 65);
}
@@ -0,0 +1,75 @@
#include <gtest/gtest.h>
#include <vanetza/geonet/traffic_class.hpp>
using namespace vanetza::geonet;
TEST(TrafficClass, ctor) {
TrafficClass a;
EXPECT_FALSE(a.store_carry_forward());
EXPECT_FALSE(a.channel_offload());
EXPECT_EQ(a.tc_id(), 0);
TrafficClass b(true, false, 8);
EXPECT_TRUE(b.store_carry_forward());
EXPECT_FALSE(b.channel_offload());
EXPECT_EQ(b.tc_id(), 8);
TrafficClass c(false, true, 5);
EXPECT_FALSE(c.store_carry_forward());
EXPECT_TRUE(c.channel_offload());
EXPECT_EQ(c.tc_id(), 5);
TrafficClass d(true, true, 1);
EXPECT_TRUE(d.store_carry_forward());
EXPECT_TRUE(d.channel_offload());
EXPECT_EQ(d.tc_id(), 1);
}
TEST(TrafficClass, store_carry_forward) {
TrafficClass a(true, false, 0);
EXPECT_TRUE(a.store_carry_forward());
a.store_carry_forward(false);
EXPECT_FALSE(a.store_carry_forward());
a.store_carry_forward(true);
EXPECT_TRUE(a.store_carry_forward());
}
TEST(TrafficClass, channel_offload) {
TrafficClass a(false, true, 0);
EXPECT_TRUE(a.channel_offload());
a.channel_offload(false);
EXPECT_FALSE(a.channel_offload());
a.channel_offload(true);
EXPECT_TRUE(a.channel_offload());
}
TEST(TrafficClass, tc_id) {
TrafficClass a(false, false, 0);
EXPECT_EQ(a.tc_id(), 0);
a.tc_id(7);
EXPECT_EQ(a.tc_id(), 7);
a.tc_id(63);
EXPECT_EQ(a.tc_id(), 63);
a.tc_id(0);
EXPECT_EQ(a.tc_id(), 0);
}
TEST(TrafficClass, map_tc_onto_profile) {
using vanetza::dcc::Profile;
TrafficClass tc;
tc.tc_id(0);
EXPECT_EQ(Profile::DP0, map_tc_onto_profile(tc));
tc.tc_id(1);
EXPECT_EQ(Profile::DP1, map_tc_onto_profile(tc));
tc.tc_id(2);
EXPECT_EQ(Profile::DP2, map_tc_onto_profile(tc));
tc.tc_id(3);
EXPECT_EQ(Profile::DP3, map_tc_onto_profile(tc));
tc.tc_id(4);
EXPECT_EQ(Profile::DP3, map_tc_onto_profile(tc));
}