Nic_bus server

A minimal Nic service that switches ethernet packets between Nic
sessions. Packets are only switched between MAC addresses of directly
connected sessions, attaching a device driver and switching to
additional MACs on further ethernet segments is not supported. That is
to say that is component is neither a hub nor a switch.

This server is intended for creating private LANs between components and
supports arbitrary level 3 protocols (IPv4 and IPv6).
This commit is contained in:
Emery Hemingway
2019-04-15 11:27:02 +02:00
committed by Norman Feske
parent 3e04718fa7
commit 498854c085
5 changed files with 387 additions and 0 deletions

View File

@@ -0,0 +1,4 @@
The nic_bus server switches packets between sessions using Ethernet headers.
Sessions may only send and receive packets with MAC addresses assigned by
the bus. For this reason it does not support attachment to ethernet hubs or
switches and is therefore not intended for use with harware interfaces.

126
src/server/nic_bus/bus.h Normal file
View File

@@ -0,0 +1,126 @@
/*
* \brief Nic session bus
* \author Emery Hemingway
* \date 2019-04-14
*/
/*
* Copyright (C) 2019 Genode Labs GmbH
*
* This file is part of the Genode OS framework, which is distributed
* under the terms of the GNU Affero General Public License version 3.
*/
#ifndef _NETWORK_STATE_H_
#define _NETWORK_STATE_H_
/* Genode includes */
#include <net/ethernet.h>
#include <base/session_label.h>
#include <util/xml_node.h>
namespace Nic_bus {
using namespace Net;
using namespace Genode;
template <typename T>
struct Bus;
}
template <typename T>
struct Nic_bus::Bus
{
struct Element;
/* static array of sessions on the bus */
enum { BUS_SIZE = 0xffU };
Element *_elements[BUS_SIZE] { nullptr };
static uint8_t _index(Mac_address const &mac) { return mac.addr[1]; };
void remove(Mac_address const mac) {
_elements[_index(mac)] = nullptr; }
Mac_address insert(Element &elem, char const *label)
{
/**
* Derive a MAC address using the FNV-1a algorithm.
*/
enum {
FNV_64_PRIME = 1099511628211U,
FNV_64_OFFSET = 14695981039346656037U,
};
uint64_t hash = FNV_64_OFFSET;
char const *p = label;
while (*p) {
hash ^= *p++;
hash *= FNV_64_PRIME;
}
for (;;) {
/* add the terminating zero */
hash *= FNV_64_PRIME;
uint8_t index = hash >> 32;
if (_elements[index] != nullptr)
continue;
/* hash until a free slot is found */
_elements[index] = &elem;
Mac_address mac;
mac.addr[0] = 0x02;
mac.addr[1] = index;
mac.addr[2] = hash >> 24;
mac.addr[3] = hash >> 16;
mac.addr[4] = hash >> 8;
mac.addr[5] = hash;
return mac;
}
}
struct Element
{
Bus &bus;
T &obj;
Mac_address const mac;
Element(Bus &b, T &o, char const *label)
: bus(b), obj(o), mac(bus.insert(*this, label)) { }
~Element() { bus.remove(mac); }
};
Bus() { }
template<typename PROC>
void apply(Mac_address mac, PROC proc)
{
uint8_t const index = _index(mac);
Element *elem = _elements[index];
if (elem != nullptr) {
if (elem->mac == mac) {
proc(elem->obj);
}
}
}
template<typename PROC>
void apply_all(PROC proc)
{
for (auto i = 0U; i < BUS_SIZE; ++i) {
Element *elem = _elements[i];
if (elem != nullptr) {
proc(elem->obj);
}
}
}
};
#endif

View File

@@ -0,0 +1,86 @@
/*
* \brief Nic bus service
* \author Emery Hemingway
* \date 2019-04-10
*/
/*
* Copyright (C) 2019 Genode Labs GmbH
*
* This file is part of the Genode OS framework, which is distributed
* under the terms of the GNU Affero General Public License version 3.
*/
/* local includes */
#include "session_component.h"
/* Genode */
#include <root/component.h>
#include <base/attached_rom_dataspace.h>
#include <os/session_policy.h>
#include <base/component.h>
namespace Nic_bus {
using namespace Net;
using namespace Genode;
class Root;
struct Main;
}
class Nic_bus::Root : public Genode::Root_component<Nic_bus::Session_component>
{
private:
Genode::Env &_env;
Attached_rom_dataspace _config_rom { _env, "config" };
Session_bus _bus { };
protected:
Session_component *_create_session(const char *args) override
{
using namespace Genode;
_config_rom.update();
Session_label label { label_from_args(args) };
Session_policy policy { label, _config_rom.xml() };
return new (md_alloc())
Session_component(_env.ep(), _env.ram(), _env.rm(),
ram_quota_from_args(args),
cap_quota_from_args(args),
Tx_size{Arg_string::find_arg(args, "tx_buf_size").ulong_value(0)},
Rx_size{Arg_string::find_arg(args, "rx_buf_size").ulong_value(0)},
_bus,
label);
}
public:
Root(Genode::Env &env,
Genode::Allocator &md_alloc)
: Genode::Root_component<Nic_bus::Session_component>(env.ep(), md_alloc),
_env(env)
{ }
};
struct Nic_bus::Main
{
Sliced_heap _sliced_heap;
Nic_bus::Root _root;
Main(Genode::Env &env)
: _sliced_heap(env.ram(), env.rm()),
_root(env, _sliced_heap)
{
env.parent().announce(env.ep().manage(_root));
}
};
void Component::construct(Genode::Env &env) { static Nic_bus::Main inst(env); }

