diff --git a/repos/libports/lib/mk/libc.mk b/repos/libports/lib/mk/libc.mk
index 1f7aa89eae..7eb2cc2236 100644
--- a/repos/libports/lib/mk/libc.mk
+++ b/repos/libports/lib/mk/libc.mk
@@ -15,7 +15,7 @@ SRC_CC = atexit.cc dummies.cc rlimit.cc sysctl.cc \
          plugin.cc plugin_registry.cc select.cc exit.cc environ.cc nanosleep.cc \
          pread_pwrite.cc readv_writev.cc poll.cc \
          libc_pdbg.cc vfs_plugin.cc rtc.cc dynamic_linker.cc signal.cc \
-         socket_operations.cc task.cc addrinfo.cc
+         socket_operations.cc task.cc addrinfo.cc socket_fs_plugin.cc
 
 CC_OPT_sysctl += -Wno-write-strings
 
diff --git a/repos/libports/src/lib/libc/socket_fs_plugin.cc b/repos/libports/src/lib/libc/socket_fs_plugin.cc
new file mode 100644
index 0000000000..8ce3f0e260
--- /dev/null
+++ b/repos/libports/src/lib/libc/socket_fs_plugin.cc
@@ -0,0 +1,788 @@
+/*
+ * \brief  Libc pseudo plugin for socket fs
+ * \author Christian Helmuth
+ * \author Christian Prochaska
+ * \author Norman Feske
+ * \author Emery Hemingway
+ * \date   2015-06-23
+ */
+
+/*
+ * Copyright (C) 2015-2017 Genode Labs GmbH
+ *
+ * This file is part of the Genode OS framework, which is distributed
+ * under the terms of the GNU General Public License version 2.
+ */
+
+/* Genode includes */
+#include <base/env.h>
+#include <base/log.h>
+#include <vfs/types.h>
+#include <util/string.h>
+
+/* libc includes */
+#include <netinet/in.h>
+#include <netdb.h>
+#include <arpa/inet.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <ctype.h>
+#include <stdio.h>
+
+/* libc-internal includes */
+#include "socket_fs_plugin.h"
+#include "libc_file.h"
+#include "libc_errno.h"
+#include "task.h"
+
+
+namespace Libc {
+	extern char const *config_socket();
+	bool read_ready(Libc::File_descriptor *);
+}
+
+
+/***************
+ ** Utilities **
+ ***************/
+
+namespace {
+
+	using Libc::Errno;
+
+	struct Absolute_path : Vfs::Absolute_path
+	{
+		Absolute_path() { }
+
+		Absolute_path(char const *path, char const *pwd = 0)
+		:
+			Vfs::Absolute_path(path, pwd)
+		{
+			remove_trailing('\n');
+		}
+	};
+
+	struct Exception { };
+	struct New_socket_failed : Exception { };
+	struct Address_conversion_failed : Exception { };
+
+	struct Socket_context : Libc::Plugin_context
+	{
+		struct Inaccessible { }; /* exception */
+
+		Absolute_path path;
+
+		int _data_fd   = -1;
+		int _remote_fd = -1;
+		int _local_fd  = -1;
+
+		Socket_context(Absolute_path const &path)
+		: path(path.base()) { }
+
+		~Socket_context()
+		{
+			if (_data_fd != -1) close(_data_fd);
+		}
+
+		int _open_file(char const *file_name)
+		{
+			Absolute_path file(file_name, path.base());
+			int const fd = open(file.base(), O_RDWR);
+			if (fd == -1) {
+				Genode::error(__func__, ": ", file_name, " file not accessible");
+				throw Inaccessible();
+			}
+			return fd;
+		}
+
+		int data_fd()
+		{
+			if (_data_fd == -1)
+				_data_fd = _open_file("data");
+
+			return _data_fd;
+		}
+
+		bool data_read_ready()
+		{
+			return Libc::read_ready(
+				Libc::file_descriptor_allocator()->find_by_libc_fd(data_fd()));
+		}
+
+		int local_fd()
+		{
+			if (_local_fd == -1)
+				_local_fd = _open_file("local");
+
+			return _local_fd;
+		}
+
+		int remote_fd()
+		{
+			if (_remote_fd == -1)
+				_remote_fd = _open_file("remote");
+
+			return _remote_fd;
+		}
+
+		bool remote_read_ready()
+		{
+			return Libc::read_ready(
+				Libc::file_descriptor_allocator()->find_by_libc_fd(remote_fd()));
+		}
+	};
+
+	struct Socket_plugin : Libc::Plugin
+	{
+		bool supports_select(int, fd_set *, fd_set *, fd_set *, timeval *) override;
+
+		ssize_t read(Libc::File_descriptor *, void *, ::size_t) override;
+		ssize_t write(Libc::File_descriptor *, const void *, ::size_t) override;
+		int close(Libc::File_descriptor *) override;
+		int select(int, fd_set *, fd_set *, fd_set *, timeval *) override;
+	};
+
+	Socket_plugin & socket_plugin()
+	{
+		static Socket_plugin inst;
+		return inst;
+	}
+
+	template <int CAPACITY> class String
+	{
+		private:
+
+			char _buf[CAPACITY] { 0 };
+
+		public:
+
+			String() { }
+
+			constexpr size_t capacity() { return CAPACITY; }
+
+			char const * base() const { return _buf; }
+			char       * base()       { return _buf; }
+
+			void terminate(size_t at) { _buf[at] = 0; }
+
+			void remove_trailing_newline()
+			{
+				int i = 0;
+				while (_buf[i] && _buf[i + 1]) i++;
+
+				if (i > 0 && _buf[i] == '\n')
+					_buf[i] = 0;
+			}
+	};
+
+	typedef String<NI_MAXHOST> Host_string;
+	typedef String<NI_MAXSERV> Port_string;
+
+	/*
+	 * Both NI_MAXHOST and NI_MAXSERV include the terminating 0, which allows
+	 * use to put ':' between host and port on concatenation.
+	 */
+	struct Sockaddr_string : String<NI_MAXHOST + NI_MAXSERV>
+	{
+		Sockaddr_string() { }
+
+		Sockaddr_string(Host_string const &host, Port_string const &port)
+		{
+			char *b = base();
+			b = stpcpy(b, host.base());
+			b = stpcpy(b, ":");
+			b = stpcpy(b, port.base());
+		}
+
+		Host_string host() const
+		{
+			Host_string host;
+
+			strncpy(host.base(), base(), host.capacity());
+			char *at = strstr(host.base(), ":");
+			if (!at)
+				throw Address_conversion_failed();
+			*at = 0;
+
+			return host;
+		}
+
+		Port_string port() const
+		{
+			Port_string port;
+
+			char *at = strstr(base(), ":");
+			if (!at)
+				throw Address_conversion_failed();
+
+			strncpy(port.base(), ++at, port.capacity());
+
+			return port;
+		}
+	};
+}
+
+
+/* TODO move to C++ structs or something */
+
+static Port_string port_string(sockaddr_in const &addr)
+{
+	Port_string port;
+
+	if (getnameinfo((sockaddr *)&addr, sizeof(addr),
+	                nullptr, 0, /* no host conversion */
+	                port.base(), port.capacity(),
+	                NI_NUMERICHOST | NI_NUMERICSERV) != 0)
+		throw Address_conversion_failed();
+
+	return port;
+}
+
+
+static Host_string host_string(sockaddr_in const &addr)
+{
+	Host_string host;
+
+	if (getnameinfo((sockaddr *)&addr, sizeof(addr),
+	                host.base(), host.capacity(),
+	                nullptr, 0, /* no port conversion */
+	                NI_NUMERICHOST | NI_NUMERICSERV) != 0)
+		throw Address_conversion_failed();
+
+	return host;
+}
+
+
+static sockaddr_in sockaddr_in_struct(Host_string const &host, Port_string const &port)
+{
+	addrinfo hints;
+	addrinfo *info = nullptr;
+
+	memset(&hints, 0, sizeof(hints));
+	hints.ai_flags = AI_NUMERICHOST | AI_NUMERICSERV;
+
+	if (getaddrinfo(host.base(), port.base(), &hints, &info))
+		throw Address_conversion_failed();
+
+	sockaddr_in addr = *(sockaddr_in*)info->ai_addr;
+
+	freeaddrinfo(info);
+
+	return addr;
+}
+
+
+static int read_sockaddr_in(int read_fd, Socket_context *context,
+                            struct sockaddr_in *addr, socklen_t *addrlen,
+                            bool block_for_read = false)
+{
+	if (!addr)                     return Errno(EFAULT);
+	if (!addrlen || *addrlen <= 0) return Errno(EINVAL);
+	if (read_fd == -1)             return Errno(ENOTCONN);
+
+	while (block_for_read && !context->remote_read_ready())
+		Libc::suspend();
+
+	Sockaddr_string addr_string;
+	int const n = read(read_fd, addr_string.base(), addr_string.capacity() - 1);
+
+	if (!n) /* probably a UDP socket */
+		return Errno(ENOTCONN);
+	if (n == -1 || !n || n >= (int)addr_string.capacity() - 1)
+		return Errno(EINVAL);
+
+	addr_string.terminate(n);
+	addr_string.remove_trailing_newline();
+
+	try {
+		/* convert the address but do not exceed the caller's buffer */
+		sockaddr_in saddr = sockaddr_in_struct(addr_string.host(), addr_string.port());
+		memcpy(addr, &saddr, *addrlen);
+		*addrlen = sizeof(saddr);
+
+		return 0;
+	} catch (Address_conversion_failed) {
+		Genode::warning("IP address conversion failed");
+		return Errno(ENOBUFS);
+	}
+}
+
+
+/***********************
+ ** Address functions **
+ ***********************/
+
+extern "C" int socket_fs_getpeername(int libc_fd, sockaddr *addr, socklen_t *addrlen)
+{
+	Libc::File_descriptor *fd = Libc::file_descriptor_allocator()->find_by_libc_fd(libc_fd);
+	if (!fd) return Errno(EBADF);
+
+	Socket_context *context;
+	if (!(context = dynamic_cast<Socket_context *>(fd->context)))
+		return Errno(ENOTSOCK);
+
+	return read_sockaddr_in(context->remote_fd(), context, (sockaddr_in *)addr, addrlen);
+}
+
+
+extern "C" int socket_fs_getsockname(int libc_fd, sockaddr *addr, socklen_t *addrlen)
+{
+	Libc::File_descriptor *fd = Libc::file_descriptor_allocator()->find_by_libc_fd(libc_fd);
+	if (!fd) return Errno(EBADF);
+
+	Socket_context *context;
+	if (!(context = dynamic_cast<Socket_context *>(fd->context)))
+		return Errno(ENOTSOCK);
+
+	return read_sockaddr_in(context->local_fd(), context, (sockaddr_in *)addr, addrlen);
+}
+
+
+/**************************
+ ** Socket transport API **
+ **************************/
+
+extern "C" int socket_fs_accept(int libc_fd, sockaddr *addr, socklen_t *addrlen)
+{
+	Libc::File_descriptor *fd = Libc::file_descriptor_allocator()->find_by_libc_fd(libc_fd);
+	if (!fd)
+		return Errno(EBADF);
+	Socket_context *context;
+	if (!(context = dynamic_cast<Socket_context *>(fd->context)))
+		return Errno(ENOTSOCK);
+
+	char accept_socket[10];
+	{
+		Absolute_path file("accept", context->path.base());
+		int const fd = open(file.base(), O_RDONLY);
+		if (fd == -1) {
+			Genode::error(__func__, ": accept file not accessible");
+			return Errno(EINVAL);
+		}
+		int n = 0;
+		/* XXX currently reading accept may return without new connection */
+		do { n = read(fd, accept_socket, sizeof(accept_socket)); } while (n == 0);
+		close(fd);
+		if (n == -1 || n >= (int)sizeof(accept_socket) - 1)
+			return Errno(EINVAL);
+
+		accept_socket[n] = 0;
+	}
+
+	Absolute_path accept_path(accept_socket, Libc::config_socket());
+	Socket_context *accept_context = new (Genode::env()->heap())
+	                                      Socket_context(accept_path);
+	Libc::File_descriptor *accept_fd =
+		Libc::file_descriptor_allocator()->alloc(&socket_plugin(), accept_context);
+
+	if (addr && addrlen) {
+		Absolute_path file("remote", accept_path.base());
+		int const fd = open(file.base(), O_RDONLY);
+		if (fd == -1) {
+			Genode::error(__func__, ": remote file not accessible");
+			return Errno(EINVAL);
+		}
+		Sockaddr_string remote;
+		int const n = read(fd, remote.base(), remote.capacity() - 1);
+		close(fd);
+		if (n == -1 || !n || n >= (int)remote.capacity() - 1)
+			return Errno(EINVAL);
+
+		remote.terminate(n);
+		remote.remove_trailing_newline();
+
+		sockaddr_in remote_addr = sockaddr_in_struct(remote.host(), remote.port());
+		memcpy(addr, &remote_addr, *addrlen);
+		*addrlen = sizeof(remote_addr);
+	}
+
+	return accept_fd->libc_fd;
+}
+
+
+extern "C" int socket_fs_bind(int libc_fd, sockaddr const *addr, socklen_t addrlen)
+{
+	if (addr->sa_family != AF_INET) {
+		Genode::error(__func__, ": family not supported");
+		return Errno(EAFNOSUPPORT);
+	}
+
+	Libc::File_descriptor *fd = Libc::file_descriptor_allocator()->find_by_libc_fd(libc_fd);
+	if (!fd)
+		return Errno(EBADF);
+	Socket_context *context;
+	if (!(context = dynamic_cast<Socket_context *>(fd->context)))
+		return Errno(ENOTSOCK);
+
+	{
+		Sockaddr_string addr_string(host_string(*(sockaddr_in *)addr),
+		                            port_string(*(sockaddr_in *)addr));
+
+		Absolute_path file("bind", context->path.base());
+		int const fd = open(file.base(), O_WRONLY);
+		if (fd == -1) {
+			Genode::error(__func__, ": bind file not accessible");
+			return Errno(EINVAL);
+		}
+		int const len = strlen(addr_string.base());
+		int const n   = write(fd, addr_string.base(), len);
+		close(fd);
+		if (n != len) return Errno(EIO);
+	}
+
+	return 0;
+}
+
+
+extern "C" int socket_fs_connect(int libc_fd, sockaddr const *addr, socklen_t addrlen)
+{
+	if (addr->sa_family != AF_INET) {
+		Genode::error(__func__, ": family not supported");
+		return Errno(EAFNOSUPPORT);
+	}
+
+	Libc::File_descriptor *fd = Libc::file_descriptor_allocator()->find_by_libc_fd(libc_fd);
+	if (!fd)
+		return Errno(EBADF);
+	Socket_context *context;
+	if (!(context = dynamic_cast<Socket_context *>(fd->context)))
+		return Errno(ENOTSOCK);
+
+	{
+		Sockaddr_string addr_string(host_string(*(sockaddr_in const *)addr),
+		                            port_string(*(sockaddr_in const *)addr));
+
+		Absolute_path file("connect", context->path.base());
+		int const fd = open(file.base(), O_WRONLY);
+		if (fd == -1) {
+			Genode::error(__func__, ": connect file not accessible");
+			return Errno(EINVAL);
+		}
+		int const len = strlen(addr_string.base());
+		int const n   = write(fd, addr_string.base(), len);
+		close(fd);
+		if (n != len) return Errno(EIO);
+	}
+
+	return 0;
+}
+
+
+extern "C" int socket_fs_listen(int libc_fd, int backlog)
+{
+	Libc::File_descriptor *fd = Libc::file_descriptor_allocator()->find_by_libc_fd(libc_fd);
+	if (!fd)
+		return Errno(EBADF);
+	Socket_context *context;
+	if (!(context = dynamic_cast<Socket_context *>(fd->context)))
+		return Errno(ENOTSOCK);
+
+	{
+		Absolute_path file("listen", context->path.base());
+		int const fd = open(file.base(), O_WRONLY);
+		if (fd == -1) {
+			Genode::error(__func__, ": listen file not accessible");
+			return Errno(EINVAL);
+		}
+		char buf[10];
+		snprintf(buf, sizeof(buf), "%d", backlog);
+		int const n = write(fd, buf, strlen(buf));
+		close(fd);
+		if ((unsigned)n != strlen(buf)) return Errno(EIO);
+	}
+
+	return 0;
+}
+
+
+static ssize_t do_recvfrom(Libc::File_descriptor *fd,
+                           void *buf, ::size_t len, int flags,
+                           struct sockaddr *src_addr, socklen_t *src_addrlen)
+{
+	Socket_context *context = dynamic_cast<Socket_context *>(fd->context);
+	if (!context)     return Errno(ENOTSOCK);
+	if (!buf || !len) return Errno(EINVAL);
+
+	/* TODO if "remote" is empty we have to block for the next packet */
+	if (src_addr) {
+		int const res = read_sockaddr_in(context->remote_fd(), context,
+		                                 (sockaddr_in *)src_addr, src_addrlen,
+		                                 true);
+		if (res < 0) return res;
+	}
+
+	try {
+		lseek(context->data_fd(), 0, 0);
+		ssize_t out_len = read(context->data_fd(), buf, len);
+		return out_len;
+	} catch (Socket_context::Inaccessible) {
+		return Errno(EINVAL);
+	}
+}
+
+
+extern "C" ssize_t socket_fs_recvfrom(int libc_fd, void *buf, ::size_t len, int flags,
+                                      sockaddr *src_addr, socklen_t *src_addrlen)
+{
+	Libc::File_descriptor *fd = Libc::file_descriptor_allocator()->find_by_libc_fd(libc_fd);
+	if (!fd) return Errno(EBADF);
+
+	return do_recvfrom(fd, buf, len, flags, src_addr, src_addrlen);
+}
+
+
+extern "C" ssize_t socket_fs_recv(int libc_fd, void *buf, ::size_t len, int flags)
+{
+	/* identical to recvfrom() with a NULL src_addr argument */
+	return socket_fs_recvfrom(libc_fd, buf, len, flags, nullptr, nullptr);
+}
+
+
+extern "C" ssize_t socket_fs_recvmsg(int libc_fd, msghdr *msg, int flags)
+{
+	Genode::warning("##########  TODO  ########## ", __func__);
+	return 0;
+}
+
+
+static ssize_t do_sendto(Libc::File_descriptor *fd,
+                         void const *buf, ::size_t len, int flags,
+                         sockaddr const *dest_addr, socklen_t dest_addrlen)
+{
+	Socket_context *context = dynamic_cast<Socket_context *>(fd->context);
+	if (!context)     return Errno(ENOTSOCK);
+	if (!buf || !len) return Errno(EINVAL);
+
+	if (dest_addr) {
+		Sockaddr_string addr_string(host_string(*(sockaddr_in const *)dest_addr),
+		                            port_string(*(sockaddr_in const *)dest_addr));
+
+#warning TODO use remote_fd()
+		Absolute_path file("remote", context->path.base());
+		int const fd = open(file.base(), O_WRONLY);
+		if (fd == -1) {
+			Genode::error(__func__, ": remote file not accessible");
+			return Errno(EINVAL);
+		}
+		int const len = strlen(addr_string.base());
+		int const n   = write(fd, addr_string.base(), len);
+		close(fd);
+		if (n != len) return Errno(EIO);
+	}
+
+	try {
+		lseek(context->data_fd(), 0, 0);
+		ssize_t out_len = write(context->data_fd(), buf, len);
+		return out_len;
+	} catch (Socket_context::Inaccessible) {
+		return Errno(EINVAL);
+	}
+}
+
+
+extern "C" ssize_t socket_fs_sendto(int libc_fd, void const *buf, ::size_t len, int flags,
+                                    sockaddr const *dest_addr, socklen_t dest_addrlen)
+{
+	Libc::File_descriptor *fd = Libc::file_descriptor_allocator()->find_by_libc_fd(libc_fd);
+	if (!fd) return Errno(EBADF);
+
+	return do_sendto(fd, buf, len, flags, dest_addr, dest_addrlen);
+}
+
+
+extern "C" ssize_t socket_fs_send(int libc_fd, void const *buf, ::size_t len, int flags)
+{
+	/* identical to sendto() with a NULL dest_addr argument */
+	return socket_fs_sendto(libc_fd, buf, len, flags, nullptr, 0);
+}
+
+
+extern "C" int socket_fs_getsockopt(int libc_fd, int level, int optname,
+                                    void *optval, socklen_t *optlen)
+{
+	Genode::warning("##########  TODO  ########## ", __func__);
+	return 0;
+}
+
+
+extern "C" int socket_fs_setsockopt(int libc_fd, int level, int optname,
+                                    void const *optval, socklen_t optlen)
+{
+	Genode::warning("##########  TODO  ########## ", __func__);
+	return 0;
+}
+
+
+extern "C" int socket_fs_shutdown(int libc_fd, int how)
+{
+	/* TODO ENOTCONN */
+
+	Libc::File_descriptor *fd = Libc::file_descriptor_allocator()->find_by_libc_fd(libc_fd);
+	if (!fd)
+		return Errno(EBADF);
+	Socket_context *context;
+	if (!(context = dynamic_cast<Socket_context *>(fd->context)))
+		return Errno(ENOTSOCK);
+
+	unlink(context->path.base());
+
+	return 0;
+}
+
+
+static Genode::String<16> new_socket(Absolute_path const &path)
+{
+	Absolute_path new_socket("new_socket", path.base());
+
+	int const fd = open(new_socket.base(), O_RDONLY);
+	if (fd == -1) {
+		Genode::error(__func__, ": new_socket file not accessible - socket fs not mounted?");
+		throw New_socket_failed();
+	}
+	char buf[10];
+	int const n = read(fd, buf, sizeof(buf));
+	close(fd);
+	if (n == -1 || !n || n >= (int)sizeof(buf) - 1)
+		throw New_socket_failed();
+	buf[n] = 0;
+
+	return Genode::String<16>(buf);
+}
+
+
+extern "C" int socket_fs_socket(int domain, int type, int protocol)
+{
+	Absolute_path path(Libc::config_socket());
+
+	if (path == "") {
+		Genode::error(__func__, ": socket fs not mounted");
+		return Errno(EACCES);
+	}
+
+	if ((type != SOCK_STREAM || (protocol != 0 && protocol != IPPROTO_TCP))
+	 && (type != SOCK_DGRAM  || (protocol != 0 && protocol != IPPROTO_UDP))) {
+		Genode::error(__func__,
+		              ": socket with type=", type,
+		              " protocol=", protocol, " not supported");
+		return Errno(EAFNOSUPPORT);
+	}
+
+	/* socket is ensured to be TCP or UDP */
+	try {
+		Absolute_path proto_path(path);
+		if (type == SOCK_STREAM)
+			proto_path.append("/tcp");
+		else
+			proto_path.append("/udp");
+
+		Genode::String<16> socket_path = new_socket(proto_path);
+		path.append("/");
+		path.append(socket_path.string());
+	} catch (New_socket_failed) { return Errno(EINVAL); }
+
+	Socket_context *context = new (Genode::env()->heap())
+	                          Socket_context(path);
+	Libc::File_descriptor *fd =
+		Libc::file_descriptor_allocator()->alloc(&socket_plugin(), context);
+
+	return fd->libc_fd;
+}
+
+
+/****************************
+ ** File-plugin operations **
+ ****************************/
+
+//int Socket_plugin::fcntl(Libc::File_descriptor *fd, int cmd, long arg) override;
+
+ssize_t Socket_plugin::read(Libc::File_descriptor *fd, void *buf, ::size_t count)
+{
+	return do_recvfrom(fd, buf, count, 0, nullptr, nullptr);
+}
+
+
+ssize_t Socket_plugin::write(Libc::File_descriptor *fd, const void *buf, ::size_t count)
+{
+	return do_sendto(fd, buf, count, 0, nullptr, 0);
+}
+
+
+bool Socket_plugin::supports_select(int nfds,
+                                    fd_set *readfds, fd_set *writefds, fd_set *exceptfds,
+                                    struct timeval *timeout)
+{
+	/* return true if any file descriptor (which is set) belongs to the VFS */
+	for (int fd = 0; fd < nfds; ++fd) {
+
+		if (FD_ISSET(fd, readfds) || FD_ISSET(fd, writefds) || FD_ISSET(fd, exceptfds)) {
+			Libc::File_descriptor *fdo =
+				Libc::file_descriptor_allocator()->find_by_libc_fd(fd);
+
+			if (fdo && (fdo->plugin == this))
+				return true;
+		}
+	}
+
+	return false;
+}
+
+
+int Socket_plugin::select(int nfds,
+                          fd_set *readfds, fd_set *writefds, fd_set *exceptfds,
+                          timeval *timeout)
+{
+	int nready = 0;
+
+	fd_set const in_readfds  = *readfds;
+	fd_set const in_writefds = *writefds;
+	/* XXX exceptfds not supported */
+
+	/* clear fd sets */
+	FD_ZERO(readfds);
+	FD_ZERO(writefds);
+	FD_ZERO(exceptfds);
+
+	for (int fd = 0; fd < nfds; ++fd) {
+
+		Libc::File_descriptor *fdo =
+			Libc::file_descriptor_allocator()->find_by_libc_fd(fd);
+
+		/* handle only fds that belong to this plugin */
+		if (!fdo || (fdo->plugin != this))
+			continue;
+
+		if (FD_ISSET(fd, &in_readfds)) {
+			try {
+				Socket_context *context = dynamic_cast<Socket_context *>(fdo->context);
+				if (context->data_read_ready()) {
+					FD_SET(fd, readfds);
+					++nready;
+				}
+			} catch (Socket_context::Inaccessible) { }
+		}
+
+		if (FD_ISSET(fd, &in_writefds)) {
+			if (false /* XXX ask if "data" is writeable */) {
+				FD_SET(fd, writefds);
+				++nready;
+			}
+		}
+
+		/* XXX exceptfds not supported */
+	}
+
+	return nready;
+}
+
+
+int Socket_plugin::close(Libc::File_descriptor *fd)
+{
+	Socket_context *context = dynamic_cast<Socket_context *>(fd->context);
+	if (!context) return Errno(EBADF);
+
+	Genode::destroy(Genode::env()->heap(), context);
+	Libc::file_descriptor_allocator()->free(fd);
+
+	return 0;
+}
diff --git a/repos/libports/src/lib/libc/socket_fs_plugin.h b/repos/libports/src/lib/libc/socket_fs_plugin.h
new file mode 100644
index 0000000000..223b04de5f
--- /dev/null
+++ b/repos/libports/src/lib/libc/socket_fs_plugin.h
@@ -0,0 +1,38 @@
+/*
+ * \brief  Libc pseudo plugin for socket fs
+ * \author Emery Hemingway
+ * \author Christian Helmuth
+ * \date   2017-02-11
+ */
+
+/*
+ * Copyright (C) 2017 Genode Labs GmbH
+ *
+ * This file is part of the Genode OS framework, which is distributed
+ * under the terms of the GNU General Public License version 2.
+ */
+
+#ifndef _LIBC__SOCKET_FS_PLUGIN_H_
+#define _LIBC__SOCKET_FS_PLUGIN_H_
+
+/* Libc includes */
+#include <sys/types.h>
+#include <sys/socket.h>
+
+extern "C" int socket_fs_getpeername(int, sockaddr *, socklen_t *);
+extern "C" int socket_fs_getsockname(int, sockaddr *, socklen_t *);
+extern "C" int socket_fs_accept(int, sockaddr *, socklen_t *);
+extern "C" int socket_fs_bind(int, sockaddr const *, socklen_t);
+extern "C" int socket_fs_connect(int, sockaddr const *, socklen_t);
+extern "C" int socket_fs_listen(int, int);
+extern "C" ssize_t socket_fs_recvfrom(int, void *, ::size_t, int, sockaddr *, socklen_t *);
+extern "C" ssize_t socket_fs_recv(int, void *, ::size_t, int);
+extern "C" ssize_t socket_fs_recvmsg(int, msghdr *, int);
+extern "C" ssize_t socket_fs_sendto(int, void const *, ::size_t, int, sockaddr const *, socklen_t);
+extern "C" ssize_t socket_fs_send(int, void const *, ::size_t, int);
+extern "C" int socket_fs_getsockopt(int, int, int, void *, socklen_t *);
+extern "C" int socket_fs_setsockopt(int, int, int, void const *, socklen_t);
+extern "C" int socket_fs_shutdown(int, int);
+extern "C" int socket_fs_socket(int, int, int);
+
+#endif /* _LIBC__SOCKET_FS_PLUGIN_H_ */
diff --git a/repos/libports/src/lib/libc/socket_operations.cc b/repos/libports/src/lib/libc/socket_operations.cc
index b215a44003..dcc9689831 100644
--- a/repos/libports/src/lib/libc/socket_operations.cc
+++ b/repos/libports/src/lib/libc/socket_operations.cc
@@ -7,7 +7,7 @@
  */
 
 /*
- * Copyright (C) 2015 Genode Labs GmbH
+ * Copyright (C) 2015-2017 Genode Labs GmbH
  *
  * This file is part of the Genode OS framework, which is distributed
  * under the terms of the GNU General Public License version 2.
@@ -19,150 +19,238 @@
 #include <util/token.h>
 
 /* libc includes */
