diff --git a/repos/os/run/internet_checksum.run b/repos/os/run/internet_checksum.run
new file mode 100644
index 0000000000..0d4be7cff8
--- /dev/null
+++ b/repos/os/run/internet_checksum.run
@@ -0,0 +1,92 @@
+assert_spec linux
+
+set tshark [installed_command tshark]
+set trafgen [installed_command trafgen]
+set lx_fs_root "internet_checksum_dir"
+set lx_fs_dir "bin/$lx_fs_root"
+set input_file_name "input.pcap"
+set input_file "bin/$input_file_name"
+
+rename exit run_tool_exit
+proc exit {{code 0}} {
+ global lx_fs_dir
+ global input_file
+ if {[get_cmd_switch --autopilot]} { exec rm -rf $input_file $lx_fs_dir }
+ run_tool_exit $code
+}
+build { core init lib/ld lib/vfs test/internet_checksum server/lx_fs }
+create_boot_directory
+
+set seed ""
+if {[info exists ::env(SEED)]} {
+ set seed $::env(SEED)
+} else {
+ set min_seed 0
+ set max_seed [expr 2**32]
+ set seed [expr int($min_seed + floor(rand() * $max_seed))]
+}
+puts "\nUse script with SEED=$seed in order to get reproducible results\n"
+
+install_config {
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ }
+
+exec rm -rf $lx_fs_dir
+exec mkdir -p $lx_fs_dir
+exec $trafgen --rand --seed $seed --in [genode_dir]/repos/os/src/test/internet_checksum/trafgen.cfg --num 10000 --out $input_file
+
+proc assert_no_bad_checksums_in {pcap_file} {
+ global tshark
+ set view_filter "ip.checksum_bad.expert || tcp.checksum_bad.expert || udp.checksum.bad || icmp.checksum_bad"
+ set out ""
+ set out [exec $tshark -o ip.check_checksum:TRUE -o tcp.check_checksum:TRUE -o udp.check_checksum:TRUE -Y "$view_filter" -r $pcap_file]
+ if {$out != ""} {
+ puts "\nError: bad checksums found in $pcap_file\n"
+ exit -1
+ }
+}
+assert_no_bad_checksums_in $input_file
+build_boot_image [list {*}[build_artifacts] $lx_fs_root $input_file_name]
+append qemu_args " -nographic "
+run_genode_until {\[init\] child "test-internet_checksum" exited.*?\n} 30
+
+set output_file "$lx_fs_dir/output.pcap"
+assert_no_bad_checksums_in $output_file
+set num_checked_checksums ""
+set string_buf ""
+regexp {checked [0-9]+ checksums} $output string_buf
+regexp {[0-9]+} $string_buf num_checked_checksums
+
+set tmp_file "$lx_fs_dir/tmp"
+exec $tshark -o ip.check_checksum:TRUE -o tcp.check_checksum:TRUE -o udp.check_checksum:TRUE -V -r $output_file | grep -e "Checksum Status:" -e "Header checksum status:" > $tmp_file
+set num_output_checksums [exec wc -l < $tmp_file]
+if {$num_checked_checksums != $num_output_checksums} {
+ puts "\nError: number of checksums in $output_file differs from number of checked checksums\n"
+ exit -1
+}
+grep_output {\[init\] child "test-internet_checksum" exited}
+compare_output_to {[init] child "test-internet_checksum" exited with exit value 0}
diff --git a/repos/os/src/test/internet_checksum/README b/repos/os/src/test/internet_checksum/README
new file mode 100644
index 0000000000..3f07af7ca6
--- /dev/null
+++ b/repos/os/src/test/internet_checksum/README
@@ -0,0 +1,19 @@
+This test aims for testing the typical use of the internet checksum algorithms
+provided with the net library. It is accompanied by a similarly named test
+script.
+
+The test script feeds the test component with a sufficiently big input.pcap
+file that is randomly generated on each run using trafgen. The test component
+then iterates over all packets in the file. For each found IPv4, UDP, TCP or
+ICMP header (except the embedded ones in ICMP errors), it typically does the
+following:
+
+ 1. try to validate the initial checksum
+ 2. try to re-calculate the initial checksum and see if it stas the same
+ 3. modify the header randomly, update the checksum incrementally, and write
+ out the result to a file output.pcap
+
+The checksums in the resulting output.pcap file are then checked by the test
+script using tshark. On each run, the test script prints the seed used for
+randomization in both, the test component and trafgen. In order to reproduce a
+given test result, one can simply run the test script with SEED=.
diff --git a/repos/os/src/test/internet_checksum/main.cc b/repos/os/src/test/internet_checksum/main.cc
new file mode 100644
index 0000000000..67f794b65c
--- /dev/null
+++ b/repos/os/src/test/internet_checksum/main.cc
@@ -0,0 +1,275 @@
+/*
+ * \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.
+ */
+
+/* Genode includes */
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+
+using namespace Net;
+using namespace Genode;
+
+#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)
+
+
+struct Parser
+{
+ char *start;
+ size_t num_bytes;
+
+ void advance_by(size_t num_bytes)
+ {
+ ASSERT(num_bytes >= num_bytes);
+ start += num_bytes;
+ num_bytes -= num_bytes;
+ }
+
+ template
+ T const &fetch()
+ {
+ ASSERT(num_bytes >= sizeof(T));
+ T const &obj = *(T const *)start;
+ advance_by(sizeof(T));
+ return obj;
+ }
+
+ void fetch(Byte_range_ptr const &dst)
+ {
+ ASSERT(num_bytes >= dst.num_bytes);
+ memcpy(dst.start, start, dst.num_bytes);
+ advance_by(dst.num_bytes);
+ }
+};
+
+
+struct Pseudo_random_number_generator
+{
+ uint64_t seed;
+
+ uint8_t random_byte()
+ {
+ seed = (16807 * seed) % 2147483647;
+ return (uint8_t)seed;
+ }
+};
+
+
+struct Pcap_file_header
+{
+ static constexpr uint32_t MAGIC_NUMBER = 0xA1B2C3D4;
+
+ uint32_t magic_number;
+ uint32_t unused[5];
+
+ bool valid() const { return magic_number == MAGIC_NUMBER; }
+};
+
+
+struct Pcap_packet_record
+{
+ uint32_t unused_0[2];
+ uint32_t captured_pkt_len;
+ uint32_t original_pkt_len;
+};
+
+
+struct Main
+{
+ Env &env;
+ Heap heap { env.ram(), env.rm() };
+ Attached_rom_dataspace config_rom { env, "config" };
+ Root_directory root { env, heap, config_rom.xml().sub_node("vfs") };
+ Reconstructible pcap_file { root, "/output.pcap" };
+ Attached_rom_dataspace pcap_rom { env, "input.pcap" };
+ Parser pcap_parser { pcap_rom.local_addr(), pcap_rom.size() };
+ unsigned long num_errors = 0;
+ unsigned long num_packets = 0;
+ unsigned long num_ip4_checksums = 0;
+ unsigned long num_udp_checksums = 0;
+ unsigned long num_tcp_checksums = 0;
+ unsigned long num_icmp_checksums = 0;
+ Pseudo_random_number_generator prng { config_rom.xml().attribute_value("seed", 0ULL) };
+
+ Main(Env &env);
+
+ void check_tcp(Tcp_packet &tcp, Ipv4_packet &ip, size_t tcp_size);
+
+ void check_udp(Udp_packet &udp, Ipv4_packet &ip);
+
+ void check_icmp(Icmp_packet &icmp, size_t icmp_size);
+
+ void check_ip4(Ipv4_packet &ip);
+
+ void modify_ip4(Ipv4_packet &ip, Internet_checksum_diff &ip_icd);
+
+ void check_recalculated_checksum(char const *prot, uint16_t got_checksum, uint16_t expect_checksum)
+ {
+ if (got_checksum != expect_checksum) {
+ error("frame ", num_packets + 1, ": ", prot, ": re-calculating initial checksum failed (got ",
+ Hex(got_checksum)," expected ", Hex(expect_checksum), " diff ", expect_checksum - got_checksum, ")");
+ num_errors++;
+ }
+ }
+
+ void validate_initial_checksum_error(char const *prot)
+ {
+ error("frame ", num_packets + 1, ": ", prot, ": validating initial checksum failed");
+ num_errors++;
+ }
+};
+
+
+void Main::check_icmp(Icmp_packet &icmp, size_t icmp_size)
+{
+ uint16_t initial_checksum = icmp.checksum();
+ size_t l5_size = icmp_size - sizeof(Icmp_packet);
+ if (icmp.checksum_error(l5_size))
+ validate_initial_checksum_error("icmp");
+ icmp.update_checksum(l5_size);
+ check_recalculated_checksum("icmp", icmp.checksum(), initial_checksum);
+ num_icmp_checksums++;
+}
+
+
+void Main::check_udp(Udp_packet &udp, Ipv4_packet &ip)
+{
+ uint16_t initial_checksum = udp.checksum();
+ if (udp.checksum_error(ip.src(), ip.dst()))
+ validate_initial_checksum_error("udp");
+ udp.update_checksum(ip.src(), ip.dst());
+ check_recalculated_checksum("udp", udp.checksum(), initial_checksum);
+ num_udp_checksums++;
+}
+
+
+void Main::check_tcp(Tcp_packet &tcp, Ipv4_packet &ip, size_t tcp_size)
+{
+ uint16_t initial_checksum = tcp.checksum();
+ tcp.update_checksum(ip.src(), ip.dst(), tcp_size);
+ check_recalculated_checksum("tcp", tcp.checksum(), initial_checksum);
+ num_tcp_checksums++;
+}
+
+
+void Main::check_ip4(Ipv4_packet &ip)
+{
+ uint16_t initial_checksum = ip.checksum();
+ if (ip.checksum_error())
+ validate_initial_checksum_error("ip");
+ ip.update_checksum();
+ check_recalculated_checksum("ip", ip.checksum(), initial_checksum);
+ num_ip4_checksums++;
+}
+
+
+void Main::modify_ip4(Ipv4_packet &ip, Internet_checksum_diff &ip_icd)
+{
+ Ipv4_address ip_src = ip.src();
+ ip_src.addr[0] &= prng.random_byte();
+ ip_src.addr[1] |= prng.random_byte();
+ ip_src.addr[2] *= prng.random_byte();
+ ip_src.addr[3] += prng.random_byte();
+ ip.src(ip_src, ip_icd);
+
+ if (prng.random_byte() & 0b11)
+ return;
+
+ Ipv4_address ip_dst = ip.dst();
+ ip_dst.addr[0] |= prng.random_byte();
+ ip_dst.addr[1] += prng.random_byte();
+ ip_dst.addr[2] &= prng.random_byte();
+ ip_dst.addr[3] *= prng.random_byte();
+ ip.dst(ip_dst, ip_icd);
+}
+
+
+Main::Main(Env &env) : env(env)
+{
+ using Append_result = Append_file::Append_result;
+ static constexpr size_t BUF_SIZE = 1024;
+ char buf[BUF_SIZE];
+
+ Pcap_file_header const &header = pcap_parser.fetch();
+ ASSERT(header.valid());
+ ASSERT(pcap_file->append((char *)&header, sizeof(header)) == Append_result::OK);
+
+ while(1) {
+ if (pcap_parser.num_bytes < sizeof(Pcap_packet_record))
+ break;
+
+ Pcap_packet_record const &record = pcap_parser.fetch();
+ if (!record.captured_pkt_len)
+ break;
+
+ ASSERT(pcap_file->append((char *)&record, sizeof(record)) == Append_result::OK);
+ ASSERT(record.captured_pkt_len == record.original_pkt_len);
+ ASSERT(record.captured_pkt_len <= BUF_SIZE);
+ pcap_parser.fetch({buf, record.captured_pkt_len});
+
+ Size_guard size_guard(record.captured_pkt_len);
+ Ethernet_frame ð = Ethernet_frame::cast_from(buf, size_guard);
+ ASSERT(eth.type() == Ethernet_frame::Type::IPV4);
+ Ipv4_packet &ip = eth.data(size_guard);
+ check_ip4(ip);
+ size_t l4_size = ip.total_length() - sizeof(Ipv4_packet);
+ switch (ip.protocol()) {
+ case Ipv4_packet::Protocol::TCP: check_tcp(ip.data(size_guard), ip, l4_size); break;
+ case Ipv4_packet::Protocol::UDP: check_udp(ip.data(size_guard), ip); break;
+ case Ipv4_packet::Protocol::ICMP: check_icmp(ip.data(size_guard), l4_size); break;
+ default: break;
+ }
+ Internet_checksum_diff ip_icd { };
+ modify_ip4(ip, ip_icd);
+ switch (ip.protocol()) {
+ case Ipv4_packet::Protocol::TCP: ip.data(size_guard).update_checksum(ip.src(), ip.dst(), l4_size); break;
+ case Ipv4_packet::Protocol::UDP: ip.data(size_guard).update_checksum(ip.src(), ip.dst()); break;
+ case Ipv4_packet::Protocol::ICMP: ip.data(size_guard).update_checksum(l4_size - sizeof(Icmp_packet)); break;
+ default: break;
+ }
+ ip.update_checksum(ip_icd);
+ ip.checksum(ip.checksum());
+ ASSERT(pcap_file->append((char *)ð, record.captured_pkt_len) == Append_result::OK);
+ num_packets++;
+ }
+ unsigned long num_checksums = num_ip4_checksums + num_udp_checksums + num_tcp_checksums + num_icmp_checksums;
+ log("checked ", num_checksums, " checksum", num_checksums == 1 ? "" : "s",
+ " (ip4 ", num_ip4_checksums, " tcp ", num_tcp_checksums," udp ", num_udp_checksums, " icmp ", num_icmp_checksums,
+ ") in ", num_packets, " packet", num_packets == 1 ? "" : "s", " with ", num_errors, " error", num_errors == 1 ? "" : "s");
+
+ pcap_file.destruct();
+ env.parent().exit(num_errors ? -1 : 0);
+}
+
+
+void Component::construct(Env &env) { static Main main(env); }
diff --git a/repos/os/src/test/internet_checksum/target.mk b/repos/os/src/test/internet_checksum/target.mk
new file mode 100644
index 0000000000..f64c701492
--- /dev/null
+++ b/repos/os/src/test/internet_checksum/target.mk
@@ -0,0 +1,7 @@
+TARGET = test-internet_checksum
+
+LIBS += base net vfs
+
+SRC_CC += main.cc
+
+INC_DIR += $(PRG_DIR)
diff --git a/repos/os/src/test/internet_checksum/trafgen.cfg b/repos/os/src/test/internet_checksum/trafgen.cfg
new file mode 100644
index 0000000000..8353c08074
--- /dev/null
+++ b/repos/os/src/test/internet_checksum/trafgen.cfg
@@ -0,0 +1,61 @@
+/*
+ * FIXME: Currently, we skip testing odd ICMP packet lengths because trafgen
+ * generates bad checksums for such packets.
+ */
+
+/* UDP */
+{
+ eth(saddr=drnd(), daddr=drnd()),
+ ip4(saddr=drnd(), daddr=drnd(), ttl=drnd(), id=drnd()),
+ udp(sport=drnd(), dport=drnd()),
+ drnd(801)
+}
+{
+ eth(saddr=drnd(), daddr=drnd()),
+ ip4(saddr=drnd(), daddr=drnd(), ttl=drnd(), id=drnd()),
+ udp(sport=drnd(), dport=drnd()),
+ drnd(80)
+}
+/* TCP */
+{
+ eth(saddr=drnd(), daddr=drnd()),
+ ip4(saddr=drnd(), daddr=drnd(), ttl=drnd(), id=drnd()),
+ tcp(sport=drnd(), dport=drnd(), seq=drnd(), ackseq=drnd()),
+ drnd(701)
+}
+{
+ eth(saddr=drnd(), daddr=drnd()),
+ ip4(saddr=drnd(), daddr=drnd(), ttl=drnd(), id=drnd()),
+ tcp(sport=drnd(), dport=drnd(), seq=drnd(), ackseq=drnd()),
+ drnd(70)
+}
+/* ICMP echo */
+{
+ eth(saddr=drnd(), daddr=drnd()),
+ ip4(saddr=drnd(), daddr=drnd(), ttl=drnd(), id=drnd()),
+ icmp4(echorequest, addr=drnd(), id=drnd(), seq=drnd()),
+ drnd(600)
+}
+{
+ eth(saddr=drnd(), daddr=drnd()),
+ ip4(saddr=drnd(), daddr=drnd(), ttl=drnd(), id=drnd()),
+ icmp4(echoreply, addr=drnd(), id=drnd(), seq=drnd()),
+ drnd(60)
+}
+/* ICMP error + embedded packet */
+{
+ eth(saddr=drnd(), daddr=drnd()),
+ ip4(saddr=drnd(), daddr=drnd(), ttl=drnd(), id=drnd()),
+ icmp4(type=1, code=drnd(0,16), addr=drnd(), id=drnd(), seq=drnd()),
+ ip4(saddr=drnd(), daddr=drnd(), ttl=drnd(), id=drnd()),
+ udp(sport=drnd(), dport=drnd()),
+ drnd(500)
+}
+{
+ eth(saddr=drnd(), daddr=drnd()),
+ ip4(saddr=drnd(), daddr=drnd(), ttl=drnd(), id=drnd()),
+ icmp4(type=1, code=drnd(0,16), addr=drnd(), id=drnd(), seq=drnd()),
+ ip4(saddr=drnd(), daddr=drnd(), ttl=drnd(), id=drnd()),
+ tcp(sport=drnd(), dport=drnd(), seq=drnd(), ackseq=drnd()),
+ drnd(50)
+}
diff --git a/tool/autopilot.list b/tool/autopilot.list
index 2e463eb8fd..08d7fe84f9 100644
--- a/tool/autopilot.list
+++ b/tool/autopilot.list
@@ -22,6 +22,7 @@ hello
ieee754
init_smp
intel_fb
+internet_checksum
libc_integration
libc_vfs_fs_ext2
libc_vfs_fs_fat