diff --git a/repos/libports/run/tcp_bulk.inc b/repos/libports/run/tcp_bulk.inc
new file mode 100644
index 000000000..8e45d63b3
--- /dev/null
+++ b/repos/libports/run/tcp_bulk.inc
@@ -0,0 +1,112 @@
+create_boot_directory
+
+append build_components {
+ core init
+ drivers/timer
+ server/nic_bridge
+ server/nic_loopback
+ test/tcp
+}
+
+append_socket_fs_build_components
+
+build $build_components
+
+append config {
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ <} [socket_fs_plugin] { ip_addr="192.168.1.1" netmask="255.255.255.0"/>
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ <} [socket_fs_plugin] { ip_addr="192.168.1.2" netmask="255.255.255.0"/>
+
+
+
+
+
+
+
+
+
+
+}
+
+install_config $config
+
+append boot_modules {
+ core ld.lib.so init timer
+ nic_bridge
+ nic_loopback
+ posix.lib.so libc.lib.so libm.lib.so vfs.lib.so vfs_lwip.lib.so
+ test-tcp
+}
+
+append_socket_fs_boot_modules
+
+build_boot_image $boot_modules
+
+append qemu_args " -nographic"
+
+run_genode_until {child "recv" exited with exit value 0} 240
+
+# vi: set ft=tcl :
diff --git a/repos/libports/run/tcp_bulk_lwip.run b/repos/libports/run/tcp_bulk_lwip.run
new file mode 100644
index 000000000..fe9f4f914
--- /dev/null
+++ b/repos/libports/run/tcp_bulk_lwip.run
@@ -0,0 +1,2 @@
+source ${genode_dir}/repos/libports/run/vfs_lwip.inc
+source ${genode_dir}/repos/libports/run/tcp_bulk.inc
diff --git a/repos/libports/run/tcp_bulk_lxip.run b/repos/libports/run/tcp_bulk_lxip.run
new file mode 100644
index 000000000..3c5b33369
--- /dev/null
+++ b/repos/libports/run/tcp_bulk_lxip.run
@@ -0,0 +1,2 @@
+source ${genode_dir}/repos/dde_linux/run/vfs_lxip.inc
+source ${genode_dir}/repos/libports/run/tcp_bulk.inc
diff --git a/repos/libports/src/test/tcp/main.c b/repos/libports/src/test/tcp/main.c
new file mode 100644
index 000000000..9e337e7c3
--- /dev/null
+++ b/repos/libports/src/test/tcp/main.c
@@ -0,0 +1,167 @@
+/*
+ * \brief Libc tcp send and recv test
+ * \author Emery Hemingway
+ * \date 2018-09-17
+ */
+
+/*
+ * 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.
+ */
+
+/* Libc includes */
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+
+/* PCG includes */
+#include
+
+enum { NUM_TEST_INTS = 1<<20, BULK_ITERATIONS = 2 };
+
+static uint32_t data[NUM_TEST_INTS];
+
+int test_send(char const *host)
+{
+ usleep(1000000);
+
+ int sock = socket(AF_INET, SOCK_STREAM, 0);
+ if (sock < 0) {
+ perror("`socket` failed");
+ return sock;
+ }
+
+ {
+ struct sockaddr_in addr;
+ addr.sin_family = AF_INET;
+ addr.sin_addr.s_addr = inet_addr(host);
+ addr.sin_port = htons(2);
+
+ fprintf(stderr, "connect to %s\n", host);
+ if (connect(sock, (struct sockaddr *)&addr, sizeof(addr))) {
+ perror("`connect` failed");
+ return ~0;
+ }
+ }
+
+ pcg32_random_t rng = PCG32_INITIALIZER;
+ for (int j = 0; j < BULK_ITERATIONS; ++j) {
+ for (size_t i = 0; i < NUM_TEST_INTS; ++i)
+ data[i] = pcg32_random_r(&rng);
+
+ size_t total = sizeof(data);
+ size_t offset = 0;
+
+ char *buf = (char *)data;
+ while (offset < total) {
+ ssize_t res = send(sock, buf+offset, total-offset, 0);
+ if (res < 1) {
+ perror("send failed");
+ return -1;
+ }
+ offset += res;
+ }
+ }
+
+ fprintf(stderr, "close server\n");
+ shutdown(sock, SHUT_RDWR);
+ usleep(10000000);
+
+ return 0;
+}
+
+int test_recv()
+{
+ int sock = socket(AF_INET, SOCK_STREAM, 0);
+ if (sock < 0) {
+ perror("`socket` failed");
+ return sock;
+ }
+
+ {
+ struct sockaddr_in addr;
+ addr.sin_family = AF_INET;
+ addr.sin_addr.s_addr = INADDR_ANY;
+ addr.sin_port = htons(2);
+
+ if (bind(sock, (struct sockaddr *)&addr, sizeof(addr))) {
+ perror("`bind` failed");
+ return ~0;
+ }
+ }
+
+ if (listen(sock, 1)) {
+ perror("listen broke");
+ }
+
+ for (;;) {
+ char string[INET_ADDRSTRLEN];
+ struct sockaddr_in addr;
+ socklen_t super_socket_safety = sizeof(addr);
+
+ int client = accept(sock, (struct sockaddr*)&addr, &super_socket_safety);
+ if (client < 0) {
+ perror("invalid socket from accept!");
+ return ~0;
+ }
+
+ inet_ntop(AF_INET, &(addr.sin_addr), string, super_socket_safety);
+ fprintf(stderr, "recv from %s\n", string);
+
+ pcg32_random_t rng = PCG32_INITIALIZER;
+ for (int j = 0; j < BULK_ITERATIONS; ++j) {
+
+ size_t total = sizeof(data);
+ size_t offset = 0;
+ char *buf = (char *)data;
+ while (offset < total) {
+ ssize_t res = recv(client, buf+offset, total-offset, 0);
+ if (res < 1) {
+ perror("recv failed");
+ return ~0;
+ }
+ offset += res;
+ }
+
+ for (size_t i = 0; i < NUM_TEST_INTS; ++i) {
+ if (data[i] != pcg32_random_r(&rng)) {
+ fprintf(stderr, "bad data at byte offset %ld\n", i<<2);
+ return ~0;
+ }
+ }
+ }
+
+ fprintf(stderr, "close client\n");
+ shutdown(client, SHUT_RDWR);
+ return 0;
+ }
+}
+
+int main(int argc, char **argv)
+{
+ if (argc < 1) {
+ fprintf(stderr, "no test name passed thru argv\n");
+ return ~0;
+ }
+ if (argc < 2) {
+ if (strcmp(argv[0], "recv") == 0)
+ return test_recv();
+ }
+ if (argc < 3) {
+ if (strcmp(argv[0], "send") == 0)
+ return test_send(argv[1]);
+ }
+
+ fprintf(stderr, "\"%s\" not a valid test\n", argv[0]);
+ return ~0;
+}
diff --git a/repos/libports/src/test/tcp/target.mk b/repos/libports/src/test/tcp/target.mk
new file mode 100644
index 000000000..9d56b7e1d
--- /dev/null
+++ b/repos/libports/src/test/tcp/target.mk
@@ -0,0 +1,5 @@
+TARGET = test-tcp
+LIBS += posix libc libpcg_random
+SRC_C += main.c
+
+CC_CXX_WARN_STRICT =