-#include <sys/types.h>
-#include <sys/socket.h>
 #include <errno.h>
 
 /* libc-internal includes */
 #include "libc_file.h"
+#include "socket_fs_plugin.h"
 
 using namespace Libc;
 
+namespace Libc { extern char const *config_socket(); }
 
-extern "C" int _accept(int libc_fd, struct sockaddr *addr, socklen_t *addrlen)
+
+/***********************
+ ** Address functions **
+ ***********************/
+
+extern "C" int _getpeername(int libc_fd, sockaddr *addr, socklen_t *addrlen)
 {
-	return accept(libc_fd, addr, addrlen);
+	if (*Libc::config_socket())
+		return socket_fs_getpeername(libc_fd, addr, addrlen);
+
+	FD_FUNC_WRAPPER(getpeername, libc_fd, addr, addrlen);
 }
 
 
-extern "C" int accept(int libc_fd, struct sockaddr *addr, socklen_t *addrlen)
+extern "C" int getpeername(int libc_fd, sockaddr *addr, socklen_t *addrlen)
 {
+	return _getpeername(libc_fd, addr, addrlen);
+}
+
+
+extern "C" int _getsockname(int libc_fd, sockaddr *addr, socklen_t *addrlen)
+{
+	if (*Libc::config_socket())
+		return socket_fs_getsockname(libc_fd, addr, addrlen);
+
+	FD_FUNC_WRAPPER(getsockname, libc_fd, addr, addrlen);
+}
+
+
+extern "C" int getsockname(int libc_fd, sockaddr *addr, socklen_t *addrlen)
+{
+	return _getsockname(libc_fd, addr, addrlen);
+}
+
+
+/**************************
+ ** Socket transport API **
+ **************************/
+
+extern "C" int _accept(int libc_fd, sockaddr *addr, socklen_t *addrlen)
+{
+	if (*Libc::config_socket())
+		return socket_fs_accept(libc_fd, addr, addrlen);
+
 	File_descriptor *ret_fd;
 	FD_FUNC_WRAPPER_GENERIC(ret_fd =, 0, accept, libc_fd, addr, addrlen);
 	return ret_fd ? ret_fd->libc_fd : INVALID_FD;
 }
 
 
