diff --git a/src/server/nic_bus/README b/src/server/nic_bus/README new file mode 100644 index 0000000..d97e805 --- /dev/null +++ b/src/server/nic_bus/README @@ -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. diff --git a/src/server/nic_bus/bus.h b/src/server/nic_bus/bus.h new file mode 100644 index 0000000..5a0cfa3 --- /dev/null +++ b/src/server/nic_bus/bus.h @@ -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 +#include +#include + +namespace Nic_bus { + using namespace Net; + using namespace Genode; + + template + struct Bus; +} + + +template +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 + 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 + void apply_all(PROC proc) + { + for (auto i = 0U; i < BUS_SIZE; ++i) { + Element *elem = _elements[i]; + if (elem != nullptr) { + proc(elem->obj); + } + } + } +}; + +#endif diff --git a/src/server/nic_bus/main.cc b/src/server/nic_bus/main.cc new file mode 100644 index 0000000..0e6d437 --- /dev/null +++ b/src/server/nic_bus/main.cc @@ -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 +#include +#include +#include + +namespace Nic_bus { + using namespace Net; + using namespace Genode; + class Root; + struct Main; +} + + +class Nic_bus::Root : public Genode::Root_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(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); } diff --git a/src/server/nic_bus/session_component.h b/src/server/nic_bus/session_component.h new file mode 100644 index 0000000..3acac2c --- /dev/null +++ b/src/server/nic_bus/session_component.h @@ -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 +#include +#include +#include +#include + +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_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 _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_ */ diff --git a/src/server/nic_bus/target.mk b/src/server/nic_bus/target.mk new file mode 100644 index 0000000..7ca9606 --- /dev/null +++ b/src/server/nic_bus/target.mk @@ -0,0 +1,3 @@ +TARGET = nic_bus +LIBS = base net +SRC_CC = main.cc