diff --git a/repos/os/src/server/nic_perf/README b/repos/os/src/server/nic_perf/README
new file mode 100644
index 0000000000..e49bf2032f
--- /dev/null
+++ b/repos/os/src/server/nic_perf/README
@@ -0,0 +1,46 @@
+The 'nic_perf' component is a benchmark component for the Nic and Uplink
+service. It can act as a Nic/Uplink server and a Nic client. The component
+periodically logs the number of transmitted/received packets and the resulting
+data rate. When enabled, it transmits continuous stream of UDP packets to a
+predefined receiver as a test stimulus.
+
+
+Basics
+~~~~~~
+
+This is an example configuration:
+
+!
+!
+!
+!
+!
+!
+!
+!
+
+The 'period_ms' attribute specifies the logging intervall (in milliseconds). By
+default, logging is disabled. The 'count' attribute defines after how may
+periods the component exits. Session policies for connecting Nic/Uplink clients
+are specified by '' nodes resp. a '' node. The component
+opens a single Nic connection if a '' node is provided.
+
+All sub-nodes comprise an optional '' node and an optional ''
+node. This is an overview of their attributes:
+
+:interface.ip:
+ Optional. Specifies the own IP address. If not specified, the component will
+ send DHCP requests to acquire an IP.
+
+:interface.dhcp_client_ip:
+ Optional. If specified, the component responds to DHCP requests with this IP
+ address.
+
+:tx.mtu:
+ Optional. Sets the size of the transmitted test packets.
+
+:tx.to:
+ Mandatory. Specifies the destination IP address.
+
+:tx.udp_port:
+ Mandatory. Specifies the destination port.
diff --git a/repos/os/src/server/nic_perf/dhcp_client.cc b/repos/os/src/server/nic_perf/dhcp_client.cc
new file mode 100644
index 0000000000..a26de4ee66
--- /dev/null
+++ b/repos/os/src/server/nic_perf/dhcp_client.cc
@@ -0,0 +1,245 @@
+/*
+ * \brief DHCP client state model
+ * \author Martin Stein
+ * \date 2016-08-24
+ */
+
+/*
+ * Copyright (C) 2016-2017 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
+#include
+
+/* Genode includes */
+#include
+
+enum { PKT_SIZE = 1024 };
+
+struct Send_buffer_too_small : Genode::Exception { };
+struct Bad_send_dhcp_args : Genode::Exception { };
+
+using namespace Genode;
+using namespace Net;
+using Message_type = Dhcp_packet::Message_type;
+using Dhcp_options = Dhcp_packet::Options_aggregator;
+
+
+/***************
+ ** Utilities **
+ ***************/
+
+void append_param_req_list(Dhcp_options &dhcp_opts)
+{
+ dhcp_opts.append_param_req_list([&] (Dhcp_options::Parameter_request_list_data &data) {
+ data.append_param_req();
+ data.append_param_req();
+ data.append_param_req();
+ data.append_param_req();
+ data.append_param_req();
+ data.append_param_req();
+ });
+}
+
+
+/*****************
+ ** Dhcp_client **
+ *****************/
+
+Dhcp_client::Dhcp_client(Timer::Connection &timer,
+ Nic_perf::Interface &interface)
+:
+ _timeout(timer, *this, &Dhcp_client::_handle_timeout),
+ _interface(interface)
+{
+ _discover();
+}
+
+
+void Dhcp_client::_discover()
+{
+ _set_state(State::SELECT, _discover_timeout);
+ _send(Message_type::DISCOVER, Ipv4_address(), Ipv4_address(),
+ Ipv4_address());
+}
+
+
+void Dhcp_client::_rerequest(State next_state)
+{
+ _set_state(next_state, _rerequest_timeout(2));
+ Ipv4_address const client_ip = _interface.ip();
+ _send(Message_type::REQUEST, client_ip, Ipv4_address(), client_ip);
+}
+
+
+void Dhcp_client::_set_state(State state, Microseconds timeout)
+{
+ _state = state;
+ _timeout.schedule(timeout);
+}
+
+
+Microseconds Dhcp_client::_rerequest_timeout(unsigned lease_time_div_log2)
+{
+ /* FIXME limit the time because of shortcomings in timeout framework */
+ enum { MAX_TIMEOUT_SEC = 3600 };
+ uint64_t timeout_sec = _lease_time_sec >> lease_time_div_log2;
+
+ if (timeout_sec > MAX_TIMEOUT_SEC) {
+ timeout_sec = MAX_TIMEOUT_SEC;
+ warning("Had to prune the state timeout of DHCP client");
+ }
+ return Microseconds(timeout_sec * 1000 * 1000);
+}
+
+
+void Dhcp_client::_handle_timeout(Duration)
+{
+ switch (_state) {
+ case State::BOUND: _rerequest(State::RENEW); break;
+ case State::RENEW: _rerequest(State::REBIND); break;
+ default: _discover();
+ }
+}
+
+
+void Dhcp_client::handle_dhcp(Dhcp_packet &dhcp, Ethernet_frame ð, Size_guard &)
+{
+ if (eth.dst() != _interface.mac() &&
+ eth.dst() != Mac_address(0xff))
+ {
+ throw Drop_packet_inform("DHCP client expects Ethernet targeting the router");
+ }
+
+ if (dhcp.client_mac() != _interface.mac()) {
+ throw Drop_packet_inform("DHCP client expects DHCP targeting the router"); }
+
+ try { _handle_dhcp_reply(dhcp); }
+ catch (Dhcp_packet::Option_not_found) {
+ throw Drop_packet_inform("DHCP client misses DHCP option"); }
+}
+
+
+void Dhcp_client::_handle_dhcp_reply(Dhcp_packet &dhcp)
+{
+ Message_type const msg_type =
+ dhcp.option().value();
+
+ switch (_state) {
+ case State::SELECT:
+
+ if (msg_type != Message_type::OFFER) {
+ throw Drop_packet_inform("DHCP client expects an offer");
+ }
+ _set_state(State::REQUEST, _request_timeout);
+ _send(Message_type::REQUEST, Ipv4_address(),
+ dhcp.option().value(),
+ dhcp.yiaddr());
+ break;
+
+ case State::REQUEST:
+ {
+ if (msg_type != Message_type::ACK) {
+ throw Drop_packet_inform("DHCP client expects an acknowledgement");
+ }
+ _lease_time_sec = dhcp.option().value();
+ _set_state(State::BOUND, _rerequest_timeout(1));
+ Ipv4_address dns_server;
+ try { dns_server = dhcp.option().value(); }
+ catch (Dhcp_packet::Option_not_found) { }
+
+ _interface.ip(dhcp.yiaddr());
+ log("Got IP address ", _interface.ip());
+ break;
+ }
+ case State::RENEW:
+ case State::REBIND:
+
+ if (msg_type != Message_type::ACK) {
+ throw Drop_packet_inform("DHCP client expects an acknowledgement");
+ }
+ _set_state(State::BOUND, _rerequest_timeout(1));
+ _lease_time_sec = dhcp.option().value();
+ break;
+
+ default: throw Drop_packet_inform("DHCP client doesn't expect a packet");
+ }
+}
+
+
+void Dhcp_client::_send(Message_type msg_type,
+ Ipv4_address client_ip,
+ Ipv4_address server_ip,
+ Ipv4_address requested_ip)
+{
+ _interface.send(PKT_SIZE, [&] (void *pkt_base, Size_guard &size_guard) {
+
+ /* create ETH header of the request */
+ Ethernet_frame ð = Ethernet_frame::construct_at(pkt_base, size_guard);
+ eth.dst(Mac_address(0xff));
+ eth.src(_interface.mac());
+ eth.type(Ethernet_frame::Type::IPV4);
+
+ /* create IP header of the request */
+ enum { IPV4_TIME_TO_LIVE = 64 };
+ size_t const ip_off = size_guard.head_size();
+ Ipv4_packet &ip = eth.construct_at_data(size_guard);
+ ip.header_length(sizeof(Ipv4_packet) / 4);
+ ip.version(4);
+ ip.time_to_live(IPV4_TIME_TO_LIVE);
+ ip.protocol(Ipv4_packet::Protocol::UDP);
+ ip.src(client_ip);
+ ip.dst(Ipv4_address(0xff));
+
+ /* create UDP header of the request */
+ size_t const udp_off = size_guard.head_size();
+ Udp_packet &udp = ip.construct_at_data(size_guard);
+ udp.src_port(Port(Dhcp_packet::BOOTPC));
+ udp.dst_port(Port(Dhcp_packet::BOOTPS));
+
+ /* create mandatory DHCP fields of the request */
+ size_t const dhcp_off = size_guard.head_size();
+ Dhcp_packet &dhcp = udp.construct_at_data(size_guard);
+ dhcp.op(Dhcp_packet::REQUEST);
+ dhcp.htype(Dhcp_packet::Htype::ETH);
+ dhcp.hlen(sizeof(Mac_address));
+ dhcp.ciaddr(client_ip);
+ dhcp.client_mac(_interface.mac());
+ dhcp.default_magic_cookie();
+
+ /* append DHCP option fields to the request */
+ Dhcp_options dhcp_opts(dhcp, size_guard);
+ dhcp_opts.append_option(msg_type);
+ switch (msg_type) {
+ case Message_type::DISCOVER:
+ append_param_req_list(dhcp_opts);
+ dhcp_opts.append_option(_interface.mac());
+ dhcp_opts.append_option(PKT_SIZE - dhcp_off);
+ break;
+
+ case Message_type::REQUEST:
+ append_param_req_list(dhcp_opts);
+ dhcp_opts.append_option(_interface.mac());
+ dhcp_opts.append_option(PKT_SIZE - dhcp_off);
+ if (_state == State::REQUEST) {
+ dhcp_opts.append_option(requested_ip);
+ dhcp_opts.append_option(server_ip);
+ }
+ break;
+
+ default:
+ throw Bad_send_dhcp_args();
+ }
+ dhcp_opts.append_option();
+
+ /* fill in header values that need the packet to be complete already */
+ udp.length(size_guard.head_size() - udp_off);
+ udp.update_checksum(ip.src(), ip.dst());
+ ip.total_length(size_guard.head_size() - ip_off);
+ ip.update_checksum();
+ });
+}
diff --git a/repos/os/src/server/nic_perf/dhcp_client.h b/repos/os/src/server/nic_perf/dhcp_client.h
new file mode 100644
index 0000000000..0cb8a08622
--- /dev/null
+++ b/repos/os/src/server/nic_perf/dhcp_client.h
@@ -0,0 +1,89 @@
+/*
+ * \brief DHCP client state model
+ * \author Martin Stein
+ * \date 2016-08-24
+ */
+
+/*
+ * Copyright (C) 2016-2017 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 _DHCP_CLIENT_H_
+#define _DHCP_CLIENT_H_
+
+/* Genode includes */
+#include
+#include
+
+namespace Nic_perf { class Interface; }
+
+namespace Net {
+
+ /* external definition */
+ class Ethernet_frame;
+
+ /* local definition */
+ class Dhcp_client;
+ class Drop_packet_inform;
+}
+
+
+struct Net::Drop_packet_inform : Genode::Exception
+{
+ char const *msg;
+
+ Drop_packet_inform(char const *msg) : msg(msg) { }
+};
+
+
+class Net::Dhcp_client
+{
+ private:
+
+ enum class State
+ {
+ INIT = 0, SELECT = 1, REQUEST = 2, BOUND = 3, RENEW = 4, REBIND = 5
+ };
+
+ enum { DISCOVER_TIMEOUT_SEC = 2 };
+ enum { REQUEST_TIMEOUT_SEC = 2 };
+
+ State _state { State::INIT };
+ Timer::One_shot_timeout _timeout;
+ unsigned long _lease_time_sec = 0;
+ Genode::Microseconds const _discover_timeout { (Genode::uint64_t)DISCOVER_TIMEOUT_SEC * 1000 * 1000 };
+ Genode::Microseconds const _request_timeout { (Genode::uint64_t)REQUEST_TIMEOUT_SEC * 1000 * 1000 };
+ Nic_perf::Interface &_interface;
+
+ void _handle_dhcp_reply(Dhcp_packet &dhcp);
+
+ void _handle_timeout(Genode::Duration);
+
+ void _rerequest(State next_state);
+
+ Genode::Microseconds _rerequest_timeout(unsigned lease_time_div_log2);
+
+ void _set_state(State state, Genode::Microseconds timeout);
+
+ void _send(Dhcp_packet::Message_type msg_type,
+ Ipv4_address client_ip,
+ Ipv4_address server_ip,
+ Ipv4_address requested_ip);
+
+ void _discover();
+
+ public:
+
+ Dhcp_client(Timer::Connection &timer,
+ Nic_perf::Interface &interface);
+
+ void handle_dhcp(Dhcp_packet &dhcp,
+ Ethernet_frame ð,
+ Size_guard &size_guard);
+
+};
+
+#endif /* _DHCP_CLIENT_H_ */
diff --git a/repos/os/src/server/nic_perf/interface.cc b/repos/os/src/server/nic_perf/interface.cc
new file mode 100644
index 0000000000..3c1bab3e83
--- /dev/null
+++ b/repos/os/src/server/nic_perf/interface.cc
@@ -0,0 +1,254 @@
+/*
+ * \brief Base class for Nic/Uplink session components
+ * \author Johannes Schlatow
+ * \date 2022-06-15
+ */
+
+/*
+ * Copyright (C) 2022 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
+
+/* Genode includes */
+#include
+#include
+#include
+
+
+void Nic_perf::Interface::_handle_eth(void * pkt_base, size_t size)
+{
+ try {
+
+ Size_guard size_guard(size);
+ Ethernet_frame ð = Ethernet_frame::cast_from(pkt_base, size_guard);
+
+ switch (eth.type()) {
+ case Ethernet_frame::Type::ARP:
+ _handle_arp(eth, size_guard);
+ break;
+ case Ethernet_frame::Type::IPV4:
+ _handle_ip(eth, size_guard);
+ break;
+ default:
+ ;
+ }
+ } catch (Size_guard::Exceeded) {
+ warning("Size guard exceeded");
+ } catch (Net::Drop_packet_inform e) {
+ error(e.msg);
+ }
+
+ _stats.rx_packet(size);
+}
+
+
+void Nic_perf::Interface::_handle_arp(Ethernet_frame & eth, Size_guard & size_guard)
+{
+ Arp_packet &arp = eth.data(size_guard);
+ if (!arp.ethernet_ipv4())
+ return;
+
+ Ipv4_address old_src_ip { };
+
+ switch (arp.opcode()) {
+ case Arp_packet::REPLY:
+ _generator.handle_arp_reply(arp);
+
+ break;
+
+ case Arp_packet::REQUEST:
+ /* check whether the request targets us */
+ if (arp.dst_ip() != _ip)
+ return;
+
+ old_src_ip = arp.src_ip();
+ arp.opcode(Arp_packet::REPLY);
+ arp.dst_mac(arp.src_mac());
+ arp.src_mac(_mac);
+ arp.src_ip(arp.dst_ip());
+ arp.dst_ip(old_src_ip);
+ eth.dst(arp.dst_mac());
+ eth.src(_mac);
+
+ send(size_guard.total_size(), [&] (void * pkt_base, Size_guard & size_guard) {
+ memcpy(pkt_base, (void*)ð, size_guard.total_size());
+ });
+ break;
+
+ default:
+ ;
+ }
+}
+
+
+void Nic_perf::Interface::_handle_ip(Ethernet_frame & eth, Size_guard & size_guard)
+{
+ Ipv4_packet &ip = eth.data(size_guard);
+ if (ip.protocol() == Ipv4_packet::Protocol::UDP) {
+
+ Udp_packet &udp = ip.data(size_guard);
+ if (Dhcp_packet::is_dhcp(&udp)) {
+ Dhcp_packet &dhcp = udp.data(size_guard);
+ switch (dhcp.op()) {
+ case Dhcp_packet::REQUEST:
+ _handle_dhcp_request(eth, dhcp);
+ break;
+ case Dhcp_packet::REPLY:
+ if (_dhcp_client.constructed()) {
+ _dhcp_client->handle_dhcp(dhcp, eth, size_guard);
+ }
+ break;
+ }
+ }
+ }
+}
+
+
+void Nic_perf::Interface::_handle_dhcp_request(Ethernet_frame & eth, Dhcp_packet & dhcp)
+{
+ Dhcp_packet::Message_type const msg_type =
+ dhcp.option().value();
+
+ switch (msg_type) {
+ case Dhcp_packet::Message_type::DISCOVER:
+ _send_dhcp_reply(eth, dhcp, Dhcp_packet::Message_type::OFFER);
+ break;
+ case Dhcp_packet::Message_type::REQUEST:
+ _send_dhcp_reply(eth, dhcp, Dhcp_packet::Message_type::ACK);
+ break;
+ default:
+ ;
+ }
+}
+
+
+void Nic_perf::Interface::_send_dhcp_reply(Ethernet_frame const & eth_req,
+ Dhcp_packet const & dhcp_req,
+ Dhcp_packet::Message_type msg_type)
+{
+ if (_ip == Ipv4_address())
+ return;
+
+ if (_dhcp_client_ip == Ipv4_address())
+ return;
+
+ enum { PKT_SIZE = 512 };
+ send(PKT_SIZE, [&] (void *pkt_base, Size_guard &size_guard) {
+
+ Ethernet_frame ð = Ethernet_frame::construct_at(pkt_base, size_guard);
+ if (msg_type == Dhcp_packet::Message_type::OFFER) {
+ eth.dst(Ethernet_frame::broadcast()); }
+ else {
+ eth.dst(eth_req.src()); }
+ eth.src(_mac);
+ eth.type(Ethernet_frame::Type::IPV4);
+
+ /* create IP header of the reply */
+ size_t const ip_off = size_guard.head_size();
+ Ipv4_packet &ip = eth.construct_at_data(size_guard);
+ ip.header_length(sizeof(Ipv4_packet) / 4);
+ ip.version(4);
+ ip.time_to_live(64);
+ ip.protocol(Ipv4_packet::Protocol::UDP);
+ ip.src(_ip);
+ ip.dst(_dhcp_client_ip);
+
+ /* create UDP header of the reply */
+ size_t const udp_off = size_guard.head_size();
+ Udp_packet &udp = ip.construct_at_data(size_guard);
+ udp.src_port(Port(Dhcp_packet::BOOTPS));
+ udp.dst_port(Port(Dhcp_packet::BOOTPC));
+
+ /* create mandatory DHCP fields of the reply */
+ Dhcp_packet &dhcp = udp.construct_at_data(size_guard);
+ dhcp.op(Dhcp_packet::REPLY);
+ dhcp.htype(Dhcp_packet::Htype::ETH);
+ dhcp.hlen(sizeof(Mac_address));
+ dhcp.xid(dhcp_req.xid());
+ if (msg_type == Dhcp_packet::Message_type::INFORM) {
+ dhcp.ciaddr(_dhcp_client_ip); }
+ else {
+ dhcp.yiaddr(_dhcp_client_ip); }
+ dhcp.siaddr(_ip);
+ dhcp.client_mac(dhcp_req.client_mac());
+ dhcp.default_magic_cookie();
+
+ /* append DHCP option fields to the reply */
+ Dhcp_packet::Options_aggregator dhcp_opts(dhcp, size_guard);
+ dhcp_opts.append_option(msg_type);
+ dhcp_opts.append_option(_ip);
+ dhcp_opts.append_option(86400);
+ dhcp_opts.append_option(_subnet_mask());
+ dhcp_opts.append_option(_ip);
+
+ dhcp_opts.append_dns_server([&] (Dhcp_options::Dns_server_data &data) {
+ data.append_address(_ip);
+ });
+ dhcp_opts.append_option(Ipv4_packet::broadcast());
+ dhcp_opts.append_option();
+
+ /* fill in header values that need the packet to be complete already */
+ udp.length(size_guard.head_size() - udp_off);
+ udp.update_checksum(ip.src(), ip.dst());
+ ip.total_length(size_guard.head_size() - ip_off);
+ ip.update_checksum();
+ });
+}
+
+
+void Nic_perf::Interface::handle_packet_stream()
+{
+ /* handle acks from client */
+ while (_source.ack_avail())
+ _source.release_packet(_source.try_get_acked_packet());
+
+ /* loop while we can make Rx progress */
+ for (;;) {
+ if (!_sink.ready_to_ack())
+ break;
+
+ if (!_sink.packet_avail())
+ break;
+
+ Packet_descriptor const packet_from_client = _sink.try_get_packet();
+
+ if (_sink.packet_valid(packet_from_client)) {
+ _handle_eth(_sink.packet_content(packet_from_client), packet_from_client.size());
+ if (!_sink.try_ack_packet(packet_from_client))
+ break;
+ }
+ }
+
+ /* skip sending if disabled or IP address is not set */
+ if (!_generator.enabled() || _ip == Ipv4_address()) {
+ _sink.wakeup();
+ _source.wakeup();
+ return;
+ }
+
+ /* loop while we can make Tx progress */
+ for (;;) {
+ /*
+ * The client fails to pick up the packets from the rx channel. So we
+ * won't try to submit new packets.
+ */
+ if (!_source.ready_to_submit())
+ break;
+
+ bool okay =
+ send(_generator.size(), [&] (void * pkt_base, Size_guard & size_guard) {
+ _generator.generate(pkt_base, size_guard, _mac, _ip);
+ });
+
+ if (!okay)
+ break;
+ }
+
+ _sink.wakeup();
+ _source.wakeup();
+}
diff --git a/repos/os/src/server/nic_perf/interface.h b/repos/os/src/server/nic_perf/interface.h
new file mode 100644
index 0000000000..4e2fab2ed0
--- /dev/null
+++ b/repos/os/src/server/nic_perf/interface.h
@@ -0,0 +1,153 @@
+/*
+ * \brief Base class for Nic/Uplink session components
+ * \author Johannes Schlatow
+ * \date 2022-06-15
+ */
+
+/*
+ * Copyright (C) 2022 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 _INTERFACE_H_
+#define _INTERFACE_H_
+
+/* local includes */
+#include
+#include
+#include
+
+/* Genode includes */
+#include
+#include
+#include
+#include
+#include
+
+namespace Nic_perf {
+ using namespace Genode;
+ using namespace Net;
+
+ using Dhcp_options = Dhcp_packet::Options_aggregator;
+
+ class Interface;
+
+ using Interface_registry = Registry;
+}
+
+class Nic_perf::Interface
+{
+ protected:
+
+ using Sink = Nic::Packet_stream_sink;
+ using Source = Nic::Packet_stream_source;
+
+ Interface_registry::Element _element;
+ Session_label _label;
+
+ Packet_stats _stats;
+ Packet_generator _generator;
+
+ bool _mac_from_policy;
+
+ Mac_address _mac { };
+ Mac_address const _default_mac;
+ Ipv4_address _ip { };
+ Ipv4_address _dhcp_client_ip { };
+
+ Source &_source;
+ Sink &_sink;
+
+ Constructible _dhcp_client { };
+ Timer::Connection &_timer;
+
+ static Ipv4_address _subnet_mask()
+ {
+ uint8_t buf[] = { 0xff, 0xff, 0xff, 0 };
+ return Ipv4_address((void*)buf);
+ }
+
+ void _handle_eth(void *, size_t);
+ void _handle_ip(Ethernet_frame &, Size_guard &);
+ void _handle_arp(Ethernet_frame &, Size_guard &);
+ void _handle_dhcp_request(Ethernet_frame &, Dhcp_packet &);
+ void _send_dhcp_reply(Ethernet_frame const &, Dhcp_packet const &, Dhcp_packet::Message_type);
+
+ public:
+
+ Interface(Interface_registry ®istry,
+ Session_label const &label,
+ Xml_node const &policy,
+ bool mac_from_policy,
+ Mac_address mac,
+ Source &source,
+ Sink &sink,
+ Timer::Connection &timer)
+ : _element(registry, *this),
+ _label(label),
+ _stats(_label),
+ _generator(timer, *this),
+ _mac_from_policy(mac_from_policy),
+ _default_mac(mac),
+ _source(source),
+ _sink(sink),
+ _timer(timer)
+ { apply_config(policy); }
+
+ void apply_config(Xml_node const &config)
+ {
+ _generator.apply_config(config);
+
+ /* restore defaults when applied to empty/incomplete config */
+ _mac = _default_mac;
+ _ip = Ipv4_address();
+ _dhcp_client_ip = Ipv4_address();
+
+ _dhcp_client.destruct();
+
+ config.with_sub_node("interface", [&] (Xml_node node) {
+ _ip = node.attribute_value("ip", _ip);
+ _dhcp_client_ip = node.attribute_value("dhcp_client_ip", _dhcp_client_ip);
+
+ if (_mac_from_policy)
+ _mac = node.attribute_value("mac", _mac);
+ });
+
+ if (_ip == Ipv4_address())
+ _dhcp_client.construct(_timer, *this);
+ }
+
+ Session_label const &label() const { return _label; }
+ Packet_stats &packet_stats() { return _stats; }
+
+ Mac_address const &mac() const { return _mac; }
+ Ipv4_address const &ip() const { return _ip; }
+ void ip(Ipv4_address const &ip) { _ip = ip; }
+
+ void handle_packet_stream();
+
+ template
+ bool send(size_t pkt_size, FUNC && write_to_pkt)
+ {
+ if (!pkt_size)
+ return false;
+
+ try {
+ Packet_descriptor pkt = _source.alloc_packet(pkt_size);
+ void *pkt_base = _source.packet_content(pkt);
+
+ Size_guard size_guard { pkt_size };
+ write_to_pkt(pkt_base, size_guard);
+
+ _source.try_submit_packet(pkt);
+ } catch (...) { return false; }
+
+ _stats.tx_packet(pkt_size);
+
+ return true;
+ }
+};
+
+#endif /* _INTERFACE_H_ */
diff --git a/repos/os/src/server/nic_perf/main.cc b/repos/os/src/server/nic_perf/main.cc
new file mode 100644
index 0000000000..44373ef1f4
--- /dev/null
+++ b/repos/os/src/server/nic_perf/main.cc
@@ -0,0 +1,123 @@
+/*
+ * \brief Throughput benchmark component for Nic and Uplink sessions
+ * \author Johannes Schlatow
+ * \date 2022-06-14
+ *
+ * This component continously sends/receives UDP packets via a Nic or Uplink
+ * session in order to benchmark the throughput.
+ */
+
+/*
+ * Copyright (C) 2022 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
+#include
+#include
+#include
+
+/* Genode includes */
+#include
+#include
+#include
+#include
+#include
+#include
+
+namespace Nic_perf {
+ class Main;
+
+ using namespace Genode;
+}
+
+
+struct Nic_perf::Main
+{
+ using Periodic_timeout = Timer::Periodic_timeout;
+
+ Env &_env;
+
+ Heap _heap { _env.ram(), _env.rm() };
+
+ Timer::Connection _timer { _env };
+
+ Attached_rom_dataspace _config { _env, "config" };
+
+ unsigned _period_ms { 5000 };
+
+ unsigned _count { 10000 };
+
+ Interface_registry _registry { };
+
+ Nic_perf::Nic_root _nic_root { _env, _heap, _registry, _config, _timer };
+
+ Nic_perf::Uplink_root _uplink_root { _env, _heap, _registry, _config, _timer };
+
+ Constructible _nic_client { };
+
+ Genode::Signal_handler _config_handler =
+ { _env.ep(), *this, &Main::_handle_config };
+
+ Constructible _timeout { };
+
+ void _handle_config()
+ {
+ _config.update();
+
+ _registry.for_each([&] (Interface &interface) {
+ with_matching_policy(interface.label(), _config.xml(),
+ [&] (Xml_node const &policy) {
+ interface.apply_config(policy);
+ },
+ [&] () { /* no matches */
+ interface.apply_config(Xml_node(""));
+ }
+ );
+ });
+
+ if (_nic_client.constructed())
+ _nic_client.destruct();
+
+ if (_config.xml().has_sub_node("nic-client"))
+ _nic_client.construct(_env, _heap, _config.xml().sub_node("nic-client"), _registry, _timer);
+
+ _period_ms = _config.xml().attribute_value("period_ms", _period_ms);
+ _count = _config.xml().attribute_value("count", _count);
+
+ _timeout.conditional(_count && _period_ms,
+ _timer, *this, &Main::_handle_timeout, Microseconds(_period_ms*1000));
+ }
+
+ void _handle_timeout(Genode::Duration)
+ {
+ _registry.for_each([&] (Interface &interface) {
+ Packet_stats &stats = interface.packet_stats();
+
+ stats.calculate_throughput(_period_ms);
+ log(stats);
+ stats.reset();
+ });
+
+ _count--;
+ if (!_count)
+ _env.parent().exit(0);
+ }
+
+ Main(Env &env) : _env(env)
+ {
+ _env.parent().announce(_env.ep().manage(_nic_root));
+ _env.parent().announce(_env.ep().manage(_uplink_root));
+
+ _config.sigh(_config_handler);
+
+ _handle_config();
+ }
+};
+
+
+void Component::construct(Genode::Env &env) { static Nic_perf::Main main(env); }
+
diff --git a/repos/os/src/server/nic_perf/nic_client.h b/repos/os/src/server/nic_perf/nic_client.h
new file mode 100644
index 0000000000..c88c07b9d8
--- /dev/null
+++ b/repos/os/src/server/nic_perf/nic_client.h
@@ -0,0 +1,71 @@
+/*
+ * \brief Nic client
+ * \author Johannes Schlatow
+ * \date 2022-06-16
+ */
+
+/*
+ * Copyright (C) 2022 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 _NIC_CLIENT_H_
+#define _NIC_CLIENT_H_
+
+/* local includes */
+#include
+
+/* Genode includes */
+#include
+#include
+#include
+
+namespace Nic_perf {
+ class Nic_client;
+
+ using namespace Genode;
+}
+
+
+class Nic_perf::Nic_client
+{
+ private:
+ enum { BUF_SIZE = Nic::Session::QUEUE_SIZE * Nic::Packet_allocator::DEFAULT_PACKET_SIZE };
+
+ Env &_env;
+ Nic::Packet_allocator _pkt_alloc;
+ Nic::Connection _nic { _env, &_pkt_alloc, BUF_SIZE, BUF_SIZE };
+ Interface _interface;
+
+ Signal_handler _packet_stream_handler
+ { _env.ep(), *this, &Nic_client::_handle_packet_stream };
+
+ void _handle_packet_stream() {
+ _interface.handle_packet_stream(); }
+
+ public:
+
+ Nic_client(Env &env,
+ Genode::Allocator &alloc,
+ Xml_node const &policy,
+ Interface_registry ®istry,
+ Timer::Connection &timer)
+ :
+ _env(env),
+ _pkt_alloc(&alloc),
+ _interface(registry, "nic-client", policy, false, Mac_address(),
+ *_nic.tx(), *_nic.rx(), timer)
+ {
+ _nic.rx_channel()->sigh_ready_to_ack(_packet_stream_handler);
+ _nic.rx_channel()->sigh_packet_avail(_packet_stream_handler);
+ _nic.tx_channel()->sigh_ack_avail(_packet_stream_handler);
+ _nic.tx_channel()->sigh_ready_to_submit(_packet_stream_handler);
+
+ _interface.handle_packet_stream();
+ }
+};
+
+
+#endif /* _NIC_CLIENT_H_ */
diff --git a/repos/os/src/server/nic_perf/nic_component.h b/repos/os/src/server/nic_perf/nic_component.h
new file mode 100644
index 0000000000..66898cf148
--- /dev/null
+++ b/repos/os/src/server/nic_perf/nic_component.h
@@ -0,0 +1,142 @@
+/*
+ * \brief Nic root and session component
+ * \author Johannes Schlatow
+ * \date 2022-06-16
+ */
+
+/*
+ * Copyright (C) 2022 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 _NIC_ROOT_H_
+#define _NIC_ROOT_H_
+
+/* local includes */
+#include
+
+/* Genode includes */
+#include
+#include
+#include
+#include
+
+namespace Nic_perf {
+ class Nic_session_component;
+ class Nic_root;
+
+ using namespace Genode;
+}
+
+
+class Nic_perf::Nic_session_component : public Nic::Session_component
+{
+ private:
+
+ Interface _interface;
+
+ static Mac_address _default_mac_address()
+ {
+ char buf[] = {2,3,4,5,6,7};
+ Mac_address result((void*)buf);
+ return result;
+ }
+
+ void _handle_packet_stream() override {
+ _interface.handle_packet_stream(); }
+
+ public:
+
+ Nic_session_component(size_t const tx_buf_size,
+ size_t const rx_buf_size,
+ Allocator &rx_block_md_alloc,
+ Env &env,
+ Session_label const &label,
+ Xml_node const &policy,
+ Interface_registry ®istry,
+ Timer::Connection &timer)
+ :
+ Nic::Session_component(tx_buf_size, rx_buf_size, CACHED,
+ rx_block_md_alloc, env),
+ _interface(registry, label, policy, true, _default_mac_address(),
+ *_rx.source(), *_tx.sink(), timer)
+ { _interface.handle_packet_stream(); }
+
+ /*****************************
+ * Session_component methods *
+ *****************************/
+
+ Nic::Mac_address mac_address() override {
+ char buf[] = {2,3,4,5,6,8};
+ Mac_address result((void*)buf);
+ return result;
+ }
+
+ bool link_state() override
+ {
+ /* XXX always return true, for now */
+ return true;
+ }
+};
+
+
+class Nic_perf::Nic_root : public Root_component
+{
+ private:
+ Env &_env;
+ Attached_rom_dataspace &_config;
+ Interface_registry &_registry;
+ Timer::Connection &_timer;
+
+ protected:
+
+ Nic_session_component *_create_session(char const *args) override
+ {
+ size_t ram_quota = Arg_string::find_arg(args, "ram_quota" ).ulong_value(0);
+ size_t tx_buf_size = Arg_string::find_arg(args, "tx_buf_size").ulong_value(0);
+ size_t rx_buf_size = Arg_string::find_arg(args, "rx_buf_size").ulong_value(0);
+
+ /* deplete ram quota by the memory needed for the session structure */
+ size_t session_size = max(4096UL, (size_t)sizeof(Nic_session_component));
+ if (ram_quota < session_size)
+ throw Insufficient_ram_quota();
+
+ /*
+ * Check if donated ram quota suffices for both communication
+ * buffers and check for overflow
+ */
+ if (tx_buf_size + rx_buf_size < tx_buf_size ||
+ tx_buf_size + rx_buf_size > ram_quota - session_size) {
+ error("insufficient 'ram_quota', got ", ram_quota, ", "
+ "need ", tx_buf_size + rx_buf_size + session_size);
+ throw Insufficient_ram_quota();
+ }
+
+ Session_label label = label_from_args(args);
+
+ Session_policy policy(label, _config.xml());
+
+ return new (md_alloc()) Nic_session_component(tx_buf_size, rx_buf_size,
+ *md_alloc(), _env, label, policy,
+ _registry, _timer);
+ }
+
+ public:
+
+ Nic_root(Env &env,
+ Allocator &md_alloc,
+ Interface_registry ®istry,
+ Attached_rom_dataspace &config,
+ Timer::Connection &timer)
+ :
+ Root_component(&env.ep().rpc_ep(), &md_alloc),
+ _env(env),
+ _config(config),
+ _registry(registry),
+ _timer(timer)
+ { }
+};
+
+#endif /* _NIC_ROOT_H_ */
diff --git a/repos/os/src/server/nic_perf/packet_generator.cc b/repos/os/src/server/nic_perf/packet_generator.cc
new file mode 100644
index 0000000000..8825f63e44
--- /dev/null
+++ b/repos/os/src/server/nic_perf/packet_generator.cc
@@ -0,0 +1,134 @@
+/*
+ * \brief Packet generator
+ * \author Johannes Schlatow
+ * \date 2022-06-14
+ */
+
+/*
+ * Copyright (C) 2022 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
+#include
+
+void Nic_perf::Packet_generator::_handle_timeout(Genode::Duration)
+{
+ /* re-issue ARP request */
+ if (_state == WAIT_ARP_REPLY)
+ _state = NEED_ARP_REQUEST;
+
+ _interface.handle_packet_stream();
+}
+
+void Nic_perf::Packet_generator::handle_arp_reply(Arp_packet const & arp)
+{
+ if (arp.src_ip() != _dst_ip)
+ return;
+
+ if (_state != WAIT_ARP_REPLY)
+ return;
+
+ _timeout.discard();
+
+ _dst_mac = arp.src_mac();
+ _state = READY;
+}
+
+
+void Nic_perf::Packet_generator::_generate_arp_request(void * pkt_base,
+ Size_guard & size_guard,
+ Mac_address const & from_mac,
+ Ipv4_address const & from_ip)
+{
+ if (from_ip == Ipv4_address()) {
+ error("Ip address not set");
+ throw Ip_address_not_set();
+ }
+
+ Ethernet_frame ð = Ethernet_frame::construct_at(pkt_base, size_guard);
+ eth.dst(Mac_address(0xff));
+ eth.src(from_mac);
+ eth.type(Ethernet_frame::Type::ARP);
+
+ Arp_packet &arp = eth.construct_at_data(size_guard);
+ arp.hardware_address_type(Arp_packet::ETHERNET);
+ arp.protocol_address_type(Arp_packet::IPV4);
+ arp.hardware_address_size(sizeof(Mac_address));
+ arp.protocol_address_size(sizeof(Ipv4_address));
+ arp.opcode(Arp_packet::REQUEST);
+ arp.src_mac(from_mac);
+ arp.src_ip(from_ip);
+ arp.dst_mac(Mac_address(0xff));
+ arp.dst_ip(_dst_ip);
+}
+
+
+void Nic_perf::Packet_generator::_generate_test_packet(void * pkt_base,
+ Size_guard & size_guard,
+ Mac_address const & from_mac,
+ Ipv4_address const & from_ip)
+{
+ if (from_ip == Ipv4_address()) {
+ error("Ip address not set");
+ throw Ip_address_not_set();
+ }
+
+ if (_dst_port == Port(0)) {
+ error("Udp port not set");
+ throw Udp_port_not_set();
+ }
+
+ Ethernet_frame ð = Ethernet_frame::construct_at(pkt_base, size_guard);
+ eth.dst(_dst_mac);
+ eth.src(from_mac);
+ eth.type(Ethernet_frame::Type::IPV4);
+
+ size_t const ip_off = size_guard.head_size();
+ Ipv4_packet &ip = eth.construct_at_data(size_guard);
+ ip.header_length(sizeof(Ipv4_packet) / 4);
+ ip.version(4);
+ ip.time_to_live(64);
+ ip.protocol(Ipv4_packet::Protocol::UDP);
+ ip.src(from_ip);
+ ip.dst(_dst_ip);
+
+ size_t udp_off = size_guard.head_size();
+ Udp_packet &udp = ip.construct_at_data(size_guard);
+ udp.src_port(Port(0));
+ udp.dst_port(_dst_port);
+
+ /* inflate packet up to _mtu */
+ size_guard.consume_head(size_guard.unconsumed());
+
+ /* fill in length fields and checksums */
+ udp.length(size_guard.head_size() - udp_off);
+ udp.update_checksum(ip.src(), ip.dst());
+ ip.total_length(size_guard.head_size() - ip_off);
+ ip.update_checksum();
+}
+
+
+void Nic_perf::Packet_generator::generate(void * pkt_base,
+ Size_guard & size_guard,
+ Mac_address const & from_mac,
+ Ipv4_address const & from_ip)
+{
+ switch (_state) {
+ case READY:
+ _generate_test_packet(pkt_base, size_guard, from_mac, from_ip);
+ break;
+ case NEED_ARP_REQUEST:
+ _generate_arp_request(pkt_base, size_guard, from_mac, from_ip);
+ _state = WAIT_ARP_REPLY;
+ _timeout.schedule(Microseconds { 1000 * 1000 });
+ break;
+ case MUTED:
+ case WAIT_ARP_REPLY:
+ throw Not_ready();
+ break;
+ }
+}
diff --git a/repos/os/src/server/nic_perf/packet_generator.h b/repos/os/src/server/nic_perf/packet_generator.h
new file mode 100644
index 0000000000..ffb73fe1d2
--- /dev/null
+++ b/repos/os/src/server/nic_perf/packet_generator.h
@@ -0,0 +1,116 @@
+/*
+ * \brief Packet generator
+ * \author Johannes Schlatow
+ * \date 2022-06-14
+ */
+
+/*
+ * Copyright (C) 2022 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 _PACKET_GENERATOR_H_
+#define _PACKET_GENERATOR_H_
+
+/* Genode includes */
+#include
+#include
+#include
+#include
+#include
+#include
+
+namespace Nic_perf {
+ using namespace Genode;
+ using namespace Net;
+
+ class Interface;
+ class Packet_generator;
+}
+
+class Nic_perf::Packet_generator
+{
+ public:
+ struct Not_ready : Exception { };
+ struct Ip_address_not_set : Exception { };
+ struct Udp_port_not_set : Exception { };
+
+ private:
+
+ enum State { MUTED, NEED_ARP_REQUEST, WAIT_ARP_REPLY, READY };
+
+ size_t _mtu { 1024 };
+ bool _enable { false };
+ Ipv4_address _dst_ip { };
+ Port _dst_port { 0 };
+ Mac_address _dst_mac { };
+ State _state { MUTED };
+
+ Timer::One_shot_timeout _timeout;
+ Nic_perf::Interface &_interface;
+
+ void _generate_arp_request(void *, Size_guard &, Mac_address const &, Ipv4_address const &);
+
+ void _generate_test_packet(void *, Size_guard &, Mac_address const &, Ipv4_address const &);
+
+ void _handle_timeout(Genode::Duration);
+
+ public:
+
+ Packet_generator(Timer::Connection &timer, Nic_perf::Interface &interface)
+ : _timeout(timer, *this, &Packet_generator::_handle_timeout),
+ _interface(interface)
+ { }
+
+ void apply_config(Xml_node const &config)
+ {
+ Ipv4_address old_ip = _dst_ip;
+
+ /* restore defaults */
+ _dst_ip = Ipv4_address();
+ _dst_port = Port(0);
+ _enable = false;
+ _state = MUTED;
+
+ config.with_sub_node("tx", [&] (Xml_node node) {
+ _mtu = node.attribute_value("mtu", _mtu);
+ _dst_ip = node.attribute_value("to", _dst_ip);
+ _dst_port = node.attribute_value("udp_port", _dst_port);
+ _enable = true;
+ _state = READY;
+ });
+
+ /* redo ARP resolution if dst ip changed */
+ if (old_ip != _dst_ip) {
+ _dst_mac = Mac_address();
+
+ if (_enable)
+ _state = NEED_ARP_REQUEST;
+ }
+ }
+
+ bool enabled() const { return _enable; }
+
+ size_t size() const
+ {
+ switch (_state) {
+ case READY:
+ return _mtu;
+ case NEED_ARP_REQUEST:
+ return Ethernet_frame::MIN_SIZE + sizeof(uint32_t);
+ case WAIT_ARP_REPLY:
+ case MUTED:
+ return 0;
+ }
+
+ return 0;
+ }
+
+ void handle_arp_reply(Arp_packet const & arp);
+
+ void generate(void *, Size_guard &, Mac_address const &, Ipv4_address const &);
+};
+
+#endif /* _PACKET_GENERATOR_H_ */
diff --git a/repos/os/src/server/nic_perf/packet_stats.h b/repos/os/src/server/nic_perf/packet_stats.h
new file mode 100644
index 0000000000..f61835a069
--- /dev/null
+++ b/repos/os/src/server/nic_perf/packet_stats.h
@@ -0,0 +1,90 @@
+/*
+ * \brief Packet statistics
+ * \author Johannes Schlatow
+ * \date 2022-06-14
+ */
+
+/*
+ * Copyright (C) 2022 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 _PACKET_STATS_H_
+#define _PACKET_STATS_H_
+
+/* Genode includes */
+#include
+
+namespace Nic_perf {
+ using namespace Genode;
+
+ class Packet_stats;
+}
+
+class Nic_perf::Packet_stats
+{
+ private:
+
+ Session_label const &_label;
+
+ size_t _sent_cnt { 0 };
+ size_t _recv_cnt { 0 };
+ size_t _sent_bytes { 0 };
+ size_t _recv_bytes { 0 };
+ unsigned _period_ms { 0 };
+ float _rx_mbit_sec { 0.0 };
+ float _tx_mbit_sec { 0.0 };
+
+ public:
+
+ Packet_stats(Session_label const & label)
+ : _label(label)
+ { }
+
+ void reset()
+ {
+ _sent_cnt = 0;
+ _recv_cnt = 0;
+ _sent_bytes = 0;
+ _recv_bytes = 0;
+ _rx_mbit_sec = 0;
+ _tx_mbit_sec = 0;
+ }
+
+ void rx_packet(size_t bytes)
+ {
+ _recv_cnt++;
+ _recv_bytes += bytes;
+ }
+
+ void tx_packet(size_t bytes)
+ {
+ _sent_cnt++;
+ _sent_bytes += bytes;
+ }
+
+ void calculate_throughput(unsigned period_ms)
+ {
+ _period_ms = period_ms;
+
+ if (_period_ms == 0) return;
+
+ _rx_mbit_sec = (float)(_recv_bytes * 8ULL) / (float)(period_ms*1000ULL);
+ _tx_mbit_sec = (float)(_sent_bytes * 8ULL) / (float)(period_ms*1000ULL);
+ }
+
+ void print(Output &out) const
+ {
+ Genode::print(out, "# Stats for session ", _label, "\n");
+ Genode::print(out, " Received ", _recv_cnt, " packets in ",
+ _period_ms, "ms at ", _rx_mbit_sec, "Mbit/s\n");
+ Genode::print(out, " Sent ", _sent_cnt, " packets in ",
+ _period_ms, "ms at ", _tx_mbit_sec, "Mbit/s\n");
+ }
+
+};
+
+
+#endif /* _PACKET_STATS_H_ */
diff --git a/repos/os/src/server/nic_perf/target.mk b/repos/os/src/server/nic_perf/target.mk
new file mode 100644
index 0000000000..5445365b49
--- /dev/null
+++ b/repos/os/src/server/nic_perf/target.mk
@@ -0,0 +1,7 @@
+TARGET = nic_perf
+SRC_CC = main.cc interface.cc packet_generator.cc dhcp_client.cc
+LIBS = base net
+
+INC_DIR += $(PRG_DIR)
+
+CC_CXX_WARN_STRICT_CONVERSION =
diff --git a/repos/os/src/server/nic_perf/uplink_component.h b/repos/os/src/server/nic_perf/uplink_component.h
new file mode 100644
index 0000000000..48098f5eca
--- /dev/null
+++ b/repos/os/src/server/nic_perf/uplink_component.h
@@ -0,0 +1,195 @@
+/*
+ * \brief Uplink root and session component
+ * \author Johannes Schlatow
+ * \date 2022-06-17
+ */
+
+/*
+ * Copyright (C) 2022 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 _UPLINK_ROOT_H_
+#define _UPLINK_ROOT_H_
+
+/* local includes */
+#include
+
+/* Genode includes */
+#include
+#include
+#include
+#include
+#include
+
+namespace Nic_perf {
+ class Uplink_session_base;
+ class Uplink_session_component;
+ class Uplink_root;
+
+ using namespace Genode;
+}
+
+
+class Nic_perf::Uplink_session_base
+{
+ friend class Uplink_session_component;
+
+ private:
+
+ class Buffer
+ {
+ private:
+
+ Ram_allocator &_ram_alloc;
+ Ram_dataspace_capability _ram_ds;
+
+ public:
+
+ Buffer(Ram_allocator &ram_alloc,
+ size_t const size)
+ :
+ _ram_alloc { ram_alloc },
+ _ram_ds { ram_alloc.alloc(size) }
+ { }
+
+ ~Buffer() { _ram_alloc.free(_ram_ds); }
+
+ Dataspace_capability ds() const { return _ram_ds; }
+ };
+
+ Env &_env;
+ Allocator &_alloc;
+ Nic::Packet_allocator _packet_alloc;
+ Buffer _tx_buf;
+ Buffer _rx_buf;
+
+ public:
+
+ Uplink_session_base(Env &env,
+ size_t tx_buf_size,
+ size_t rx_buf_size,
+ Allocator &alloc)
+ :
+ _env { env },
+ _alloc { alloc },
+ _packet_alloc { &_alloc },
+ _tx_buf { _env.ram(), tx_buf_size },
+ _rx_buf { _env.ram(), rx_buf_size }
+ { }
+};
+
+
+class Nic_perf::Uplink_session_component : private Uplink_session_base,
+ public Uplink::Session_rpc_object
+{
+ private:
+
+ Interface _interface;
+ Signal_handler _packet_stream_handler
+ { _env.ep(), *this, &Uplink_session_component::_handle_packet_stream };
+
+ void _handle_packet_stream() {
+ _interface.handle_packet_stream(); }
+
+ public:
+
+ Uplink_session_component(size_t const tx_buf_size,
+ size_t const rx_buf_size,
+ Allocator &alloc,
+ Env &env,
+ Session_label const &label,
+ Xml_node const &policy,
+ Interface_registry ®istry,
+ Mac_address mac,
+ Timer::Connection &timer)
+ :
+ Uplink_session_base(env, tx_buf_size, rx_buf_size, alloc),
+ Uplink::Session_rpc_object(env.rm(), _tx_buf.ds(), _rx_buf.ds(),
+ &_packet_alloc, env.ep().rpc_ep()),
+ _interface(registry, label, policy, false, mac,
+ *_rx.source(), *_tx.sink(), timer)
+ {
+ _interface.handle_packet_stream();
+
+ _tx.sigh_ready_to_ack (_packet_stream_handler);
+ _tx.sigh_packet_avail (_packet_stream_handler);
+ _rx.sigh_ack_avail (_packet_stream_handler);
+ _rx.sigh_ready_to_submit(_packet_stream_handler);
+ }
+};
+
+
+class Nic_perf::Uplink_root : public Root_component
+{
+ private:
+ Env &_env;
+ Attached_rom_dataspace &_config;
+ Interface_registry &_registry;
+ Timer::Connection &_timer;
+
+ protected:
+
+ Uplink_session_component *_create_session(char const *args) override
+ {
+ size_t ram_quota = Arg_string::find_arg(args, "ram_quota" ).ulong_value(0);
+ size_t tx_buf_size = Arg_string::find_arg(args, "tx_buf_size").ulong_value(0);
+ size_t rx_buf_size = Arg_string::find_arg(args, "rx_buf_size").ulong_value(0);
+
+ /* deplete ram quota by the memory needed for the session structure */
+ size_t session_size = max(4096UL, (size_t)sizeof(Uplink_session_component));
+ if (ram_quota < session_size)
+ throw Insufficient_ram_quota();
+
+ /*
+ * Check if donated ram quota suffices for both communication
+ * buffers and check for overflow
+ */
+ if (tx_buf_size + rx_buf_size < tx_buf_size ||
+ tx_buf_size + rx_buf_size > ram_quota - session_size) {
+ error("insufficient 'ram_quota', got ", ram_quota, ", "
+ "need ", tx_buf_size + rx_buf_size + session_size);
+ throw Insufficient_ram_quota();
+ }
+
+ 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())
+ throw Service_denied();
+
+ mac_arg.string(mac_str, MAC_STR_LENGTH, "");
+ Mac_address mac { };
+ ascii_to(mac_str, mac);
+ if (mac == Mac_address { })
+ throw Service_denied();
+
+ Session_label label = label_from_args(args);
+
+ Session_policy policy(label, _config.xml());
+
+ return new (md_alloc()) Uplink_session_component(tx_buf_size, rx_buf_size,
+ *md_alloc(), _env, label, policy,
+ _registry, mac, _timer);
+ }
+
+ public:
+
+ Uplink_root(Env &env,
+ Allocator &md_alloc,
+ Interface_registry ®istry,
+ Attached_rom_dataspace &config,
+ Timer::Connection &timer)
+ :
+ Root_component(&env.ep().rpc_ep(), &md_alloc),
+ _env(env),
+ _config(config),
+ _registry(registry),
+ _timer(timer)
+ { }
+};
+
+#endif /* _UPLINK_ROOT_H_ */