-extern "C" int _bind(int libc_fd, const struct sockaddr *addr,
-                     socklen_t addrlen)
+extern "C" int accept(int libc_fd, sockaddr *addr, socklen_t *addrlen)
 {
-	return bind(libc_fd, addr, addrlen);
+	return _accept(libc_fd, addr, addrlen);
 }
 
 
-extern "C" int bind(int libc_fd, const struct sockaddr *addr,
-                    socklen_t addrlen) {
-	FD_FUNC_WRAPPER(bind, libc_fd, addr, addrlen); }
-
-
-extern "C" int connect(int libc_fd, const struct sockaddr *addr,
-                       socklen_t addrlen) {
-	FD_FUNC_WRAPPER(connect, libc_fd, addr, addrlen); }
-
-
-extern "C" int _connect(int libc_fd, const struct sockaddr *addr,
-                        socklen_t addrlen)
+extern "C" int _bind(int libc_fd, sockaddr const *addr, socklen_t addrlen)
 {
-    return connect(libc_fd, addr, addrlen);
+	if (*Libc::config_socket())
+		return socket_fs_bind(libc_fd, addr, addrlen);
+
+	FD_FUNC_WRAPPER(bind, libc_fd, addr, addrlen);
 }
 
 
-extern "C" int _getpeername(int libc_fd, struct sockaddr *addr, socklen_t *addrlen) {
-	FD_FUNC_WRAPPER(getpeername, libc_fd, addr, addrlen); }
-
-
-extern "C" int getpeername(int libc_fd, struct sockaddr *addr, socklen_t *addrlen)
+extern "C" int bind(int libc_fd, sockaddr const *addr, socklen_t addrlen)
 {
-	return _getpeername(libc_fd, addr, addrlen);
+	return _bind(libc_fd, addr, addrlen);
 }
 
 
