From c23491ef3ce831553395c2ea450f59cad9a5619a Mon Sep 17 00:00:00 2001 From: Martin Stein Date: Wed, 12 Jul 2023 11:41:31 +0200 Subject: [PATCH] nic_uplink: connect a Nic to an Uplink session * Adds a new component server/nic_uplink that forwards packets unmodified between one Uplink session at one side and potentially multiple Nic sessions at the other side. * Adds a new run script nic_uplink.run that does a basic test with multiple Nic clients on this component and adds it to the autopilot list. * Adds a new depot recipe src/nic_uplink for this component. * Adds a new depot recipe pkg/pc_nic for deploying the pc_nic_driver together with a nic_uplink server. This allows for raw access to the network connected to the Nic of the system in contrast to the commonly used routed and NAT'd access via NIC router. That said, it enables the use of network protocols not yet supported by the NIC router at the cost of less protection. Ref #4966 --- repos/os/recipes/pkg/pc_nic/README | 2 + repos/os/recipes/pkg/pc_nic/archives | 3 + repos/os/recipes/pkg/pc_nic/hash | 1 + repos/os/recipes/pkg/pc_nic/runtime | 66 ++ repos/os/recipes/src/nic_uplink/content.mk | 17 + repos/os/recipes/src/nic_uplink/hash | 1 + repos/os/recipes/src/nic_uplink/used_apis | 5 + repos/os/run/nic_uplink.run | 141 ++++ repos/os/src/server/nic_uplink/README | 28 + repos/os/src/server/nic_uplink/assertion.h | 35 + repos/os/src/server/nic_uplink/config.xsd | 12 + repos/os/src/server/nic_uplink/main.cc | 820 +++++++++++++++++++++ repos/os/src/server/nic_uplink/quota.h | 28 + repos/os/src/server/nic_uplink/target.mk | 14 + tool/autopilot.list | 1 + 15 files changed, 1174 insertions(+) create mode 100644 repos/os/recipes/pkg/pc_nic/README create mode 100755 repos/os/recipes/pkg/pc_nic/archives create mode 100644 repos/os/recipes/pkg/pc_nic/hash create mode 100755 repos/os/recipes/pkg/pc_nic/runtime create mode 100644 repos/os/recipes/src/nic_uplink/content.mk create mode 100644 repos/os/recipes/src/nic_uplink/hash create mode 100644 repos/os/recipes/src/nic_uplink/used_apis create mode 100755 repos/os/run/nic_uplink.run create mode 100644 repos/os/src/server/nic_uplink/README create mode 100644 repos/os/src/server/nic_uplink/assertion.h create mode 100644 repos/os/src/server/nic_uplink/config.xsd create mode 100755 repos/os/src/server/nic_uplink/main.cc create mode 100644 repos/os/src/server/nic_uplink/quota.h create mode 100755 repos/os/src/server/nic_uplink/target.mk diff --git a/repos/os/recipes/pkg/pc_nic/README b/repos/os/recipes/pkg/pc_nic/README new file mode 100644 index 0000000000..a591b739d2 --- /dev/null +++ b/repos/os/recipes/pkg/pc_nic/README @@ -0,0 +1,2 @@ + + Runtime for using the PC NIC driver with the NIC Uplink adapter diff --git a/repos/os/recipes/pkg/pc_nic/archives b/repos/os/recipes/pkg/pc_nic/archives new file mode 100755 index 0000000000..973c9f3f39 --- /dev/null +++ b/repos/os/recipes/pkg/pc_nic/archives @@ -0,0 +1,3 @@ +_/src/nic_uplink +_/src/pc_nic_drv +_/src/init diff --git a/repos/os/recipes/pkg/pc_nic/hash b/repos/os/recipes/pkg/pc_nic/hash new file mode 100644 index 0000000000..a7c00559c1 --- /dev/null +++ b/repos/os/recipes/pkg/pc_nic/hash @@ -0,0 +1 @@ +2023-07-20 b182af537d83f8d29d58a3dd70e8c49f775c26d0 diff --git a/repos/os/recipes/pkg/pc_nic/runtime b/repos/os/recipes/pkg/pc_nic/runtime new file mode 100755 index 0000000000..33d2326d23 --- /dev/null +++ b/repos/os/recipes/pkg/pc_nic/runtime @@ -0,0 +1,66 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/repos/os/recipes/src/nic_uplink/content.mk b/repos/os/recipes/src/nic_uplink/content.mk new file mode 100644 index 0000000000..94ea8b1333 --- /dev/null +++ b/repos/os/recipes/src/nic_uplink/content.mk @@ -0,0 +1,17 @@ +MIRROR_FROM_REP_DIR := \ + $(addprefix src/server/nic_router/, \ + communication_buffer.cc \ + communication_buffer.h \ + session_env.h \ + session_creation.h \ + list.h \ + ) + +content: $(MIRROR_FROM_REP_DIR) + +$(MIRROR_FROM_REP_DIR): + mkdir -p $(dir $@) + cp -r $(REP_DIR)/$@ $@ + +SRC_DIR = src/server/nic_uplink +include $(GENODE_DIR)/repos/base/recipes/src/content.inc diff --git a/repos/os/recipes/src/nic_uplink/hash b/repos/os/recipes/src/nic_uplink/hash new file mode 100644 index 0000000000..55f5f5e165 --- /dev/null +++ b/repos/os/recipes/src/nic_uplink/hash @@ -0,0 +1 @@ +2023-07-20 fefd70d2d32066af73cbdc53ba606eec73b73c7b diff --git a/repos/os/recipes/src/nic_uplink/used_apis b/repos/os/recipes/src/nic_uplink/used_apis new file mode 100644 index 0000000000..73fa31d89a --- /dev/null +++ b/repos/os/recipes/src/nic_uplink/used_apis @@ -0,0 +1,5 @@ +base +os +net +nic_session +uplink_session diff --git a/repos/os/run/nic_uplink.run b/repos/os/run/nic_uplink.run new file mode 100755 index 0000000000..8f23dd2251 --- /dev/null +++ b/repos/os/run/nic_uplink.run @@ -0,0 +1,141 @@ +# +# there are no nic driver packages for rpi3 and imx53 tz +# +if {[have_board rpi3] || [have_board imx53_qsb_tz]} { + puts "Run script is not supported on this platform." + exit 0 +} + +# +# lx & riscv would require extra setup on the test machine which is not desired +# +if {[get_cmd_switch --autopilot] && ([have_spec linux] || + [have_board virt_qemu_riscv])} { + + puts "Autopilot mode is not supported on this platform." + exit 0 +} + +proc test_timeout { } { + if {[have_spec sel4] && [have_board pc]} { return 60 } + return 30 +} + +proc dst_ip { } { + if {[expr ![have_include power_on/qemu]]} { + return "10.0.0.2" + } else { + return "10.0.2.2" + } +} + +build { server/nic_uplink app/ping } + +create_boot_directory + +import_from_depot [depot_user]/src/[base_src] \ + [depot_user]/pkg/[drivers_nic_pkg] \ + [depot_user]/src/init + +append config { + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +} + +install_config $config + +build_boot_image [build_artifacts] + +append qemu_args " -nographic " +append_qemu_nic_args + +run_genode_until "child \"ping_.\" exited with exit value 0.*child \"ping_.\" exited with exit value 0.*\n" [test_timeout] diff --git a/repos/os/src/server/nic_uplink/README b/repos/os/src/server/nic_uplink/README new file mode 100644 index 0000000000..098aa7031b --- /dev/null +++ b/repos/os/src/server/nic_uplink/README @@ -0,0 +1,28 @@ +Component for forwarding network packets between a NIC session and an Uplink +session. + +On startup, the component announces only an Uplink service and waits for an +Uplink session to be created. Once the component hosts its first Uplink +session, it also announces a NIC service. The component takes the MAC address +of the first Uplink session and uses it as MAC address for all NIC sessions to +come throughout the entire component lifetime. Later Uplink session requests +must have the same MAC address as the first one or they will be denied. +This implies that switching between two network devices is not possible without +restarting the component. + +The component supports only one Uplink session but multiple NIC session at a +time. A packet received at the Uplink session is forwarded unmodified to all +NIC sessions that exist at that time. A packet received at a NIC session is +forwarded to the one Uplink session if it exists. The link state of each NIC +session is equal to the existence of an Uplink session. + +This is an exemplary configuration of the component: + +! + +The verbose flag is optional, disabled by default, and toggles whether the +component provides diagnostic output on its LOG session. The diagnostic output +consists of a full packet trace as well as component errors and warnings. + +The component is accompanied by the os/run/nic_uplink.run test that is suitable +for hardware and Qemu. diff --git a/repos/os/src/server/nic_uplink/assertion.h b/repos/os/src/server/nic_uplink/assertion.h new file mode 100644 index 0000000000..4141014656 --- /dev/null +++ b/repos/os/src/server/nic_uplink/assertion.h @@ -0,0 +1,35 @@ +/* + * \brief Assertion macros + * \author Martin Stein + * \date 2023-07-13 + */ + +/* + * Copyright (C) 2023 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 _ASSERTION_H_ +#define _ASSERTION_H_ + +/* base includes */ +#include +#include + +#define ASSERT(condition) \ + do { \ + if (!(condition)) { \ + Genode::error(__FILE__, ":", __LINE__, ": ", " assertion \"", #condition, "\" failed "); \ + Genode::sleep_forever(); \ + } \ + } while (false) + +#define ASSERT_NEVER_REACHED \ + do { \ + Genode::error(__FILE__, ":", __LINE__, ": ", " should have never been reached"); \ + Genode::sleep_forever(); \ + } while (false) + +#endif /* _ASSERTION_H_ */ diff --git a/repos/os/src/server/nic_uplink/config.xsd b/repos/os/src/server/nic_uplink/config.xsd new file mode 100644 index 0000000000..aef8844770 --- /dev/null +++ b/repos/os/src/server/nic_uplink/config.xsd @@ -0,0 +1,12 @@ + + + + + + + + + + + + diff --git a/repos/os/src/server/nic_uplink/main.cc b/repos/os/src/server/nic_uplink/main.cc new file mode 100755 index 0000000000..eae4380d1d --- /dev/null +++ b/repos/os/src/server/nic_uplink/main.cc @@ -0,0 +1,820 @@ +/* + * \brief Component construct and main component object + * \author Martin Stein + * \date 2023-07-13 + */ + +/* + * Copyright (C) 2023 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. + */ + +/* base includes */ +#include +#include +#include +#include + +/* nic_uplink includes */ +#include +#include + +/* nic_router includes */ +#include +#include +#include + +/* os includes */ +#include +#include +#include +#include + +using namespace Genode; +using namespace Net; + +namespace Nic_uplink { + + class Main; +} + +using namespace Nic_uplink; + +namespace Net { + + enum { PKT_STREAM_QUEUE_SIZE = 1024 }; + + template + void log_if(bool condition, ARGS &&... args) + { + if (condition) + log(args...); + } + + using Packet_descriptor = Genode::Packet_descriptor; + using Packet_stream_policy = Genode::Packet_stream_policy; + using Packet_stream_sink = Genode::Packet_stream_sink; + using Packet_stream_source = Genode::Packet_stream_source; + + class Network_interface; + class Uplink_session_component_base; + class Uplink_session_component; + class Uplink_session_root; + class Nic_session_component_base; + class Nic_session_component; + class Nic_session_root; + using Nic_session_list_item = List_element; + using Nic_session_list = Net::List; +} + + +class Net::Network_interface +{ + public: + + using Label = String<32>; + + private: + + Packet_stream_sink &_sink; + Packet_stream_source &_source; + Label const _label; + bool const _verbose; + + public: + + Network_interface(Packet_stream_sink &sink, + Packet_stream_source &source, + Label const &label, + bool verbose) + : + _sink { sink }, + _source { source }, + _label { label }, + _verbose { verbose } + { } + + virtual ~Network_interface() { } + + template + void send_packet(size_t pkt_size, GENERATE_PKT && generate_pkt) + { + if (!_source.ready_to_submit()) { + log_if(_verbose, "[", _label, "] failed to send packet"); + return; + } + _source.alloc_packet_attempt(pkt_size).with_result( + [&] (Packet_descriptor pkt) + { + void *pkt_base { _source.packet_content(pkt) }; + generate_pkt(Byte_range_ptr { (char *)pkt_base, pkt_size }); + Size_guard size_guard(pkt_size); + log_if(_verbose, "[", _label, "] snd ", Ethernet_frame::cast_from(pkt_base, size_guard)); + _source.try_submit_packet(pkt); + }, + [&] (Packet_stream_source::Alloc_packet_error) + { + log_if(_verbose, "[", _label, "] failed to alloc packet"); + } + ); + } + + void forward_packet(Byte_range_ptr const &src); + + template + void handle_received_packets(HANDLE_PKT && handle_pkt) const + { + while (_source.ack_avail()) { + _source.release_packet(_source.try_get_acked_packet()); + } + while (_sink.packet_avail()) { + Packet_descriptor const pkt { _sink.get_packet() }; + handle_pkt(Byte_range_ptr { _sink.packet_content(pkt), pkt.size() }); + if (!_sink.try_ack_packet(pkt)) + log_if(_verbose, "[", _label, "] failed to ack packet"); + } + } + + void wakeup_source() { _source.wakeup(); }; + + void wakeup_sink() { _sink.wakeup(); } +}; + + +class Net::Uplink_session_component_base +{ + protected: + + Session_env &_session_env; + Heap _alloc; + Nic::Packet_allocator _packet_alloc; + Communication_buffer _tx_buf; + Communication_buffer _rx_buf; + + public: + + Uplink_session_component_base(Session_env &session_env, + size_t const tx_buf_size, + size_t const rx_buf_size); +}; + + +class Net::Uplink_session_component +: + private Uplink_session_component_base, + public ::Uplink::Session_rpc_object +{ + private: + + Main &_main; + Ram_dataspace_capability const _ram_ds; + Network_interface _net_if; + Signal_handler _pkt_stream_signal_handler; + + void _handle_pkt_stream_signal(); + + public: + + Uplink_session_component(Session_env &session_env, + size_t const tx_buf_size, + size_t const rx_buf_size, + Ram_dataspace_capability const ram_ds, + Main &main); + + void forward_packet(Byte_range_ptr const &src) { _net_if.forward_packet(src); } + + void wakeup_source() { _net_if.wakeup_source(); }; + + void wakeup_sink() { _net_if.wakeup_sink(); } + + + /*************** + ** Accessors ** + ***************/ + + Ram_dataspace_capability ram_ds() const { return _ram_ds; }; + Session_env const &session_env() const { return _session_env; }; +}; + + +class Net::Uplink_session_root +: + public Root_component +{ + private: + + Env &_env; + Quota &_shared_quota; + Main &_main; + + + /******************** + ** Root_component ** + ********************/ + + Uplink_session_component *_create_session(char const *args) override; + void _destroy_session(Uplink_session_component *session) override; + + public: + + Uplink_session_root(Env &env, + Allocator &alloc, + Quota &shared_quota, + Main &main); +}; + + +class Net::Nic_session_component_base +{ + protected: + + Session_env &_session_env; + Heap _alloc; + Nic::Packet_allocator _packet_alloc; + Communication_buffer _tx_buf; + Communication_buffer _rx_buf; + + public: + + Nic_session_component_base(Session_env &session_env, + size_t const tx_buf_size, + size_t const rx_buf_size); +}; + + +class Net::Nic_session_component +: + private Nic_session_component_base, + public ::Nic::Session_rpc_object +{ + private: + + Main &_main; + Ram_dataspace_capability const _ram_ds; + Network_interface _net_if; + Signal_handler _pkt_stream_signal_handler; + Signal_context_capability _link_state_sigh { }; + Nic_session_list_item _list_item { this }; + + void _handle_pkt_stream_signal(); + + public: + + Nic_session_component(Session_env &session_env, + size_t const tx_buf_size, + size_t const rx_buf_size, + Ram_dataspace_capability const ram_ds, + Main &main); + + void forward_packet(Byte_range_ptr const &src) { _net_if.forward_packet(src); } + + void submit_link_state_signal(); + + void wakeup_source() { _net_if.wakeup_source(); }; + + void wakeup_sink() { _net_if.wakeup_sink(); } + + + /****************** + ** Nic::Session ** + ******************/ + + Mac_address mac_address() override; + bool link_state() override; + void link_state_sigh(Signal_context_capability sigh) override; + + + /*************** + ** Accessors ** + ***************/ + + Ram_dataspace_capability ram_ds() const { return _ram_ds; }; + Session_env const &session_env() const { return _session_env; }; + + template + void with_list_item(FUNC && func) { func(_list_item); } +}; + + +class Net::Nic_session_root +: + public Root_component +{ + private: + + Env &_env; + Quota &_shared_quota; + Main &_main; + + + /******************** + ** Root_component ** + ********************/ + + Nic_session_component *_create_session(char const *args) override; + void _destroy_session(Nic_session_component *session) override; + + public: + + Nic_session_root(Env &env, + Allocator &alloc, + Quota &shared_quota, + Main &main); +}; + + +class Nic_uplink::Main +{ + private: + + Env &_env; + Quota _shared_quota { }; + Heap _heap { &_env.ram(), &_env.rm() }; + Uplink_session_component *_uplink_session_ptr { nullptr }; + Nic_session_list _nic_session_list { }; + Uplink_session_root _uplink_session_root { _env, _heap, _shared_quota, *this }; + Nic_session_root _nic_session_root { _env, _heap, _shared_quota, *this }; + bool _nic_service_announced { false }; + Mac_address _uplink_mac { }; + bool _uplink_mac_valid { false }; + Attached_rom_dataspace _config_rom { _env, "config" }; + bool const _verbose { _config_rom.xml().attribute_value("verbose", false) }; + + Main(Main const &) = delete; + + Main & operator = (Main const &) = delete; + + public: + + Main(Env &env); + + bool verbose() const { return _verbose; } + + bool ready_to_manage_uplink_session() const { return !_uplink_session_ptr; } + + void manage_uplink_session(Uplink_session_component &session, + Mac_address const &mac); + + template + void with_uplink_session(FUNC && func) + { + if (_uplink_session_ptr) + func(*_uplink_session_ptr, _uplink_mac); + } + + void dissolve_uplink_session(Uplink_session_component &session); + + void manage_nic_session(Nic_session_component &session); + + template + void for_each_nic_session(FUNC && func) + { + _nic_session_list.for_each([&] (Nic_session_list_item &list_item) + { + func(*list_item.object()); + }); + } + + void dissolve_nic_session(Nic_session_component &session); +}; + + +/************************************* + ** Net::Nic_session_component_base ** + *************************************/ + +Nic_session_component_base::Nic_session_component_base(Session_env &session_env, + size_t const tx_buf_size, + size_t const rx_buf_size) +: + _session_env { session_env }, + _alloc { _session_env, _session_env }, + _packet_alloc { &_alloc }, + _tx_buf { _session_env, tx_buf_size }, + _rx_buf { _session_env, rx_buf_size } +{ } + + +/******************************** + ** Net::Nic_session_component ** + ********************************/ + +Net::Nic_session_component::Nic_session_component(Session_env &session_env, + size_t const tx_buf_size, + size_t const rx_buf_size, + Ram_dataspace_capability const ram_ds, + Main &main) +: + Nic_session_component_base { session_env, tx_buf_size,rx_buf_size }, + Session_rpc_object { + _session_env, _tx_buf.ds(), _rx_buf.ds(), &_packet_alloc, + _session_env.ep().rpc_ep() }, + _main { main }, + _ram_ds { ram_ds }, + _net_if { *_tx.sink(), *_rx.source(), "nic", _main.verbose() }, + _pkt_stream_signal_handler { session_env.ep(), *this, &Nic_session_component::_handle_pkt_stream_signal } +{ + /* install packet stream signal handlers */ + _tx.sigh_packet_avail(_pkt_stream_signal_handler); + _rx.sigh_ack_avail(_pkt_stream_signal_handler); + + /* + * We do not install ready_to_submit because submission is only triggered by + * incoming packets (and dropped if the submit queue is full). + * The ack queue should never be full otherwise we'll be leaking packets. + */ +} + + +void Net::Nic_session_component::_handle_pkt_stream_signal() +{ + _net_if.handle_received_packets([&] (Byte_range_ptr const &src) { + + Size_guard size_guard { src.num_bytes }; + Ethernet_frame ð { Ethernet_frame::cast_from(src.start, size_guard) }; + log_if(_main.verbose(), "[nic] rcv ", eth); + + _main.with_uplink_session([&] (Uplink_session_component &uplink_session, + Mac_address const &) + { + uplink_session.forward_packet(src); + }); + }); + _main.with_uplink_session([&] (Uplink_session_component &uplink_session, + Mac_address const &) + { + uplink_session.wakeup_source(); + }); + wakeup_sink(); +} + + +Mac_address Net::Nic_session_component::mac_address() +{ + Mac_address result { }; + _main.with_uplink_session([&] (Uplink_session_component &, + Mac_address const &uplink_mac) + { + result = uplink_mac; + }); + return result; +} + + +bool Net::Nic_session_component::link_state() +{ + bool result { false }; + _main.with_uplink_session([&] (Uplink_session_component &, + Mac_address const &) + { + result = true; + }); + return result; +} + + +void Net::Nic_session_component::link_state_sigh(Signal_context_capability sigh) +{ + _link_state_sigh = sigh; +} + + +void Net::Nic_session_component::submit_link_state_signal() +{ + Signal_transmitter { _link_state_sigh }.submit(); +} + + +/**************************** + ** Net::Network_interface ** + ****************************/ + +void Net::Network_interface::forward_packet(Byte_range_ptr const &src) +{ + send_packet(src.num_bytes, [&] (Byte_range_ptr const &dst) { + memcpy(dst.start, src.start, dst.num_bytes); + }); +} + + +/**************************************** + ** Net::Uplink_session_component_base ** + ****************************************/ + +Net::Uplink_session_component_base:: +Uplink_session_component_base(Session_env &session_env, + size_t const tx_buf_size, + size_t const rx_buf_size) +: + _session_env { session_env }, + _alloc { _session_env, _session_env }, + _packet_alloc { &_alloc }, + _tx_buf { _session_env, tx_buf_size }, + _rx_buf { _session_env, rx_buf_size } +{ } + + +/*********************************** + ** Net::Uplink_session_component ** + ***********************************/ + +void Net::Uplink_session_component::_handle_pkt_stream_signal() +{ + _net_if.handle_received_packets([&] (Byte_range_ptr const &src) + { + Size_guard size_guard { src.num_bytes }; + Ethernet_frame ð { Ethernet_frame::cast_from(src.start, size_guard) }; + log_if(_main.verbose(), "[uplink] rcv ", eth); + + _main.for_each_nic_session([&] (Nic_session_component &nic_session) + { + nic_session.forward_packet(src); + }); + }); + _main.for_each_nic_session([&] (Nic_session_component &nic_session) + { + nic_session.wakeup_source(); + }); + wakeup_sink(); +} + + +Net::Uplink_session_component::Uplink_session_component(Session_env &session_env, + size_t const tx_buf_size, + size_t const rx_buf_size, + Ram_dataspace_capability const ram_ds, + Main &main) +: + Uplink_session_component_base { session_env, tx_buf_size,rx_buf_size }, + Session_rpc_object { + _session_env, _tx_buf.ds(), _rx_buf.ds(), &_packet_alloc, + _session_env.ep().rpc_ep() }, + _main { main }, + _ram_ds { ram_ds }, + _net_if { *_tx.sink(), *_rx.source(), "uplink", _main.verbose() }, + _pkt_stream_signal_handler { session_env.ep(), *this, &Uplink_session_component::_handle_pkt_stream_signal } +{ + /* install packet stream signal handlers */ + _tx.sigh_packet_avail(_pkt_stream_signal_handler); + _rx.sigh_ack_avail(_pkt_stream_signal_handler); + + /* + * We do not install ready_to_submit because submission is only triggered + * by incoming packets (and dropped if the submit queue is full). + * The ack queue should never be full otherwise we'll be leaking packets. + */ +} + + +/****************************** + ** Net::Uplink_session_root ** + ******************************/ + +Net::Uplink_session_root::Uplink_session_root(Env &env, + Allocator &alloc, + Quota &shared_quota, + Main &main) +: + Root_component { &env.ep().rpc_ep(), &alloc }, + _env { env }, + _shared_quota { shared_quota }, + _main { main } +{ } + + +Uplink_session_component * +Net::Uplink_session_root::_create_session(char const *args) +{ + Session_creation session_creation { }; + if (!_main.ready_to_manage_uplink_session()) { + log_if(_main.verbose(), "[uplink] failed to manage new session"); + throw Service_denied(); + } + try { + return session_creation.execute( + _env, _shared_quota, args, + [&] (Session_env &session_env, void *session_at, Ram_dataspace_capability ram_ds) + { + enum { MAC_STR_LENGTH = 19 }; + char mac_str [MAC_STR_LENGTH]; + Arg mac_arg { Arg_string::find_arg(args, "mac_address") }; + + if (!mac_arg.valid()) { + log_if(_main.verbose(), "[uplink] failed to find 'mac_address' arg"); + throw Service_denied(); + } + mac_arg.string(mac_str, MAC_STR_LENGTH, ""); + Mac_address mac { }; + ascii_to(mac_str, mac); + if (mac == Mac_address { }) { + log_if(_main.verbose(), "[uplink] malformed 'mac_address' arg"); + throw Service_denied(); + } + Uplink_session_component &session { + *construct_at( + session_at, + session_env, + Arg_string::find_arg(args, "tx_buf_size").ulong_value(0), + Arg_string::find_arg(args, "rx_buf_size").ulong_value(0), + ram_ds, _main) }; + + _main.manage_uplink_session(session, mac); + return &session; + }); + } + catch (Region_map::Invalid_dataspace) { + log_if(_main.verbose(), "[uplink] failed to attach RAM"); + throw Service_denied(); + } + catch (Region_map::Region_conflict) { + log_if(_main.verbose(), "[uplink] failed to attach RAM"); + throw Service_denied(); + } + catch (Out_of_ram) { + log_if(_main.verbose(), "[uplink] insufficient session RAM quota"); + throw Insufficient_ram_quota(); + } + catch (Out_of_caps) { + log_if(_main.verbose(), "[uplink] insufficient session CAP quota"); + throw Insufficient_cap_quota(); + } +} + +void Net::Uplink_session_root::_destroy_session(Uplink_session_component *session_ptr) +{ + _main.dissolve_uplink_session(*session_ptr); + + /* read out initial dataspace and session env and destruct session */ + Ram_dataspace_capability ram_ds { session_ptr->ram_ds() }; + Session_env const &session_env { session_ptr->session_env() }; + session_ptr->~Uplink_session_component(); + + /* copy session env to stack and detach/free all session data */ + Session_env session_env_stack { session_env }; + session_env_stack.detach(session_ptr); + session_env_stack.detach(&session_env); + session_env_stack.free(ram_ds); + + /* check for leaked quota */ + if (session_env_stack.ram_guard().used().value) + log_if(_main.verbose(), "[uplink] session leaks RAM quota of ", + session_env_stack.ram_guard().used().value, " byte(s)"); + if (session_env_stack.cap_guard().used().value) + log_if(_main.verbose(), "[uplink] session leaks CAP quota of ", + session_env_stack.cap_guard().used().value, " cap(s)"); +} + + +/*************************** + ** Net::Nic_session_root ** + ***************************/ + +Net::Nic_session_root::Nic_session_root(Env &env, + Allocator &alloc, + Quota &shared_quota, + Main &main) +: + Root_component { &env.ep().rpc_ep(), &alloc }, + _env { env }, + _shared_quota { shared_quota }, + _main { main } +{ } + + +Nic_session_component *Net::Nic_session_root::_create_session(char const *args) +{ + Session_creation session_creation { }; + try { + return session_creation.execute( + _env, _shared_quota, args, + [&] (Session_env &session_env, void *session_at, Ram_dataspace_capability ram_ds) + { + Nic_session_component &session { + *construct_at( + session_at, session_env, + Arg_string::find_arg(args, "tx_buf_size").ulong_value(0), + Arg_string::find_arg(args, "rx_buf_size").ulong_value(0), + ram_ds, _main) }; + + _main.manage_nic_session(session); + return &session; + } + ); + } + catch (Region_map::Invalid_dataspace) { + log_if(_main.verbose(), "[nic] failed to attach RAM"); + throw Service_denied(); + } + catch (Region_map::Region_conflict) { + log_if(_main.verbose(), "[nic] failed to attach RAM"); + throw Service_denied(); + } + catch (Out_of_ram) { + log_if(_main.verbose(), "[nic] insufficient session RAM quota"); + throw Insufficient_ram_quota(); + } + catch (Out_of_caps) { + log_if(_main.verbose(), "[nic] insufficient session CAP quota"); + throw Insufficient_cap_quota(); + } +} + +void Net::Nic_session_root::_destroy_session(Nic_session_component *session_ptr) +{ + _main.dissolve_nic_session(*session_ptr); + + /* read out initial dataspace and session env and destruct session */ + Ram_dataspace_capability ram_ds { session_ptr->ram_ds() }; + Session_env const &session_env { session_ptr->session_env() }; + session_ptr->~Nic_session_component(); + + /* copy session env to stack and detach/free all session data */ + Session_env session_env_stack { session_env }; + session_env_stack.detach(session_ptr); + session_env_stack.detach(&session_env); + session_env_stack.free(ram_ds); + + /* check for leaked quota */ + if (session_env_stack.ram_guard().used().value) + log_if(_main.verbose(), "[nic] session leaks RAM quota of ", + session_env_stack.ram_guard().used().value, " byte(s)"); + if (session_env_stack.cap_guard().used().value) + log_if(_main.verbose(), "[nic] session leaks CAP quota of ", + session_env_stack.cap_guard().used().value, " cap(s)"); +} + + +/********************** + ** Nic_uplink::Main ** + **********************/ + +Nic_uplink::Main::Main(Env &env) +: + _env { env } +{ + _env.parent().announce(_env.ep().manage(_uplink_session_root)); +} + + +void Nic_uplink::Main::manage_uplink_session(Uplink_session_component &session, + Mac_address const &mac) +{ + ASSERT(!_uplink_session_ptr); + + if (_uplink_mac_valid) + ASSERT(_uplink_mac == mac); + + _uplink_session_ptr = &session; + _uplink_mac = mac; + + if (!_nic_service_announced) { + _env.parent().announce(_env.ep().manage(_nic_session_root)); + _nic_service_announced = true; + } + for_each_nic_session([&] (Nic_session_component &nic_session) + { + nic_session.submit_link_state_signal(); + }); + log_if(_verbose, "[uplink] session created! mac=", _uplink_mac); +} + + +void Nic_uplink::Main::manage_nic_session(Nic_session_component &session) +{ + session.with_list_item([&] (Nic_session_list_item &item) + { + _nic_session_list.insert(&item); + }); + log_if(_verbose, "[nic] session created!"); +} + + +void Nic_uplink::Main::dissolve_uplink_session(Uplink_session_component &session) +{ + ASSERT(_uplink_session_ptr == &session); + _uplink_session_ptr = nullptr; + for_each_nic_session([&] (Nic_session_component &nic_session) + { + nic_session.submit_link_state_signal(); + }); + log_if(_verbose, "[uplink] session dissolved!"); +} + + +void Nic_uplink::Main::dissolve_nic_session(Nic_session_component &session) +{ + session.with_list_item([&] (Nic_session_list_item &item) + { + _nic_session_list.remove(&item); + }); + log_if(_verbose, "[nic] session dissolved!"); +} + + +/*********************** + ** Genode::Component ** + ***********************/ + +void Component::construct(Env &env) { static Nic_uplink::Main main { env }; } diff --git a/repos/os/src/server/nic_uplink/quota.h b/repos/os/src/server/nic_uplink/quota.h new file mode 100644 index 0000000000..11484bf5f7 --- /dev/null +++ b/repos/os/src/server/nic_uplink/quota.h @@ -0,0 +1,28 @@ +/* + * \brief Session quota + * \author Martin Stein + * \date 2023-07-13 + */ + +/* + * Copyright (C) 2023 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 _QUOTA_H_ +#define _QUOTA_H_ + +namespace Net { + + class Quota; +} + +struct Net::Quota +{ + Genode::size_t ram { 0 }; + Genode::size_t cap { 0 }; +}; + +#endif /* _QUOTA_H_ */ diff --git a/repos/os/src/server/nic_uplink/target.mk b/repos/os/src/server/nic_uplink/target.mk new file mode 100755 index 0000000000..70d3222d2a --- /dev/null +++ b/repos/os/src/server/nic_uplink/target.mk @@ -0,0 +1,14 @@ +TARGET = nic_uplink + +LIBS += base net + +INC_DIR += $(PRG_DIR) +INC_DIR += $(REP_DIR)/src/server/nic_router + +SRC_CC += main.cc +SRC_CC += communication_buffer.cc + +vpath main.cc $(PRG_DIR) +vpath communication_buffer.cc $(REP_DIR)/src/server/nic_router + +CONFIG_XSD = config.xsd diff --git a/tool/autopilot.list b/tool/autopilot.list index 6f47444752..0d13efe99a 100644 --- a/tool/autopilot.list +++ b/tool/autopilot.list @@ -54,6 +54,7 @@ nic_router_flood nic_router_ipv4_fragm nic_router_stress nic_router_uplinks +nic_uplink nvme ping ping_nic_router