View File

@@ -0,0 +1,168 @@
/*
* \brief Nic bus session component
* \author Emery Hemingway
* \date 2019-04-14
*/
/*
* Copyright (C) 2019 Genode Labs GmbH
*
* This file is part of the Genode OS framework, which is distributed
* under the terms of the GNU Affero General Public License version 3.
*/
#ifndef _SESSION_COMPONENT_H_
#define _SESSION_COMPONENT_H_
/* local includes */
#include "bus.h"
/* Genode includes */
#include <net/ethernet.h>
#include <net/size_guard.h>
#include <nic/component.h>
#include <base/heap.h>
#include <base/session_label.h>
namespace Nic_bus {
using namespace Net;
struct Tx_size { Genode::size_t value; };
struct Rx_size { Genode::size_t value; };
class Session_resources;
class Session_component;
typedef Bus<Session_component> Session_bus;
}
/**
* Base class to manage session quotas and allocations
*/
class Nic_bus::Session_resources
{
protected:
Genode::Ram_quota_guard _ram_guard;
Genode::Cap_quota_guard _cap_guard;
Genode::Constrained_ram_allocator _ram_alloc;
Genode::Attached_ram_dataspace _tx_ds, _rx_ds;
Genode::Heap _alloc;
Nic::Packet_allocator _rx_pkt_alloc { &_alloc };
Session_resources(Genode::Ram_allocator &ram,
Genode::Region_map &region_map,
Genode::Ram_quota ram_quota,
Genode::Cap_quota cap_quota,
Tx_size tx_size,
Rx_size rx_size)
:
_ram_guard(ram_quota), _cap_guard(cap_quota),
_ram_alloc(ram, _ram_guard, _cap_guard),
_tx_ds(_ram_alloc, region_map, tx_size.value),
_rx_ds(_ram_alloc, region_map, rx_size.value),
_alloc(_ram_alloc, region_map)
{ }
};
class Nic_bus::Session_component : private Session_resources,
public Nic::Session_rpc_object
{
private:
Session_bus::Element _bus_elem;
Genode::Session_label const _label;
Genode::Io_signal_handler<Session_component> _packet_handler;
Nic::Packet_stream_sink<::Nic::Session::Policy> &sink() {
return *_tx.sink(); }
Nic::Packet_stream_source<::Nic::Session::Policy> &source() {
return *_rx.source(); }
void _send(Ethernet_frame const &eth, Genode::size_t const size)
{
while (source().ack_avail())
source().release_packet(source().get_acked_packet());
if (!source().ready_to_submit()) return;
/* drop the packet if the queue is congested */
Nic::Packet_descriptor pkt = source().alloc_packet(size);
void *content = source().packet_content(pkt);
Genode::memcpy(content, (void*)&eth, size);
source().submit_packet(pkt);
}
void _handle_packet(Nic::Packet_descriptor const &pkt)
{
if (!pkt.size() || !sink().packet_valid(pkt)) return;
Size_guard size_guard(pkt.size());
Ethernet_frame const &eth = Ethernet_frame::cast_from(
sink().packet_content(pkt), size_guard);
if (eth.src() != _bus_elem.mac) {
Genode::warning(
eth.src(), " is not the managed MAC adress, "
"dropping packet from ", _label);
return;
}
auto send = [&] (Session_component &other) { other._send(eth, pkt.size()); };
if (eth.dst().addr[0] & 1) {
/* multicast */
_bus_elem.bus.apply_all(send);
} else {
/* unicast */
_bus_elem.bus.apply(eth.dst(), send);
}
}
void _handle_packets()
{
while (sink().ready_to_ack() && sink().packet_avail()) {
_handle_packet(sink().peek_packet());
sink().acknowledge_packet(sink().get_packet());
}
}
public:
Session_component(Genode::Entrypoint &ep,
Genode::Ram_allocator &ram,
Genode::Region_map &region_map,
Genode::Ram_quota ram_quota,
Genode::Cap_quota cap_quota,
Tx_size tx_size,
Rx_size rx_size,
Session_bus &bus,
Genode::Session_label const &label)
:
Session_resources(ram, region_map,
ram_quota, cap_quota,
tx_size, rx_size),
Nic::Session_rpc_object(region_map,
_tx_ds.cap(), _rx_ds.cap(),
&_rx_pkt_alloc, ep.rpc_ep()),
_bus_elem(bus, *this, label.string()), _label(label),
_packet_handler(ep, *this, &Session_component::_handle_packets)
{
_tx.sigh_packet_avail(_packet_handler);
_tx.sigh_ready_to_ack(_packet_handler);
}
Nic::Mac_address mac_address() override { return _bus_elem.mac; }
bool link_state() override { return true; }
void link_state_sigh(Genode::Signal_context_capability) override { }
};
#endif /* _SESSION_COMPONENT_H_ */

View File

@@ -0,0 +1,3 @@
TARGET = nic_bus
LIBS = base net
SRC_CC = main.cc