-extern "C" int _getsockname(int libc_fd, struct sockaddr *addr, socklen_t *addrlen) {
-	FD_FUNC_WRAPPER(getsockname, libc_fd, addr, addrlen); }
-
-
-extern "C" int getsockname(int libc_fd, struct sockaddr *addr, socklen_t *addrlen)
+extern "C" int _connect(int libc_fd, sockaddr const *addr, socklen_t addrlen)
 {
-	return _getsockname(libc_fd, addr, addrlen);
+	if (*Libc::config_socket())
+		return socket_fs_connect(libc_fd, addr, addrlen);
+
+	FD_FUNC_WRAPPER(connect, libc_fd, addr, addrlen);
+}
+
+
+extern "C" int connect(int libc_fd, sockaddr const *addr, socklen_t addrlen)
+{
+    return _connect(libc_fd, addr, addrlen);
 }
 
 
 extern "C" int _listen(int libc_fd, int backlog)
 {
-	return listen(libc_fd, backlog);
+	if (*Libc::config_socket())
+		return socket_fs_listen(libc_fd, backlog);
+
+	FD_FUNC_WRAPPER(listen, libc_fd, backlog);
 }
 
 
-extern "C" int listen(int libc_fd, int backlog) {
-	FD_FUNC_WRAPPER(listen, libc_fd, backlog); }
-
-
-extern "C" ssize_t recv(int libc_fd, void *buf, ::size_t len, int flags) {
-	FD_FUNC_WRAPPER(recv, libc_fd, buf, len, flags); }
+extern "C" int listen(int libc_fd, int backlog)
+{
+	return _listen(libc_fd, backlog);
+}
 
 
 extern "C" ssize_t _recvfrom(int libc_fd, void *buf, ::size_t len, int flags,
-                              struct sockaddr *src_addr, socklen_t *addrlen) {
-	FD_FUNC_WRAPPER(recvfrom, libc_fd, buf, len, flags, src_addr, addrlen); }
-
-
-extern "C" ssize_t recvfrom(int libc_fd, void *buf, ::size_t len, int flags,
-                              struct sockaddr *src_addr, socklen_t *addrlen)
+                             sockaddr *src_addr, socklen_t *src_addrlen)
 {
-	return _recvfrom(libc_fd, buf, len, flags, src_addr, addrlen);
+	if (*Libc::config_socket())
+		return socket_fs_recvfrom(libc_fd, buf, len, flags, src_addr, src_addrlen);
+
+	FD_FUNC_WRAPPER(recvfrom, libc_fd, buf, len, flags, src_addr, src_addrlen);
 }
 
 
