diff --git a/repos/dde_linux/run/nic_router_uplinks.run b/repos/dde_linux/run/nic_router_uplinks.run
index ce5147f004..77fe902d95 100644
--- a/repos/dde_linux/run/nic_router_uplinks.run
+++ b/repos/dde_linux/run/nic_router_uplinks.run
@@ -7,18 +7,6 @@ proc wifi_ssid {} {
proc wifi_psk {} {
return "$::env(GENODE_WIFI_PSK2)" }
-proc router_verbose {} {
- return "no" }
-
-proc router_verbose_packet_drop {} {
- return "no" }
-
-proc router_verbose_packets {} {
- return "no" }
-
-proc router_verbose_domain_state {} {
- return "yes" }
-
create_boot_directory
import_from_depot [depot_user]/src/[base_src] \
[depot_user]/pkg/pc_wifi \
@@ -32,7 +20,7 @@ import_from_depot [depot_user]/src/[base_src] \
[depot_user]/src/report_rom \
[depot_user]/src/pc_rtc
-build { app/ping }
+build { app/ping server/nic_router test/nic_router_uplinks }
install_config {
@@ -52,14 +40,16 @@ install_config {
-
+
-
+
+
+
@@ -110,7 +100,7 @@ install_config {
-
+
@@ -140,7 +130,7 @@ install_config {
-
+
@@ -152,221 +142,6 @@ install_config {
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
@@ -375,9 +150,17 @@ install_config {
-
-
-
+
+
+
+
+
+
+
+
+
+
+
@@ -386,8 +169,10 @@ install_config {
+
@@ -396,42 +181,10 @@ install_config {
}
+append qemu_args " -nographic "
+
build_boot_image [build_artifacts]
-append done_string ".*router.config: change"
-append done_string ".*router.config: sleep 5001 milliseconds"
-append done_string ".*64 bytes from 1.1.1.1: icmp_seq="
-
-append done_string ".*router.config: change"
-append done_string ".*router.config: sleep 5002 milliseconds"
-append done_string ".*64 bytes from 1.1.1.1: icmp_seq="
-
-append done_string ".*router.config: change"
-append done_string ".*router.config: sleep 5003 milliseconds"
-append done_string ".*downlink.*invalid domain.*invalid ICMP rule"
-
-append done_string ".*router.config: change"
-append done_string ".*router.config: sleep 5004 milliseconds"
-append done_string ".*64 bytes from 1.1.1.1: icmp_seq="
-
-append done_string ".*router.config: change"
-append done_string ".*router.config: sleep 5005 milliseconds"
-append done_string ".*64 bytes from 1.1.1.1: icmp_seq="
-
-append done_string ".*router.config: change"
-append done_string ".*router.config: sleep 5006 milliseconds"
-append done_string ".*downlink.*invalid domain.*invalid ICMP rule"
-
-append done_string ".*router.config: change"
-append done_string ".*router.config: sleep 5007 milliseconds"
-append done_string ".*64 bytes from 1.1.1.1: icmp_seq="
-
-append done_string ".*router.config: change"
-append done_string ".*router.config: sleep 5008 milliseconds"
-append done_string ".*64 bytes from 1.1.1.1: icmp_seq="
-
-append done_string ".*router.config: change"
-append done_string ".*router.config: sleep 600000 milliseconds"
-append done_string ".*From 10.0.1.79 icmp_seq=.* Destination Unreachable"
-
-run_genode_until $done_string 90
+run_genode_until "child \"test\" exited with exit value.*\n" 300
+grep_output {\[init\] child "test" exited with exit value}
+compare_output_to {[init] child "test" exited with exit value 0}
diff --git a/repos/dde_linux/src/test/nic_router_uplinks/assertion.h b/repos/dde_linux/src/test/nic_router_uplinks/assertion.h
new file mode 100644
index 0000000000..b3e6477cc3
--- /dev/null
+++ b/repos/dde_linux/src/test/nic_router_uplinks/assertion.h
@@ -0,0 +1,35 @@
+/*
+ * \brief Assertion macros
+ * \author Martin Stein
+ * \date 2023-06-09
+ */
+
+/*
+ * Copyright (C) 2023 Genode Labs GmbH
+ *
+ * This file is part of the Genode OS framework, which is distributed
+ * under the terms of the GNU Affero General Public License version 3.
+ */
+
+#ifndef _TRESOR__ASSERTION_H_
+#define _TRESOR__ASSERTION_H_
+
+/* base includes */
+#include
+#include
+
+#define ASSERT(condition) \
+ do { \
+ if (!(condition)) { \
+ Genode::error(__FILE__, ":", __LINE__, ": ", " assertion \"", #condition, "\" failed "); \
+ Genode::sleep_forever(); \
+ } \
+ } while (false)
+
+#define ASSERT_NEVER_REACHED \
+ do { \
+ Genode::error(__FILE__, ":", __LINE__, ": ", " should have never been reached"); \
+ Genode::sleep_forever(); \
+ } while (false)
+
+#endif /* _TRESOR__ASSERTION_H_ */
diff --git a/repos/dde_linux/src/test/nic_router_uplinks/main.cc b/repos/dde_linux/src/test/nic_router_uplinks/main.cc
new file mode 100644
index 0000000000..bb8aeeca46
--- /dev/null
+++ b/repos/dde_linux/src/test/nic_router_uplinks/main.cc
@@ -0,0 +1,172 @@
+/*
+ * \brief Server component for Network Address Translation on NIC sessions
+ * \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.
+ */
+
+/* Genode */
+#include
+#include
+#include
+#include
+
+/* local */
+#include
+
+using namespace Genode;
+
+enum { STEP_TIMEOUT_US = 60*1000*1000 };
+
+using Driver = String<16>;
+
+struct Result
+{
+ enum { INVALID_ID = ~0U };
+ enum Type { REPLY, DESTINATION_UNREACHABLE, INVALID };
+ using Type_string = String<32>;
+
+ unsigned id;
+ Type type;
+
+ static Type string_to_type(Type_string const &string)
+ {
+ if (string == "reply") return REPLY;
+ if (string == "destination_unreachable") return DESTINATION_UNREACHABLE;
+ return INVALID;
+ }
+
+ Result(Xml_node const &node)
+ :
+ id(node.attribute_value("id", (unsigned)INVALID_ID)),
+ type(string_to_type(node.attribute_value("type", Type_string())))
+ {
+ ASSERT(id != INVALID_ID && type != INVALID);
+ }
+};
+
+struct Goal
+{
+ Result::Type result_type;
+ unsigned count;
+};
+
+struct Main
+{
+ Env &env;
+ Expanding_reporter router_config_reporter { env, "config", "router_config" };
+ Attached_rom_dataspace result_rom { env, "ping_result" };
+ Signal_handler result_handler { env.ep(), *this, &Main::handle_result };
+ unsigned last_result_id = Result::INVALID_ID;
+ Constructible goal { };
+ Timer::Connection timer { env };
+ Timer::One_shot_timeout timeout { timer, *this, &Main::handle_timeout };
+ unsigned step { 0UL };
+ Constructible driver { };
+
+ void handle_result()
+ {
+ if (!goal.constructed()) {
+ step_succeeded();
+ return;
+ }
+ result_rom.update();
+ Result result(result_rom.xml());
+ if (result.id != last_result_id) {
+ if (result.type == goal->result_type) {
+ ASSERT(goal->count);
+ if (!--goal->count) {
+ goal.destruct();
+ step_succeeded();
+ }
+ } else
+ warning("test step ", step, " observed unexpected result");
+ last_result_id = result.id;
+ }
+ }
+
+ void handle_timeout(Duration)
+ {
+ error("test step ", step, " timed out");
+ env.parent().exit(-1);
+ }
+
+ void update_router_config()
+ {
+ router_config_reporter.generate([&] (Xml_generator &xml) {
+ xml.attribute("dhcp_discover_timeout_sec", "1");
+ xml.node("policy", [&] {
+ xml.attribute("label_prefix", "ping");
+ xml.attribute("domain", "downlink"); });
+ xml.node("policy", [&] {
+ xml.attribute("label_prefix", "dhcp");
+ xml.attribute("domain", "uplink"); });
+
+ if (driver.constructed()) {
+ xml.node("policy", [&] {
+ xml.attribute("label_prefix", *driver);
+ xml.attribute("domain", "uplink"); });
+ xml.node("domain", [&] {
+ xml.attribute("name", "uplink");
+ xml.node("nat", [&] {
+ xml.attribute("domain", "downlink");
+ xml.attribute("icmp-ids", "999"); }); });
+ }
+ xml.node("domain", [&] {
+ xml.attribute("name", "downlink");
+ xml.attribute("interface", "10.0.1.79/24");
+ xml.node("dhcp-server", [&] {
+ xml.attribute("ip_first", "10.0.1.80");
+ xml.attribute("ip_last", "10.0.1.100"); });
+
+ if (driver.constructed())
+ xml.node("icmp", [&] {
+ xml.attribute("dst", "0.0.0.0/0");
+ xml.attribute("domain", "uplink"); }); }); });
+ }
+
+ void start_step(unsigned step_arg, Driver driver_arg = Driver())
+ {
+ if (driver_arg == Driver()) {
+ driver.destruct();
+ goal.construct(Result::DESTINATION_UNREACHABLE, 3);
+ } else {
+ driver.construct(driver_arg);
+ goal.construct(Result::REPLY, 3);
+ }
+ timeout.schedule(Microseconds(STEP_TIMEOUT_US));
+ update_router_config();
+ step = step_arg;
+ log("test step ", step, " started");
+ }
+
+ void step_succeeded()
+ {
+ log("test step ", step, " succeeded");
+ switch (step) {
+ case 0: start_step(1, "nic"); break;
+ case 1: start_step(2, "wifi"); break;
+ case 2: start_step(3); break;
+ case 3: start_step(4, "nic"); break;
+ case 4: start_step(5, "nic"); break;
+ case 5: start_step(6); break;
+ case 6: start_step(7, "wifi"); break;
+ case 7: start_step(8, "nic"); break;
+ case 8: env.parent().exit(0); break;
+ }
+ }
+
+ Main(Env &env) : env(env)
+ {
+ result_rom.sigh(result_handler);
+ handle_result();
+ }
+};
+
+void Component::construct(Env &env) { static Main main(env); }
diff --git a/repos/dde_linux/src/test/nic_router_uplinks/target.mk b/repos/dde_linux/src/test/nic_router_uplinks/target.mk
new file mode 100644
index 0000000000..2b878ec76e
--- /dev/null
+++ b/repos/dde_linux/src/test/nic_router_uplinks/target.mk
@@ -0,0 +1,14 @@
+TARGET = test-nic_router_uplinks
+
+LIBS += base
+
+NIC_ROUTER_DIR := $(call select_from_repositories,src/server/nic_router)
+
+SRC_CC += main.cc dns.cc xml_node.cc
+
+INC_DIR += $(PRG_DIR) $(NIC_ROUTER_DIR)
+
+vpath dns.cc $(NIC_ROUTER_DIR)
+vpath xml_node.cc $(NIC_ROUTER_DIR)
+
+CC_CXX_WARN_STRICT_CONVERSION =