diff --git a/repos/os/run/nic_router_flood.run b/repos/os/run/nic_router_flood.run
new file mode 100644
index 0000000000..1bd87f4281
--- /dev/null
+++ b/repos/os/run/nic_router_flood.run
@@ -0,0 +1,197 @@
+#
+# Build
+#
+
+if {![have_include power_on/qemu] ||
+ [have_spec foc] || [have_spec odroid_xu] ||
+ [expr [have_spec imx53] && [have_spec trustzone]]} {
+
+ puts "Run script is not supported on this platform."
+ exit 0
+}
+
+
+set build_components {
+ core
+ init
+ drivers/timer
+ drivers/nic
+ server/nic_router
+ test/net_flood
+ app/ping
+}
+
+proc gpio_drv { } { if {[have_spec rpi] && [have_spec hw]} { return hw_gpio_drv }
+ if {[have_spec rpi] && [have_spec foc]} { return foc_gpio_drv }
+ return gpio_drv }
+
+proc good_dst_ip { } { return "10.0.2.2" }
+proc bad_dst_ip { } { return "10.0.0.123" }
+
+lappend_if [have_spec gpio] build_components drivers/gpio
+
+source ${genode_dir}/repos/base/run/platform_drv.inc
+append_platform_drv_build_components
+
+build $build_components
+
+create_boot_directory
+
+#
+# Generate config
+#
+
+append config {
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ }
+
+append_platform_drv_config
+
+append_if [have_spec gpio] config "
+
+
+
+
+ "
+
+append config {
+
+
+
+
+
+
+
+
+
+ } [nic_drv_config] {
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+}
+
+install_config $config
+
+#
+# Boot modules
+#
+
+# generic modules
+append boot_modules {
+ core init
+ timer
+ } [nic_drv_binary] {
+ test-net_flood
+ ld.lib.so
+ nic_router
+ ping
+}
+
+# platform-specific modules
+lappend_if [have_spec linux] boot_modules fb_sdl
+lappend_if [have_spec gpio] boot_modules [gpio_drv]
+
+append_platform_drv_boot_modules
+
+build_boot_image $boot_modules
+
+append_if [have_spec x86] qemu_args " -net nic,model=e1000 "
+append_if [have_spec lan9118] qemu_args " -net nic,model=lan9118 "
+
+append qemu_args " -net user -nographic "
+
+run_genode_until ".*\"ping\" exited with exit value 0.*\n" 60
diff --git a/repos/os/src/test/net_flood/config.xsd b/repos/os/src/test/net_flood/config.xsd
new file mode 100644
index 0000000000..e558a90718
--- /dev/null
+++ b/repos/os/src/test/net_flood/config.xsd
@@ -0,0 +1,45 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/repos/os/src/test/net_flood/dhcp_client.cc b/repos/os/src/test/net_flood/dhcp_client.cc
new file mode 100644
index 0000000000..ceda41604e
--- /dev/null
+++ b/repos/os/src/test/net_flood/dhcp_client.cc
@@ -0,0 +1,267 @@
+/*
+ * \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
+#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(Genode::Allocator &alloc,
+ Timer::Connection &timer,
+ Nic &nic,
+ Dhcp_client_handler &handler)
+:
+ _alloc (alloc),
+ _timeout (timer, *this, &Dhcp_client::_handle_timeout),
+ _nic (nic),
+ _handler (handler)
+{
+ _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 = _handler.ip_config().interface.address;
+ _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 };
+ unsigned long 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 * 1000UL * 1000UL);
+}
+
+
+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_eth(Ethernet_frame ð, Size_guard &size_guard)
+{
+ if (eth.dst() != _nic.mac() &&
+ eth.dst() != Mac_address(0xff))
+ {
+ throw Drop_packet_inform("DHCP client expects Ethernet targeting the router");
+ }
+ Ipv4_packet &ip = eth.data(size_guard);
+ if (ip.protocol() != Ipv4_packet::Protocol::UDP) {
+ throw Drop_packet_inform("DHCP client expects UDP packet"); }
+
+ Udp_packet &udp = ip.data(size_guard);
+ if (!Dhcp_packet::is_dhcp(&udp)) {
+ throw Drop_packet_inform("DHCP client expects DHCP packet"); }
+
+ Dhcp_packet &dhcp = udp.data(size_guard);
+ if (dhcp.op() != Dhcp_packet::REPLY) {
+ throw Drop_packet_inform("DHCP client expects DHCP reply"); }
+
+ if (dhcp.client_mac() != _nic.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) { }
+
+ Ipv4_config ip_config(
+ Ipv4_address_prefix(
+ dhcp.yiaddr(),
+ dhcp.option().value()),
+ dhcp.option().value(),
+ dns_server);
+
+ _handler.ip_config(ip_config);
+ 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)
+{
+ _nic.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(_nic.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(_nic.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(_nic.mac());
+ dhcp_opts.append_option(PKT_SIZE - dhcp_off);
+ break;
+
+ case Message_type::REQUEST:
+ append_param_req_list(dhcp_opts);
+ dhcp_opts.append_option(_nic.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/test/net_flood/dhcp_client.h b/repos/os/src/test/net_flood/dhcp_client.h
new file mode 100644
index 0000000000..0f52d6ba9d
--- /dev/null
+++ b/repos/os/src/test/net_flood/dhcp_client.h
@@ -0,0 +1,105 @@
+/*
+ * \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 Net {
+
+ /* external definition */
+ class Nic_peer;
+ class Ipv4_config;
+ class Ethernet_frame;
+
+ /* local definition */
+ class Dhcp_client;
+ class Dhcp_client_handler;
+ 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_handler
+{
+ public:
+
+ virtual void ip_config(Ipv4_config const &ip_config) = 0;
+
+ virtual Ipv4_config const &ip_config() const = 0;
+
+ virtual ~Dhcp_client_handler() { }
+};
+
+
+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 };
+
+ Genode::Allocator &_alloc;
+ State _state { State::INIT };
+ Timer::One_shot_timeout _timeout;
+ unsigned long _lease_time_sec = 0;
+ Genode::Microseconds const _discover_timeout { DISCOVER_TIMEOUT_SEC * 1000 * 1000 };
+ Genode::Microseconds const _request_timeout { REQUEST_TIMEOUT_SEC * 1000 * 1000 };
+ Nic &_nic;
+ Dhcp_client_handler &_handler;
+
+ 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(Genode::Allocator &alloc,
+ Timer::Connection &timer,
+ Nic &nic,
+ Dhcp_client_handler &handler);
+
+ void handle_eth(Ethernet_frame ð,
+ Size_guard &size_guard);
+
+};
+
+#endif /* _DHCP_CLIENT_H_ */
diff --git a/repos/os/src/test/net_flood/ipv4_address_prefix.cc b/repos/os/src/test/net_flood/ipv4_address_prefix.cc
new file mode 100644
index 0000000000..bf46d115e3
--- /dev/null
+++ b/repos/os/src/test/net_flood/ipv4_address_prefix.cc
@@ -0,0 +1,103 @@
+/*
+ * \brief Ipv4 address combined with a subnet prefix length
+ * \author Martin Stein
+ * \date 2017-10-12
+ */
+
+/*
+ * Copyright (C) 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
+
+using namespace Genode;
+using namespace Net;
+
+
+Ipv4_address Ipv4_address_prefix::subnet_mask() const
+{
+ Ipv4_address result;
+ if (prefix >= 8) {
+
+ result.addr[0] = 0xff;
+
+ if (prefix >= 16) {
+
+ result.addr[1] = 0xff;
+
+ if (prefix >= 24) {
+
+ result.addr[2] = 0xff;
+ result.addr[3] = 0xff << (32 - prefix);
+ } else {
+ result.addr[2] = 0xff << (24 - prefix);
+ }
+ } else {
+ result.addr[1] = 0xff << (16 - prefix);
+ }
+ } else {
+ result.addr[0] = 0xff << (8 - prefix);
+ }
+ return result;
+}
+
+
+void Ipv4_address_prefix::print(Genode::Output &output) const
+{
+ Genode::print(output, address, "/", prefix);
+}
+
+
+bool Ipv4_address_prefix::prefix_matches(Ipv4_address const &ip) const
+{
+ uint8_t prefix_left = prefix;
+ uint8_t byte = 0;
+ for (; prefix_left >= 8; prefix_left -= 8, byte++) {
+ if (ip.addr[byte] != address.addr[byte]) {
+ return false; }
+ }
+ if (prefix_left == 0) {
+ return true; }
+
+ uint8_t const mask = ~(0xff >> prefix_left);
+ return !((ip.addr[byte] ^ address.addr[byte]) & mask);
+}
+
+
+Ipv4_address Ipv4_address_prefix::broadcast_address() const
+{
+ Ipv4_address result = address;
+ Ipv4_address const mask = subnet_mask();
+ for (unsigned i = 0; i < 4; i++) {
+ result.addr[i] |= ~mask.addr[i];
+ }
+ return result;
+}
+
+
+Ipv4_address_prefix::Ipv4_address_prefix(Ipv4_address address,
+ Ipv4_address subnet_mask)
+:
+ address(address), prefix(0)
+{
+ Genode::uint8_t rest;
+ if (subnet_mask.addr[0] != 0xff) {
+ rest = subnet_mask.addr[0];
+ prefix = 0;
+ } else if (subnet_mask.addr[1] != 0xff) {
+ rest = subnet_mask.addr[1];
+ prefix = 8;
+ } else if (subnet_mask.addr[2] != 0xff) {
+ rest = subnet_mask.addr[2];
+ prefix = 16;
+ } else {
+ rest = subnet_mask.addr[3];
+ prefix = 24;
+ }
+ for (Genode::uint8_t mask = 1 << 7; rest & mask; mask >>= 1)
+ prefix++;
+}
diff --git a/repos/os/src/test/net_flood/ipv4_address_prefix.h b/repos/os/src/test/net_flood/ipv4_address_prefix.h
new file mode 100644
index 0000000000..4fb378332c
--- /dev/null
+++ b/repos/os/src/test/net_flood/ipv4_address_prefix.h
@@ -0,0 +1,82 @@
+/*
+ * \brief Ipv4 address combined with a subnet prefix length
+ * \author Martin Stein
+ * \date 2017-10-12
+ */
+
+/*
+ * Copyright (C) 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 _IPV4_ADDRESS_PREFIX_H_
+#define _IPV4_ADDRESS_PREFIX_H_
+
+/* Genode includes */
+#include
+
+namespace Net { class Ipv4_address_prefix; }
+
+
+struct Net::Ipv4_address_prefix
+{
+ Ipv4_address address { };
+ Genode::uint8_t prefix;
+
+ Ipv4_address_prefix(Ipv4_address address,
+ Ipv4_address subnet_mask);
+
+ Ipv4_address_prefix() : prefix(32) { }
+
+ bool valid() const { return address.valid() || prefix == 0; }
+
+ void print(Genode::Output &output) const;
+
+ bool prefix_matches(Ipv4_address const &ip) const;
+
+ Ipv4_address subnet_mask() const;
+
+ Ipv4_address broadcast_address() const;
+
+ bool operator != (Ipv4_address_prefix const &other) const
+ {
+ return prefix != other.prefix ||
+ address != other.address;
+ }
+};
+
+
+namespace Genode {
+
+ inline size_t ascii_to(char const *s, Net::Ipv4_address_prefix &result);
+}
+
+
+Genode::size_t Genode::ascii_to(char const *s, Net::Ipv4_address_prefix &result)
+{
+ /* read the leading IPv4 address, fail if there's no address */
+ Net::Ipv4_address_prefix buf;
+ size_t read_len = ascii_to(s, buf.address);
+ if (!read_len) {
+ return 0; }
+
+ /* check for the following slash */
+ s += read_len;
+ if (*s != '/') {
+ return 0; }
+ read_len++;
+ s++;
+
+ /* read the prefix, fail if there's no prefix */
+ size_t prefix_len = ascii_to_unsigned(s, buf.prefix, 10);
+ if (!prefix_len) {
+ return 0; }
+
+ /* fill result and return read length */
+ result = buf;
+ return read_len + prefix_len;
+}
+
+#endif /* _IPV4_ADDRESS_PREFIX_H_ */
diff --git a/repos/os/src/test/net_flood/ipv4_config.cc b/repos/os/src/test/net_flood/ipv4_config.cc
new file mode 100644
index 0000000000..5aea166f58
--- /dev/null
+++ b/repos/os/src/test/net_flood/ipv4_config.cc
@@ -0,0 +1,43 @@
+/*
+ * \brief IPv4 peer configuration
+ * \author Martin Stein
+ * \date 2016-08-19
+ */
+
+/*
+ * 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.
+ */
+
+/* Genode includes */
+#include
+
+/* local includes */
+#include
+
+using namespace Genode;
+using namespace Net;
+
+Ipv4_config::Ipv4_config(Ipv4_address_prefix interface,
+ Ipv4_address gateway,
+ Ipv4_address dns_server)
+:
+ interface(interface), gateway(gateway), dns_server(dns_server)
+{
+ if (!valid && (interface_valid || gateway_valid)) {
+ error("Bad IP configuration");
+ }
+}
+
+
+void Ipv4_config::print(Genode::Output &output) const
+{
+ Genode::print(output, "interface ", interface);
+ if (gateway.valid()) {
+ Genode::print(output, ", gateway ", gateway); }
+
+ if (dns_server.valid()) {
+ Genode::print(output, ", DNS server ", dns_server); }
+}
diff --git a/repos/os/src/test/net_flood/ipv4_config.h b/repos/os/src/test/net_flood/ipv4_config.h
new file mode 100644
index 0000000000..6b3f011074
--- /dev/null
+++ b/repos/os/src/test/net_flood/ipv4_config.h
@@ -0,0 +1,49 @@
+/*
+ * \brief IPv4 peer configuration
+ * \author Martin Stein
+ * \date 2016-08-19
+ */
+
+/*
+ * 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 _IPV4_CONFIG_H_
+#define _IPV4_CONFIG_H_
+
+/* local includes */
+#include
+
+namespace Net { class Ipv4_config; }
+
+struct Net::Ipv4_config
+{
+ Ipv4_address_prefix const interface { };
+ bool const interface_valid { interface.valid() };
+ Ipv4_address const gateway { };
+ bool const gateway_valid { gateway.valid() };
+ Ipv4_address const dns_server { };
+ bool const valid { interface_valid &&
+ (!gateway_valid ||
+ interface.prefix_matches(gateway)) };
+
+ Ipv4_config(Ipv4_address_prefix interface,
+ Ipv4_address gateway,
+ Ipv4_address dns_server);
+
+ Ipv4_config() { }
+
+ bool operator != (Ipv4_config const &other) const
+ {
+ return interface != other.interface ||
+ gateway != other.gateway ||
+ dns_server != other.dns_server;
+ }
+
+ void print(Genode::Output &output) const;
+};
+
+#endif /* _IPV4_CONFIG_H_ */
diff --git a/repos/os/src/test/net_flood/main.cc b/repos/os/src/test/net_flood/main.cc
new file mode 100644
index 0000000000..7ea142cb36
--- /dev/null
+++ b/repos/os/src/test/net_flood/main.cc
@@ -0,0 +1,355 @@
+/*
+ * \brief Test the reachability of a host on an IP network
+ * \author Martin Stein
+ * \date 2018-03-27
+ */
+
+/*
+ * Copyright (C) 2018 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
+#include
+#include
+#include
+
+using namespace Net;
+using namespace Genode;
+
+
+class Main : public Nic_handler,
+ public Dhcp_client_handler
+{
+ private:
+
+ using Periodic_timeout = Timer::Periodic_timeout;
+
+ enum { IPV4_TIME_TO_LIVE = 64 };
+ enum { ICMP_DATA_SIZE = 56 };
+ enum { ICMP_SEQ = 1 };
+ enum { SRC_PORT = 50000 };
+ enum { FIRST_DST_PORT = 49152 };
+ enum { LAST_DST_PORT = 65535 };
+
+ Env &_env;
+ Attached_rom_dataspace _config_rom { _env, "config" };
+ Xml_node _config { _config_rom.xml() };
+ Timer::Connection _timer { _env };
+ Microseconds _period_us { 100 };
+ Constructible _period { };
+ Heap _heap { &_env.ram(), &_env.rm() };
+ bool const _verbose { _config.attribute_value("verbose", false) };
+ Net::Nic _nic { _env, _heap, *this, _verbose };
+ Ipv4_address const _dst_ip { _config.attribute_value("dst_ip", Ipv4_address()) };
+ Mac_address _dst_mac { };
+ Constructible _dhcp_client { };
+ Reconstructible _ip_config { _config.attribute_value("interface", Ipv4_address_prefix()),
+ _config.attribute_value("gateway", Ipv4_address()),
+ Ipv4_address() };
+ Protocol const _protocol { _config.attribute_value("protocol", Protocol::ICMP) };
+ Port _dst_port { FIRST_DST_PORT };
+
+ void _handle_arp(Ethernet_frame ð,
+ Size_guard &size_guard);
+
+ void _broadcast_arp_request(Ipv4_address const &ip);
+
+ void _send_arp_reply(Ethernet_frame &req_eth,
+ Arp_packet &req_arp);
+
+ void _send_ping(Duration not_used = Duration(Microseconds(0)));
+
+ public:
+
+ struct Invalid_arguments : Exception { };
+
+ Main(Env &env);
+
+
+ /*****************
+ ** Nic_handler **
+ *****************/
+
+ void handle_eth(Ethernet_frame ð,
+ Size_guard &size_guard) override;
+
+
+ /*************************
+ ** Dhcp_client_handler **
+ *************************/
+
+ void ip_config(Ipv4_config const &ip_config) override;
+
+ Ipv4_config const &ip_config() const override { return *_ip_config; }
+};
+
+
+void Main::ip_config(Ipv4_config const &ip_config)
+{
+ if (_verbose) {
+ log("IP config: ", ip_config); }
+
+ _ip_config.construct(ip_config);
+ _period.construct(_timer, *this, &Main::_send_ping, _period_us);
+}
+
+
+Main::Main(Env &env) : _env(env)
+{
+ /* exit unsuccessful if parameters are invalid */
+ if (_dst_ip == Ipv4_address()) {
+ throw Invalid_arguments(); }
+
+ /* if there is a static IP config, start sending pings periodically */
+ if (ip_config().valid) {
+ _period.construct(_timer, *this, &Main::_send_ping, _period_us); }
+
+ /* else, start the DHCP client for requesting an IP config */
+ else {
+ _dhcp_client.construct(_heap, _timer, _nic, *this); }
+}
+
+
+void Main::handle_eth(Ethernet_frame ð,
+ Size_guard &size_guard)
+{
+ try {
+ /* print receipt message */
+ if (_verbose) {
+ log("rcv ", eth); }
+
+ if (!ip_config().valid) {
+ _dhcp_client->handle_eth(eth, size_guard); }
+
+ /* drop packet if ETH does not target us */
+ if (eth.dst() != _nic.mac() &&
+ eth.dst() != Ethernet_frame::broadcast())
+ {
+ if (_verbose) {
+ log("bad ETH destination"); }
+ return;
+ }
+ /* select ETH sub-protocol */
+ switch (eth.type()) {
+ case Ethernet_frame::Type::ARP: _handle_arp(eth, size_guard); break;
+ default: ; }
+ }
+ catch (Drop_packet_inform exception) {
+ if (_verbose) {
+ log("drop packet: ", exception.msg); }
+ }
+}
+
+
+void Main::_handle_arp(Ethernet_frame ð,
+ Size_guard &size_guard)
+{
+ /* check ARP protocol- and hardware address type */
+ Arp_packet &arp = eth.data(size_guard);
+ if (!arp.ethernet_ipv4()) {
+ error("ARP for unknown protocol"); }
+
+ /* check ARP operation */
+ switch (arp.opcode()) {
+ case Arp_packet::REPLY:
+
+ /* check whether we waited for this ARP reply */
+ if (_dst_mac != Mac_address()) {
+ return; }
+
+ if (ip_config().interface.prefix_matches(_dst_ip)) {
+ if (arp.src_ip() != _dst_ip) {
+ return; }
+ } else {
+ if (arp.src_ip() != ip_config().gateway) {
+ return; }
+ }
+ /* set destination MAC address and retry to ping */
+ _dst_mac = arp.src_mac();
+ return;
+
+ case Arp_packet::REQUEST:
+
+ /* check whether the ARP request targets us */
+ if (arp.dst_ip() != ip_config().interface.address) {
+ return; }
+
+ _send_arp_reply(eth, arp);
+
+ default: ; }
+}
+
+
+void Main::_send_arp_reply(Ethernet_frame &req_eth,
+ Arp_packet &req_arp)
+{
+ _nic.send(sizeof(Ethernet_frame) + sizeof(Arp_packet),
+ [&] (void *pkt_base, Size_guard &size_guard)
+ {
+ /* write Ethernet header */
+ Ethernet_frame ð = Ethernet_frame::construct_at(pkt_base, size_guard);
+ eth.dst(req_eth.src());
+ eth.src(_nic.mac());
+ eth.type(Ethernet_frame::Type::ARP);
+
+ /* write ARP header */
+ 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::REPLY);
+ arp.src_mac(_nic.mac());
+ arp.src_ip(ip_config().interface.address);
+ arp.dst_mac(req_eth.src());
+ arp.dst_ip(req_arp.src_ip());
+ });
+}
+
+
+void Main::_broadcast_arp_request(Ipv4_address const &dst_ip)
+{
+ _nic.send(sizeof(Ethernet_frame) + sizeof(Arp_packet),
+ [&] (void *pkt_base, Size_guard &size_guard)
+ {
+ /* write Ethernet header */
+ Ethernet_frame ð = Ethernet_frame::construct_at(pkt_base, size_guard);
+ eth.dst(Mac_address(0xff));
+ eth.src(_nic.mac());
+ eth.type(Ethernet_frame::Type::ARP);
+
+ /* write ARP header */
+ 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(_nic.mac());
+ arp.src_ip(ip_config().interface.address);
+ arp.dst_mac(Mac_address(0xff));
+ arp.dst_ip(dst_ip);
+ });
+}
+
+
+void Main::_send_ping(Duration)
+{
+ /* if we do not yet know the Ethernet destination, request it via ARP */
+ if (_dst_mac == Mac_address()) {
+ if (ip_config().interface.prefix_matches(_dst_ip)) {
+ _broadcast_arp_request(_dst_ip); }
+ else {
+ _broadcast_arp_request(ip_config().gateway); }
+ return;
+ }
+ _nic.send(sizeof(Ethernet_frame) + sizeof(Ipv4_packet) +
+ sizeof(Icmp_packet) + ICMP_DATA_SIZE,
+ [&] (void *pkt_base, Size_guard &size_guard)
+ {
+ /* create ETH header */
+ Ethernet_frame ð = Ethernet_frame::construct_at(pkt_base, size_guard);
+ eth.dst(_dst_mac);
+ eth.src(_nic.mac());
+ eth.type(Ethernet_frame::Type::IPV4);
+
+ /* create IP header */
+ 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.src(ip_config().interface.address);
+ ip.dst(_dst_ip);
+
+ /* select IP-encapsulated protocol */
+ switch (_protocol) {
+ case Protocol::ICMP:
+ {
+ /* adapt IP header to ICMP */
+ ip.protocol(Ipv4_packet::Protocol::ICMP);
+
+ /* create ICMP header */
+ Icmp_packet &icmp = ip.construct_at_data(size_guard);
+ icmp.type(Icmp_packet::Type::ECHO_REQUEST);
+ icmp.code(Icmp_packet::Code::ECHO_REQUEST);
+ icmp.query_id(_dst_port.value);
+ icmp.query_seq(ICMP_SEQ);
+
+ /* finish ICMP header */
+ icmp.update_checksum(ICMP_DATA_SIZE);
+
+ /* prepare next ICMP ping */
+ if (_dst_port.value == LAST_DST_PORT) {
+ _dst_port.value = FIRST_DST_PORT; }
+ else {
+ _dst_port.value++; }
+ break;
+ }
+ case Protocol::UDP:
+ {
+ /* adapt IP header to UDP */
+ ip.protocol(Ipv4_packet::Protocol::UDP);
+
+ /* create UDP header */
+ size_t const udp_off = size_guard.head_size();
+ Udp_packet &udp = ip.construct_at_data(size_guard);
+ udp.src_port(Port(SRC_PORT));
+ udp.dst_port(_dst_port);
+
+ /* finish UDP header */
+ udp.length(size_guard.head_size() - udp_off);
+ udp.update_checksum(ip.src(), ip.dst());
+
+ /* prepare next ping */
+ if (_dst_port.value == LAST_DST_PORT) {
+ _dst_port.value = FIRST_DST_PORT; }
+ else {
+ _dst_port.value++; }
+ break;
+ }
+ case Protocol::TCP:
+ {
+ /* adapt IP header to TCP */
+ ip.protocol(Ipv4_packet::Protocol::TCP);
+
+ /* create TCP header */
+ size_t const tcp_off = size_guard.head_size();
+ Tcp_packet &tcp = ip.construct_at_data(size_guard);
+ tcp.src_port(Port(SRC_PORT));
+ tcp.dst_port(_dst_port);
+
+ /* finish TCP header */
+ tcp.update_checksum(ip.src(), ip.dst(), size_guard.head_size() - tcp_off);
+
+ /* prepare next ping */
+ if (_dst_port.value == LAST_DST_PORT) {
+ _dst_port.value = FIRST_DST_PORT; }
+ else {
+ _dst_port.value++; }
+ break;
+ }
+ }
+ /* finish IP header */
+ ip.total_length(size_guard.head_size() - ip_off);
+ ip.update_checksum();
+ });
+}
+
+
+void Component::construct(Env &env) { static Main main(env); }
diff --git a/repos/os/src/test/net_flood/nic.cc b/repos/os/src/test/net_flood/nic.cc
new file mode 100644
index 0000000000..dd835d63e3
--- /dev/null
+++ b/repos/os/src/test/net_flood/nic.cc
@@ -0,0 +1,46 @@
+/*
+ * \brief NIC connection wrapper for a more convenient interface
+ * \author Martin Stein
+ * \date 2018-04-16
+ */
+
+/*
+ * Copyright (C) 2018 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
+
+using namespace Net;
+using namespace Genode;
+
+
+void Net::Nic::_ready_to_ack()
+{
+ while (_source().ack_avail()) {
+ _source().release_packet(_source().get_acked_packet()); }
+}
+
+
+void Net::Nic::_ready_to_submit()
+{
+ while (_sink().packet_avail()) {
+
+ Packet_descriptor const pkt = _sink().get_packet();
+ if (!pkt.size()) {
+ continue; }
+
+ Size_guard size_guard(pkt.size());
+ _handler.handle_eth(Ethernet_frame::cast_from(_sink().packet_content(pkt), size_guard),
+ size_guard);
+
+ if (!_sink().ready_to_ack()) {
+ error("ack state FULL");
+ return;
+ }
+ _sink().acknowledge_packet(pkt);
+ }
+}
diff --git a/repos/os/src/test/net_flood/nic.h b/repos/os/src/test/net_flood/nic.h
new file mode 100644
index 0000000000..2f63a8c092
--- /dev/null
+++ b/repos/os/src/test/net_flood/nic.h
@@ -0,0 +1,129 @@
+/*
+ * \brief NIC connection wrapper for a more convenient interface
+ * \author Martin Stein
+ * \date 2018-04-16
+ */
+
+/*
+ * Copyright (C) 2018 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_H_
+#define _NIC_H_
+
+/* Genode includes */
+#include
+#include
+#include
+
+namespace Genode {
+
+ class Env;
+}
+
+namespace Net {
+
+ struct Nic_handler;
+ class Nic;
+
+ using Packet_descriptor = ::Nic::Packet_descriptor;
+ using Packet_stream_sink = ::Nic::Packet_stream_sink< ::Nic::Session::Policy>;
+ using Packet_stream_source = ::Nic::Packet_stream_source< ::Nic::Session::Policy>;
+}
+
+
+struct Net::Nic_handler
+{
+ virtual void handle_eth(Ethernet_frame ð,
+ Size_guard &size_guard) = 0;
+
+ virtual ~Nic_handler() { }
+};
+
+
+class Net::Nic
+{
+ private:
+
+ using Signal_handler = Genode::Signal_handler;
+
+ enum { PKT_SIZE = ::Nic::Packet_allocator::DEFAULT_PACKET_SIZE };
+ enum { BUF_SIZE = 90 * PKT_SIZE };
+
+ Genode::Env &_env;
+ Genode::Allocator &_alloc;
+ Nic_handler &_handler;
+ bool const &_verbose;
+ ::Nic::Packet_allocator _pkt_alloc { &_alloc };
+ ::Nic::Connection _nic { _env, &_pkt_alloc, BUF_SIZE, BUF_SIZE };
+ Signal_handler _sink_ack { _env.ep(), *this, &Nic::_ack_avail };
+ Signal_handler _sink_submit { _env.ep(), *this, &Nic::_ready_to_submit };
+ Signal_handler _source_ack { _env.ep(), *this, &Nic::_ready_to_ack };
+ Signal_handler _source_submit { _env.ep(), *this, &Nic::_packet_avail };
+ Mac_address const _mac { _nic.mac_address() };
+
+ Net::Packet_stream_sink &_sink() { return *_nic.rx(); }
+ Net::Packet_stream_source &_source() { return *_nic.tx(); }
+
+
+ /***********************************
+ ** Packet-stream signal handlers **
+ ***********************************/
+
+ void _ready_to_submit();
+ void _ack_avail() { }
+ void _ready_to_ack();
+ void _packet_avail() { }
+
+ public:
+
+ Nic(Genode::Env &env,
+ Genode::Allocator &alloc,
+ Nic_handler &handler,
+ bool const &verbose)
+ :
+ _env (env),
+ _alloc (alloc),
+ _handler (handler),
+ _verbose (verbose)
+ {
+ /* install packet stream signals */
+ _nic.rx_channel()->sigh_ready_to_ack(_sink_ack);
+ _nic.rx_channel()->sigh_packet_avail(_sink_submit);
+ _nic.tx_channel()->sigh_ack_avail(_source_ack);
+ _nic.tx_channel()->sigh_ready_to_submit(_source_submit);
+ }
+
+ template
+ void send(Genode::size_t pkt_size,
+ FUNC && write_to_pkt)
+ {
+ 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().submit_packet(pkt);
+ if (_verbose) {
+ Size_guard size_guard(pkt_size);
+ try { Genode::log("snd ", Ethernet_frame::cast_from(pkt_base, size_guard)); }
+ catch (Size_guard::Exceeded) { Genode::log("snd ?"); }
+ }
+ }
+ catch (Net::Packet_stream_source::Packet_alloc_failed) {
+ Genode::warning("failed to allocate packet"); }
+ }
+
+
+ /***************
+ ** Accessors **
+ ***************/
+
+ Mac_address const &mac() const { return _mac; }
+};
+
+
+#endif /* _NIC_H_ */
diff --git a/repos/os/src/test/net_flood/protocol.h b/repos/os/src/test/net_flood/protocol.h
new file mode 100644
index 0000000000..713d7f040c
--- /dev/null
+++ b/repos/os/src/test/net_flood/protocol.h
@@ -0,0 +1,33 @@
+/*
+ * \brief Supported protocols
+ * \author Martin Stein
+ * \date 2018-03-27
+ */
+
+/*
+ * Copyright (C) 2018 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 __PROTOCOL_H_
+#define __PROTOCOL_H_
+
+/* Genode includes */
+#include
+
+namespace Genode { enum class Protocol : uint16_t { ICMP, UDP, TCP }; }
+
+namespace Genode
+{
+ inline size_t ascii_to(char const *s, Protocol &result)
+ {
+ if (!strcmp(s, "icmp", 4)) { result = Protocol::ICMP; return 4; }
+ if (!strcmp(s, "udp", 3)) { result = Protocol::UDP; return 3; }
+ if (!strcmp(s, "tcp", 3)) { result = Protocol::TCP; return 3; }
+ return 0;
+ }
+}
+
+#endif /* __PROTOCOL_H_ */
diff --git a/repos/os/src/test/net_flood/target.mk b/repos/os/src/test/net_flood/target.mk
new file mode 100644
index 0000000000..9df2191a96
--- /dev/null
+++ b/repos/os/src/test/net_flood/target.mk
@@ -0,0 +1,10 @@
+TARGET = test-net_flood
+
+LIBS += base net
+
+SRC_CC += main.cc dhcp_client.cc ipv4_address_prefix.cc
+SRC_CC += nic.cc ipv4_config.cc
+
+INC_DIR += $(PRG_DIR)
+
+CONFIG_XSD = config.xsd
diff --git a/tool/autopilot.list b/tool/autopilot.list
index 003929950d..43080f2a8f 100644
--- a/tool/autopilot.list
+++ b/tool/autopilot.list
@@ -53,6 +53,7 @@ nic_bridge
nic_dump
nic_loopback
nic_router
+nic_router_flood
nic_router_uplinks
noux
noux_net_netcat