-extern "C" ssize_t recvmsg(int libc_fd, struct msghdr *msg, int flags) {
-	FD_FUNC_WRAPPER(recvmsg, libc_fd, msg, flags); }
-
-
-extern "C" ssize_t send(int libc_fd, const void *buf, ::size_t len, int flags) {
-	FD_FUNC_WRAPPER(send, libc_fd, buf, len, flags); }
-
-
-extern "C" ssize_t _sendto(int libc_fd, const void *buf, ::size_t len, int flags,
-                            const struct sockaddr *dest_addr, socklen_t addrlen) {
-	FD_FUNC_WRAPPER(sendto, libc_fd, buf, len, flags, dest_addr, addrlen); }
-
-
-extern "C" ssize_t sendto(int libc_fd, const void *buf, ::size_t len, int flags,
-                            const struct sockaddr *dest_addr, socklen_t addrlen)
+extern "C" ssize_t recvfrom(int libc_fd, void *buf, ::size_t len, int flags,
+                            sockaddr *src_addr, socklen_t *src_addrlen)
 {
-	return _sendto(libc_fd, buf, len, flags, dest_addr, addrlen);
+	return _recvfrom(libc_fd, buf, len, flags, src_addr, src_addrlen);
+}
+
+
+extern "C" ssize_t recv(int libc_fd, void *buf, ::size_t len, int flags)
+{
+	if (*Libc::config_socket())
+		return socket_fs_recv(libc_fd, buf, len, flags);
+
+	FD_FUNC_WRAPPER(recv, libc_fd, buf, len, flags);
+}
+
+
+extern "C" ssize_t _recvmsg(int libc_fd, msghdr *msg, int flags)
+{
+	if (*Libc::config_socket())
+		return socket_fs_recvmsg(libc_fd, msg, flags);
+
+	FD_FUNC_WRAPPER(recvmsg, libc_fd, msg, flags);
+}
+
+
+extern "C" ssize_t recvmsg(int libc_fd, msghdr *msg, int flags)
+{
+	return _recvmsg(libc_fd, msg, flags);
+}
+
+
+extern "C" ssize_t _sendto(int libc_fd, void const *buf, ::size_t len, int flags,
+                           sockaddr const *dest_addr, socklen_t dest_addrlen)
+{
+	if (*Libc::config_socket())
+		return socket_fs_sendto(libc_fd, buf, len, flags, dest_addr, dest_addrlen);
+
+	FD_FUNC_WRAPPER(sendto, libc_fd, buf, len, flags, dest_addr, dest_addrlen);
+}
+
+
+extern "C" ssize_t sendto(int libc_fd, void const *buf, ::size_t len, int flags,
+                          sockaddr const *dest_addr, socklen_t dest_addrlen)
+{
+	return _sendto(libc_fd, buf, len, flags, dest_addr, dest_addrlen);
+}
+
+
+extern "C" ssize_t send(int libc_fd, void const *buf, ::size_t len, int flags)
+{
+	if (*Libc::config_socket())
+		return socket_fs_send(libc_fd, buf, len, flags);
+
+	FD_FUNC_WRAPPER(send, libc_fd, buf, len, flags);
 }
 
 
 extern "C" int _getsockopt(int libc_fd, int level, int optname,
                           void *optval, socklen_t *optlen)
 {
-	return getsockopt(libc_fd, level, optname, optval, optlen);
+	if (*Libc::config_socket())
+		return socket_fs_getsockopt(libc_fd, level, optname, optval, optlen);
+
+	FD_FUNC_WRAPPER(getsockopt, libc_fd, level, optname, optval, optlen);
 }
 
 
 extern "C" int getsockopt(int libc_fd, int level, int optname,
-                          void *optval, socklen_t *optlen) {
-	FD_FUNC_WRAPPER(getsockopt, libc_fd, level, optname, optval, optlen); }
+                          void *optval, socklen_t *optlen)
+{
+	return _getsockopt(libc_fd, level, optname, optval, optlen);
+}
 
 
 extern "C" int _setsockopt(int libc_fd, int level, int optname,
-                          const void *optval, socklen_t optlen) {
-	FD_FUNC_WRAPPER(setsockopt, libc_fd, level, optname, optval, optlen); }
+                           void const *optval, socklen_t optlen)
+{
+	if (*Libc::config_socket())
+		return socket_fs_setsockopt(libc_fd, level, optname, optval, optlen);
+
+	FD_FUNC_WRAPPER(setsockopt, libc_fd, level, optname, optval, optlen);
+}
 
 
 extern "C" int setsockopt(int libc_fd, int level, int optname,
-                          const void *optval, socklen_t optlen)
+                          void const *optval, socklen_t optlen)
 {
 	return _setsockopt(libc_fd, level, optname, optval, optlen);
 }
 
 
