mirror of
https://github.com/genodelabs/genode.git
synced 2024-12-23 15:32:25 +00:00
os: add nic_perf component
The nic_perf component is used for benchmarking the throughput of Nic and Uplink sessions. genodelabs/genode#4555
This commit is contained in:
parent
8df8f78fe2
commit
3e562bc9bb
46
repos/os/src/server/nic_perf/README
Normal file
46
repos/os/src/server/nic_perf/README
Normal file
@ -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:
|
||||
|
||||
! <config period_ms="5000" count="10000">
|
||||
! <default-policy>
|
||||
! <tx mtu="1500" to="10.0.1.2" udp_port="12345"/>
|
||||
! </default-policy>
|
||||
! <nic-client>
|
||||
! <interface ip="10.0.2.1" dhcp_client_ip="10.0.2.2"/>
|
||||
! </nic-client>
|
||||
! </config>
|
||||
|
||||
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 '<policy>' nodes resp. a '<default-policy>' node. The component
|
||||
opens a single Nic connection if a '<nic-client>' node is provided.
|
||||
|
||||
All sub-nodes comprise an optional '<interface>' node and an optional '<tx>'
|
||||
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.
|
245
repos/os/src/server/nic_perf/dhcp_client.cc
Normal file
245
repos/os/src/server/nic_perf/dhcp_client.cc
Normal file
@ -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 <dhcp_client.h>
|
||||
#include <interface.h>
|
||||
|
||||
/* Genode includes */
|
||||
#include <util/xml_node.h>
|
||||
|
||||
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<Size_guard>;
|
||||
|
||||
|
||||
/***************
|
||||
** 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<Dhcp_packet::Message_type_option>();
|
||||
data.append_param_req<Dhcp_packet::Server_ipv4>();
|
||||
data.append_param_req<Dhcp_packet::Ip_lease_time>();
|
||||
data.append_param_req<Dhcp_packet::Dns_server_ipv4>();
|
||||
data.append_param_req<Dhcp_packet::Subnet_mask>();
|
||||
data.append_param_req<Dhcp_packet::Router_ipv4>();
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
/*****************
|
||||
** 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<Dhcp_packet::Message_type_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<Dhcp_packet::Server_ipv4>().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<Dhcp_packet::Ip_lease_time>().value();
|
||||
_set_state(State::BOUND, _rerequest_timeout(1));
|
||||
Ipv4_address dns_server;
|
||||
try { dns_server = dhcp.option<Dhcp_packet::Dns_server_ipv4>().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<Dhcp_packet::Ip_lease_time>().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<Ipv4_packet>(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<Udp_packet>(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<Dhcp_packet>(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<Dhcp_packet::Message_type_option>(msg_type);
|
||||
switch (msg_type) {
|
||||
case Message_type::DISCOVER:
|
||||
append_param_req_list(dhcp_opts);
|
||||
dhcp_opts.append_option<Dhcp_packet::Client_id>(_interface.mac());
|
||||
dhcp_opts.append_option<Dhcp_packet::Max_msg_size>(PKT_SIZE - dhcp_off);
|
||||
break;
|
||||
|
||||
case Message_type::REQUEST:
|
||||
append_param_req_list(dhcp_opts);
|
||||
dhcp_opts.append_option<Dhcp_packet::Client_id>(_interface.mac());
|
||||
dhcp_opts.append_option<Dhcp_packet::Max_msg_size>(PKT_SIZE - dhcp_off);
|
||||
if (_state == State::REQUEST) {
|
||||
dhcp_opts.append_option<Dhcp_packet::Requested_addr>(requested_ip);
|
||||
dhcp_opts.append_option<Dhcp_packet::Server_ipv4>(server_ip);
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
throw Bad_send_dhcp_args();
|
||||
}
|
||||
dhcp_opts.append_option<Dhcp_packet::Options_end>();
|
||||
|
||||
/* 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();
|
||||
});
|
||||
}
|
89
repos/os/src/server/nic_perf/dhcp_client.h
Normal file
89
repos/os/src/server/nic_perf/dhcp_client.h
Normal file
@ -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 <net/dhcp.h>
|
||||
#include <timer_session/connection.h>
|
||||
|
||||
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<Dhcp_client> _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_ */
|
254
repos/os/src/server/nic_perf/interface.cc
Normal file
254
repos/os/src/server/nic_perf/interface.cc
Normal file
@ -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 <interface.h>
|
||||
|
||||
/* Genode includes */
|
||||
#include <net/mac_address.h>
|
||||
#include <net/ipv4.h>
|
||||
#include <net/udp.h>
|
||||
|
||||
|
||||
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<Arp_packet>(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<Ipv4_packet>(size_guard);
|
||||
if (ip.protocol() == Ipv4_packet::Protocol::UDP) {
|
||||
|
||||
Udp_packet &udp = ip.data<Udp_packet>(size_guard);
|
||||
if (Dhcp_packet::is_dhcp(&udp)) {
|
||||
Dhcp_packet &dhcp = udp.data<Dhcp_packet>(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<Dhcp_packet::Message_type_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<Ipv4_packet>(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<Udp_packet>(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<Dhcp_packet>(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<Size_guard> dhcp_opts(dhcp, size_guard);
|
||||
dhcp_opts.append_option<Dhcp_packet::Message_type_option>(msg_type);
|
||||
dhcp_opts.append_option<Dhcp_packet::Server_ipv4>(_ip);
|
||||
dhcp_opts.append_option<Dhcp_packet::Ip_lease_time>(86400);
|
||||
dhcp_opts.append_option<Dhcp_packet::Subnet_mask>(_subnet_mask());
|
||||
dhcp_opts.append_option<Dhcp_packet::Router_ipv4>(_ip);
|
||||
|
||||
dhcp_opts.append_dns_server([&] (Dhcp_options::Dns_server_data &data) {
|
||||
data.append_address(_ip);
|
||||
});
|
||||
dhcp_opts.append_option<Dhcp_packet::Broadcast_addr>(Ipv4_packet::broadcast());
|
||||
dhcp_opts.append_option<Dhcp_packet::Options_end>();
|
||||
|
||||
/* 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();
|
||||
}
|
153
repos/os/src/server/nic_perf/interface.h
Normal file
153
repos/os/src/server/nic_perf/interface.h
Normal file
@ -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 <packet_generator.h>
|
||||
#include <packet_stats.h>
|
||||
#include <dhcp_client.h>
|
||||
|
||||
/* Genode includes */
|
||||
#include <base/registry.h>
|
||||
#include <os/packet_stream.h>
|
||||
#include <net/dhcp.h>
|
||||
#include <nic_session/nic_session.h>
|
||||
#include <timer_session/connection.h>
|
||||
|
||||
namespace Nic_perf {
|
||||
using namespace Genode;
|
||||
using namespace Net;
|
||||
|
||||
using Dhcp_options = Dhcp_packet::Options_aggregator<Size_guard>;
|
||||
|
||||
class Interface;
|
||||
|
||||
using Interface_registry = Registry<Interface>;
|
||||
}
|
||||
|
||||
class Nic_perf::Interface
|
||||
{
|
||||
protected:
|
||||
|
||||
using Sink = Nic::Packet_stream_sink<Nic::Session::Policy>;
|
||||
using Source = Nic::Packet_stream_source<Nic::Session::Policy>;
|
||||
|
||||
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> _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 <typename FUNC>
|
||||
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_ */
|
123
repos/os/src/server/nic_perf/main.cc
Normal file
123
repos/os/src/server/nic_perf/main.cc
Normal file
@ -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 <interface.h>
|
||||
#include <uplink_component.h>
|
||||
#include <nic_component.h>
|
||||
#include <nic_client.h>
|
||||
|
||||
/* Genode includes */
|
||||
#include <base/component.h>
|
||||
#include <base/heap.h>
|
||||
#include <base/attached_rom_dataspace.h>
|
||||
#include <util/arg_string.h>
|
||||
#include <timer_session/connection.h>
|
||||
#include <util/reconstructible.h>
|
||||
|
||||
namespace Nic_perf {
|
||||
class Main;
|
||||
|
||||
using namespace Genode;
|
||||
}
|
||||
|
||||
|
||||
struct Nic_perf::Main
|
||||
{
|
||||
using Periodic_timeout = Timer::Periodic_timeout<Main>;
|
||||
|
||||
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> _nic_client { };
|
||||
|
||||
Genode::Signal_handler<Main> _config_handler =
|
||||
{ _env.ep(), *this, &Main::_handle_config };
|
||||
|
||||
Constructible<Periodic_timeout> _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("<config/>"));
|
||||
}
|
||||
);
|
||||
});
|
||||
|
||||
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); }
|
||||
|
71
repos/os/src/server/nic_perf/nic_client.h
Normal file
71
repos/os/src/server/nic_perf/nic_client.h
Normal file
@ -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 <interface.h>
|
||||
|
||||
/* Genode includes */
|
||||
#include <nic_session/connection.h>
|
||||
#include <nic/packet_allocator.h>
|
||||
#include <base/attached_rom_dataspace.h>
|
||||
|
||||
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<Nic_client> _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_ */
|
142
repos/os/src/server/nic_perf/nic_component.h
Normal file
142
repos/os/src/server/nic_perf/nic_component.h
Normal file
@ -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 <interface.h>
|
||||
|
||||
/* Genode includes */
|
||||
#include <root/component.h>
|
||||
#include <nic/component.h>
|
||||
#include <base/attached_rom_dataspace.h>
|
||||
#include <os/session_policy.h>
|
||||
|
||||
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<Nic_session_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<Nic_session_component>(&env.ep().rpc_ep(), &md_alloc),
|
||||
_env(env),
|
||||
_config(config),
|
||||
_registry(registry),
|
||||
_timer(timer)
|
||||
{ }
|
||||
};
|
||||
|
||||
#endif /* _NIC_ROOT_H_ */
|
134
repos/os/src/server/nic_perf/packet_generator.cc
Normal file
134
repos/os/src/server/nic_perf/packet_generator.cc
Normal file
@ -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 <packet_generator.h>
|
||||
#include <interface.h>
|
||||
|
||||
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<Arp_packet>(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<Ipv4_packet>(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<Udp_packet>(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;
|
||||
}
|
||||
}
|
116
repos/os/src/server/nic_perf/packet_generator.h
Normal file
116
repos/os/src/server/nic_perf/packet_generator.h
Normal file
@ -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 <util/xml_node.h>
|
||||
#include <net/mac_address.h>
|
||||
#include <net/ipv4.h>
|
||||
#include <net/arp.h>
|
||||
#include <net/udp.h>
|
||||
#include <timer_session/connection.h>
|
||||
|
||||
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<Packet_generator> _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_ */
|
90
repos/os/src/server/nic_perf/packet_stats.h
Normal file
90
repos/os/src/server/nic_perf/packet_stats.h
Normal file
@ -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 <base/session_label.h>
|
||||
|
||||
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_ */
|
7
repos/os/src/server/nic_perf/target.mk
Normal file
7
repos/os/src/server/nic_perf/target.mk
Normal file
@ -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 =
|
195
repos/os/src/server/nic_perf/uplink_component.h
Normal file
195
repos/os/src/server/nic_perf/uplink_component.h
Normal file
@ -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 <interface.h>
|
||||
|
||||
/* Genode includes */
|
||||
#include <root/component.h>
|
||||
#include <uplink_session/rpc_object.h>
|
||||
#include <nic/packet_allocator.h>
|
||||
#include <base/attached_rom_dataspace.h>
|
||||
#include <os/session_policy.h>
|
||||
|
||||
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<Uplink_session_component> _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<Uplink_session_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<Uplink_session_component>(&env.ep().rpc_ep(), &md_alloc),
|
||||
_env(env),
|
||||
_config(config),
|
||||
_registry(registry),
|
||||
_timer(timer)
|
||||
{ }
|
||||
};
|
||||
|
||||
#endif /* _UPLINK_ROOT_H_ */
|
Loading…
Reference in New Issue
Block a user