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:
@@ -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));
|
||||
}
|
||||
+325
@@ -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());
|
||||
}
|
||||
+35
@@ -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));
|
||||
}
|
||||
Reference in New Issue
Block a user