-extern "C" int shutdown(int libc_fd, int how) {
-	FD_FUNC_WRAPPER(shutdown, libc_fd, how); }
-
-
-extern "C" int socket(int domain, int type, int protocol)
+extern "C" int _shutdown(int libc_fd, int how)
 {
+	if (*Libc::config_socket())
+		return socket_fs_shutdown(libc_fd, how);
+
+	FD_FUNC_WRAPPER(shutdown, libc_fd, how);
+}
+
+
+extern "C" int shutdown(int libc_fd, int how)
+{
+	return _shutdown(libc_fd, how);
+}
+
+
+extern "C" int _socket(int domain, int type, int protocol)
+{
+	if (*Libc::config_socket())
+		return socket_fs_socket(domain, type, protocol);
+
 	Plugin *plugin;
 	File_descriptor *new_fdo;
 
@@ -183,7 +271,7 @@ extern "C" int socket(int domain, int type, int protocol)
 }
 
 
-extern "C" int _socket(int domain, int type, int protocol)
+extern "C" int socket(int domain, int type, int protocol)
 {
-	return socket(domain, type, protocol);
+	return _socket(domain, type, protocol);
 }
diff --git a/repos/libports/src/lib/libc/vfs_plugin.cc b/repos/libports/src/lib/libc/vfs_plugin.cc
index b46fb6a04b..6aaf77853b 100644
--- a/repos/libports/src/lib/libc/vfs_plugin.cc
+++ b/repos/libports/src/lib/libc/vfs_plugin.cc
@@ -111,6 +111,13 @@ namespace Libc {
 		static Config_attr rtc("rtc", "");
 		return rtc.string();
 	}
+
+	char const *config_socket() __attribute__((weak));
+	char const *config_socket()
+	{
+		static Config_attr socket("socket", "");
+		return socket.string();
+	}
 }
 
 int Libc::Vfs_plugin::access(const char *path, int amode)
@@ -797,3 +804,17 @@ int Libc::Vfs_plugin::select(int nfds,
 	}
 	return nready;
 }
+
+namespace Libc {
+
+	bool read_ready(Libc::File_descriptor *fd)
+	{
+		Vfs::Vfs_handle *handle = vfs_handle(fd);
+		if (!handle) return false;
+
+		handle->fs().notify_read_ready(handle);
+
+		return handle->fs().read_ready(handle);
+	}
+
+}