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:
committed by
Norman Feske
parent
3e04718fa7
commit
498854c085
4
src/server/nic_bus/README
Normal file
4
src/server/nic_bus/README
Normal 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
126
src/server/nic_bus/bus.h
Normal 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
|
||||
86
src/server/nic_bus/main.cc
Normal file
86
src/server/nic_bus/main.cc
Normal 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); }
|
||||
168
src/server/nic_bus/session_component.h
Normal file
168
src/server/nic_bus/session_component.h
Normal 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 ®ion_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 ð, 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*)ð, 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 ð = 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 ®ion_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_ */
|
||||
3
src/server/nic_bus/target.mk
Normal file
3
src/server/nic_bus/target.mk
Normal file
@@ -0,0 +1,3 @@
|
||||
TARGET = nic_bus
|
||||
LIBS = base net
|
||||
SRC_CC = main.cc
|
||||
Reference in New Issue
Block a user