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.
353 lines
11 KiB
C++
353 lines
11 KiB
C++
#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
|