mirror of
https://github.com/genodelabs/genode.git
synced 2024-12-19 21:57:55 +00:00
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
This commit is contained in:
parent
4c4962b306
commit
c23491ef3c
2
repos/os/recipes/pkg/pc_nic/README
Normal file
2
repos/os/recipes/pkg/pc_nic/README
Normal file
@ -0,0 +1,2 @@
|
||||
|
||||
Runtime for using the PC NIC driver with the NIC Uplink adapter
|
3
repos/os/recipes/pkg/pc_nic/archives
Executable file
3
repos/os/recipes/pkg/pc_nic/archives
Executable file
@ -0,0 +1,3 @@
|
||||
_/src/nic_uplink
|
||||
_/src/pc_nic_drv
|
||||
_/src/init
|
1
repos/os/recipes/pkg/pc_nic/hash
Normal file
1
repos/os/recipes/pkg/pc_nic/hash
Normal file
@ -0,0 +1 @@
|
||||
2023-07-20 b182af537d83f8d29d58a3dd70e8c49f775c26d0
|
66
repos/os/recipes/pkg/pc_nic/runtime
Executable file
66
repos/os/recipes/pkg/pc_nic/runtime
Executable file
@ -0,0 +1,66 @@
|
||||
<runtime ram="25M" caps="600" binary="init">
|
||||
|
||||
<requires> <timer/> <platform/> </requires>
|
||||
|
||||
<provides> <nic/> </provides>
|
||||
|
||||
<content>
|
||||
<rom label="ld.lib.so"/>
|
||||
<rom label="nic_uplink"/>
|
||||
<rom label="pc_nic_drv"/>
|
||||
<rom label="init"/>
|
||||
</content>
|
||||
|
||||
<config>
|
||||
|
||||
<parent-provides>
|
||||
<service name="ROM"/>
|
||||
<service name="PD"/>
|
||||
<service name="RM"/>
|
||||
<service name="CPU"/>
|
||||
<service name="LOG"/>
|
||||
<service name="Timer"/>
|
||||
<service name="Platform"/>
|
||||
</parent-provides>
|
||||
|
||||
<service name="Nic">
|
||||
<default-policy> <child name="nic_uplink"/> </default-policy>
|
||||
</service>
|
||||
|
||||
<start name="nic_uplink" caps="100">
|
||||
<resource name="RAM" quantum="1M"/>
|
||||
<provides>
|
||||
<service name="Uplink"/>
|
||||
<service name="Nic"/>
|
||||
</provides>
|
||||
<config/>
|
||||
<route>
|
||||
<service name="Timer"> <parent/> </service>
|
||||
<service name="PD"> <parent/> </service>
|
||||
<service name="ROM"> <parent/> </service>
|
||||
<service name="LOG"> <parent/> </service>
|
||||
<service name="CPU"> <parent/> </service>
|
||||
</route>
|
||||
</start>
|
||||
|
||||
<start name="nic_drv" caps="300">
|
||||
<binary name="pc_nic_drv"/>
|
||||
<resource name="RAM" quantum="20M"/>
|
||||
<resource name="CPU" quantum="10"/>
|
||||
<config/>
|
||||
<route>
|
||||
<service name="Uplink"> <child name="nic_uplink"/> </service>
|
||||
<service name="ROM"> <parent/> </service>
|
||||
<service name="PD"> <parent/> </service>
|
||||
<service name="RM"> <parent/> </service>
|
||||
<service name="CPU"> <parent/> </service>
|
||||
<service name="LOG"> <parent/> </service>
|
||||
<service name="Timer"> <parent/> </service>
|
||||
<service name="Platform"> <parent label="nic"/> </service>
|
||||
</route>
|
||||
<config> <report mac_address="yes"/> </config>
|
||||
</start>
|
||||
|
||||
</config>
|
||||
|
||||
</runtime>
|
17
repos/os/recipes/src/nic_uplink/content.mk
Normal file
17
repos/os/recipes/src/nic_uplink/content.mk
Normal file
@ -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
|
1
repos/os/recipes/src/nic_uplink/hash
Normal file
1
repos/os/recipes/src/nic_uplink/hash
Normal file
@ -0,0 +1 @@
|
||||
2023-07-20 fefd70d2d32066af73cbdc53ba606eec73b73c7b
|
5
repos/os/recipes/src/nic_uplink/used_apis
Normal file
5
repos/os/recipes/src/nic_uplink/used_apis
Normal file
@ -0,0 +1,5 @@
|
||||
base
|
||||
os
|
||||
net
|
||||
nic_session
|
||||
uplink_session
|
141
repos/os/run/nic_uplink.run
Executable file
141
repos/os/run/nic_uplink.run
Executable file
@ -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 {
|
||||
|
||||
<config>
|
||||
|
||||
<parent-provides>
|
||||
<service name="ROM"/>
|
||||
<service name="IRQ"/>
|
||||
<service name="IO_PORT"/>
|
||||
<service name="IO_MEM"/>
|
||||
<service name="PD"/>
|
||||
<service name="RM"/>
|
||||
<service name="CPU"/>
|
||||
<service name="LOG"/>
|
||||
</parent-provides>
|
||||
|
||||
<start name="timer" caps="100">
|
||||
<resource name="RAM" quantum="1M"/>
|
||||
<provides><service name="Timer"/></provides>
|
||||
<route>
|
||||
<service name="PD"> <parent/> </service>
|
||||
<service name="ROM"> <parent/> </service>
|
||||
<service name="LOG"> <parent/> </service>
|
||||
<service name="CPU"> <parent/> </service>
|
||||
<service name="IO_PORT"> <parent/> </service>
|
||||
<service name="IO_MEM"> <parent/> </service>
|
||||
<service name="IRQ"> <parent/> </service>
|
||||
</route>
|
||||
</start>
|
||||
|
||||
<start name="drivers" caps="1200" managing_system="yes">
|
||||
<resource name="RAM" quantum="32M"/>
|
||||
<binary name="init"/>
|
||||
<route>
|
||||
<service name="ROM" label="config"> <parent label="drivers.config"/> </service>
|
||||
<service name="Uplink"> <child name="nic_uplink"/> </service>
|
||||
<service name="Timer"> <child name="timer"/> </service>
|
||||
<service name="RM"> <parent/> </service>
|
||||
<service name="PD"> <parent/> </service>
|
||||
<service name="ROM"> <parent/> </service>
|
||||
<service name="LOG"> <parent/> </service>
|
||||
<service name="CPU"> <parent/> </service>
|
||||
<service name="IO_PORT"> <parent/> </service>
|
||||
<service name="IO_MEM"> <parent/> </service>
|
||||
<service name="IRQ"> <parent/> </service>
|
||||
</route>
|
||||
</start>
|
||||
|
||||
<start name="nic_uplink" caps="100">
|
||||
<resource name="RAM" quantum="1M"/>
|
||||
<provides>
|
||||
<service name="Uplink"/>
|
||||
<service name="Nic"/>
|
||||
</provides>
|
||||
<config verbose="yes"/>
|
||||
<route>
|
||||
<service name="Timer"> <child name="timer"/> </service>
|
||||
<service name="PD"> <parent/> </service>
|
||||
<service name="ROM"> <parent/> </service>
|
||||
<service name="LOG"> <parent/> </service>
|
||||
<service name="CPU"> <parent/> </service>
|
||||
</route>
|
||||
</start>
|
||||
|
||||
<start name="ping_1" caps="100">
|
||||
<binary name="ping"/>
|
||||
<resource name="RAM" quantum="8M"/>
|
||||
<config dst_ip="} [dst_ip] {" period_sec="1" count="3"/>
|
||||
<route>
|
||||
<service name="Nic"> <child name="nic_uplink"/> </service>
|
||||
<service name="Timer"> <child name="timer"/> </service>
|
||||
<service name="PD"> <parent/> </service>
|
||||
<service name="ROM"> <parent/> </service>
|
||||
<service name="LOG"> <parent/> </service>
|
||||
<service name="CPU"> <parent/> </service>
|
||||
</route>
|
||||
</start>
|
||||
|
||||
<start name="ping_2" caps="100">
|
||||
<binary name="ping"/>
|
||||
<resource name="RAM" quantum="8M"/>
|
||||
<config dst_ip="} [dst_ip] {" period_sec="1" count="3"/>
|
||||
<route>
|
||||
<service name="Nic"> <child name="nic_uplink"/> </service>
|
||||
<service name="Timer"> <child name="timer"/> </service>
|
||||
<service name="PD"> <parent/> </service>
|
||||
<service name="ROM"> <parent/> </service>
|
||||
<service name="LOG"> <parent/> </service>
|
||||
<service name="CPU"> <parent/> </service>
|
||||
</route>
|
||||
</start>
|
||||
|
||||
</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]
|
28
repos/os/src/server/nic_uplink/README
Normal file
28
repos/os/src/server/nic_uplink/README
Normal file
@ -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:
|
||||
|
||||
! <config verbose="no"/>
|
||||
|
||||
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.
|
35
repos/os/src/server/nic_uplink/assertion.h
Normal file
35
repos/os/src/server/nic_uplink/assertion.h
Normal file
@ -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 <base/log.h>
|
||||
#include <base/sleep.h>
|
||||
|
||||
#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_ */
|
12
repos/os/src/server/nic_uplink/config.xsd
Normal file
12
repos/os/src/server/nic_uplink/config.xsd
Normal file
@ -0,0 +1,12 @@
|
||||
<?xml version="1.0"?>
|
||||
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema">
|
||||
|
||||
<xs:include schemaLocation="base_types.xsd"/>
|
||||
|
||||
<xs:element name="config">
|
||||
<xs:complexType>
|
||||
<xs:attribute name="verbose" type="Boolean" />
|
||||
</xs:complexType>
|
||||
</xs:element><!-- config -->
|
||||
|
||||
</xs:schema>
|
820
repos/os/src/server/nic_uplink/main.cc
Executable file
820
repos/os/src/server/nic_uplink/main.cc
Executable file
@ -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 <base/component.h>
|
||||
#include <base/heap.h>
|
||||
#include <root/component.h>
|
||||
#include <base/attached_rom_dataspace.h>
|
||||
|
||||
/* nic_uplink includes */
|
||||
#include <quota.h>
|
||||
#include <assertion.h>
|
||||
|
||||
/* nic_router includes */
|
||||
#include <session_creation.h>
|
||||
#include <communication_buffer.h>
|
||||
#include <list.h>
|
||||
|
||||
/* os includes */
|
||||
#include <net/ethernet.h>
|
||||
#include <nic/packet_allocator.h>
|
||||
#include <uplink_session/rpc_object.h>
|
||||
#include <nic_session/rpc_object.h>
|
||||
|
||||
using namespace Genode;
|
||||
using namespace Net;
|
||||
|
||||
namespace Nic_uplink {
|
||||
|
||||
class Main;
|
||||
}
|
||||
|
||||
using namespace Nic_uplink;
|
||||
|
||||
namespace Net {
|
||||
|
||||
enum { PKT_STREAM_QUEUE_SIZE = 1024 };
|
||||
|
||||
template <typename... ARGS>
|
||||
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<Packet_descriptor, PKT_STREAM_QUEUE_SIZE, PKT_STREAM_QUEUE_SIZE, char>;
|
||||
using Packet_stream_sink = Genode::Packet_stream_sink<Packet_stream_policy>;
|
||||
using Packet_stream_source = Genode::Packet_stream_source<Packet_stream_policy>;
|
||||
|
||||
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<Nic_session_component>;
|
||||
using Nic_session_list = Net::List<Nic_session_list_item>;
|
||||
}
|
||||
|
||||
|
||||
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 <typename GENERATE_PKT>
|
||||
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 <typename HANDLE_PKT>
|
||||
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<Uplink_session_component> _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<Uplink_session_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<Nic_session_component> _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 <typename FUNC>
|
||||
void with_list_item(FUNC && func) { func(_list_item); }
|
||||
};
|
||||
|
||||
|
||||
class Net::Nic_session_root
|
||||
:
|
||||
public Root_component<Nic_session_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 <typename FUNC>
|
||||
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 <typename FUNC>
|
||||
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<Uplink_session_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<Uplink_session_component> 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<Uplink_session_component>(
|
||||
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<Nic_session_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<Nic_session_component> 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<Nic_session_component>(
|
||||
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 }; }
|
28
repos/os/src/server/nic_uplink/quota.h
Normal file
28
repos/os/src/server/nic_uplink/quota.h
Normal file
@ -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_ */
|
14
repos/os/src/server/nic_uplink/target.mk
Executable file
14
repos/os/src/server/nic_uplink/target.mk
Executable file
@ -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
|
@ -54,6 +54,7 @@ nic_router_flood
|
||||
nic_router_ipv4_fragm
|
||||
nic_router_stress
|
||||
nic_router_uplinks
|
||||
nic_uplink
|
||||
nvme
|
||||
ping
|
||||
ping_nic_router
|
||||
|
Loading…
Reference in New Issue
Block a user