Index: uspace/Makefile
===================================================================
--- uspace/Makefile	(revision 4c67e52b4043ea5cd4323c9bd8094bfde545d638)
+++ uspace/Makefile	(revision 899f1a95f22eca90d14c5209d80a487c9a0979f0)
@@ -64,4 +64,5 @@
 	app/nettest1 \
 	app/nettest2 \
+	app/nettest3 \
 	app/ping \
 	app/websrv \
Index: uspace/app/netecho/netecho.c
===================================================================
--- uspace/app/netecho/netecho.c	(revision 4c67e52b4043ea5cd4323c9bd8094bfde545d638)
+++ uspace/app/netecho/netecho.c	(revision 899f1a95f22eca90d14c5209d80a487c9a0979f0)
@@ -241,4 +241,6 @@
 		/* Accept a socket if a stream socket is used */
 		addrlen = sizeof(address_buf);
+		if (verbose)
+			printf("accept()\n");
             	socket_id = accept(listening_id, (void *) address_buf, &addrlen);
 		if (socket_id <= 0) {
@@ -258,4 +260,6 @@
 
 		/* Receive a message to echo */
+		if (verbose)
+			printf("recvfrom()\n");
 		rcv_size = recvfrom(socket_id, data, size, 0, address,
 		    &addrlen);
@@ -297,7 +301,17 @@
 
 			/* Answer the request either with the static reply or the original data */
-			rc = sendto(socket_id, reply ? reply : data, reply ? reply_length : length, 0, address, addrlen);
-			if (rc != EOK)
-				socket_print_error(stderr, rc, "Socket send: ", "\n");
+			if (type == SOCK_STREAM) {
+				if (verbose)
+					printf("send()\n");
+				rc = send(socket_id, reply ? reply : data, reply ? reply_length : length, 0);
+				if (rc != EOK)
+					socket_print_error(stderr, rc, "Socket send: ", "\n");
+			} else {
+				if (verbose)
+					printf("sendto()\n");
+				rc = sendto(socket_id, reply ? reply : data, reply ? reply_length : length, 0, address, addrlen);
+				if (rc != EOK)
+					socket_print_error(stderr, rc, "Socket send: ", "\n");
+			}
 		}
 
Index: uspace/app/nettest3/Makefile
===================================================================
--- uspace/app/nettest3/Makefile	(revision 899f1a95f22eca90d14c5209d80a487c9a0979f0)
+++ uspace/app/nettest3/Makefile	(revision 899f1a95f22eca90d14c5209d80a487c9a0979f0)
@@ -0,0 +1,37 @@
+#
+# Copyright (c) 2011 Jiri Svoboda
+# All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+#
+# - Redistributions of source code must retain the above copyright
+#   notice, this list of conditions and the following disclaimer.
+# - Redistributions in binary form must reproduce the above copyright
+#   notice, this list of conditions and the following disclaimer in the
+#   documentation and/or other materials provided with the distribution.
+# - The name of the author may not be used to endorse or promote products
+#   derived from this software without specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+# IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+# OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+# IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+# NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+# THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+#
+
+USPACE_PREFIX = ../..
+LIBS =
+EXTRA_CFLAGS =
+BINARY = nettest3
+
+SOURCES = \
+	nettest3.c
+
+include $(USPACE_PREFIX)/Makefile.common
Index: uspace/app/nettest3/nettest3.c
===================================================================
--- uspace/app/nettest3/nettest3.c	(revision 899f1a95f22eca90d14c5209d80a487c9a0979f0)
+++ uspace/app/nettest3/nettest3.c	(revision 899f1a95f22eca90d14c5209d80a487c9a0979f0)
@@ -0,0 +1,128 @@
+/*
+ * Copyright (c) 2011 Jiri Svoboda
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ *   notice, this list of conditions and the following disclaimer.
+ * - Redistributions in binary form must reproduce the above copyright
+ *   notice, this list of conditions and the following disclaimer in the
+ *   documentation and/or other materials provided with the distribution.
+ * - The name of the author may not be used to endorse or promote products
+ *   derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/** @addtogroup nettest
+ * @{
+ */
+
+/** @file
+ * Networking test 3.
+ */
+
+#include <async.h>
+#include <stdio.h>
+#include <str.h>
+
+#include <net/in.h>
+#include <net/in6.h>
+#include <net/inet.h>
+#include <net/socket.h>
+
+#define BUF_SIZE 32
+
+static char *data;
+static size_t size;
+
+static char buf[BUF_SIZE];
+
+static struct sockaddr_in addr;
+
+static uint16_t port;
+
+int main(int argc, char *argv[])
+{
+	int rc;
+	int fd;
+	char *endptr;
+
+	port = 7;
+
+	data = (char *)"Hello World!";
+	size = str_size(data);
+
+	/* Connect to local IP address by default */
+	addr.sin_family = AF_INET;
+	addr.sin_port = htons(port);
+	addr.sin_addr.s_addr = htonl(0x7f000001);
+
+	if (argc >= 2) {
+		printf("parsing address '%s'\n", argv[1]);
+		rc = inet_pton(AF_INET, argv[1], (uint8_t *)&addr.sin_addr.s_addr);
+		if (rc != EOK) {
+			fprintf(stderr, "Error parsing address\n");
+			return 1;
+		}
+		printf("result: rc=%d, family=%d, addr=%x\n", rc,
+		    addr.sin_family, addr.sin_addr.s_addr);
+	}
+
+	if (argc >= 3) {
+		printf("parsing port '%s'\n", argv[2]);
+		addr.sin_port = htons(strtoul(argv[2], &endptr, 10));
+		if (*endptr != '\0') {
+			fprintf(stderr, "Error parsing port\n");
+			return 1;
+		}
+	}
+
+	printf("socket()\n");
+	fd = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP);
+	printf(" -> %d\n", fd);
+	if (fd < 0)
+		return 1;
+
+	printf("connect()\n");
+	rc = connect(fd, (struct sockaddr *)&addr, sizeof(addr));
+	printf(" -> %d\n", rc);
+	if (rc != 0)
+		return 1;
+
+	printf("send()\n");
+	rc = send(fd, data, size, 0);
+	printf(" -> %d\n", rc);
+	if (rc < 0)
+		return 1;
+
+	do {
+		printf("recv()\n");
+		rc = recv(fd, buf, BUF_SIZE, 0);
+		printf(" -> %d\n", rc);
+	} while (rc > 0);
+
+	async_usleep(1000*1000);
+
+	printf("closesocket()\n");
+	rc = closesocket(fd);
+	printf(" -> %d\n", rc);
+
+	return 0;
+}
+
+
+/** @}
+ */
Index: uspace/app/websrv/websrv.c
===================================================================
--- uspace/app/websrv/websrv.c	(revision 4c67e52b4043ea5cd4323c9bd8094bfde545d638)
+++ uspace/app/websrv/websrv.c	(revision 899f1a95f22eca90d14c5209d80a487c9a0979f0)
@@ -1,4 +1,4 @@
 /*
- * Copyright (c) 2010 Jiri Svoboda
+ * Copyright (c) 2011 Jiri Svoboda
  * All rights reserved.
  *
@@ -31,8 +31,14 @@
  */
 /**
- * @file (Less-than-skeleton) web server.
+ * @file Skeletal web server.
  */
 
+#include <bool.h>
+#include <errno.h>
 #include <stdio.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <stdlib.h>
+#include <fcntl.h>
 
 #include <net/in.h>
@@ -44,13 +50,194 @@
 #define PORT_NUMBER 8080
 
+#define WEB_ROOT "/data/web"
+
 /** Buffer for receiving the request. */
 #define BUFFER_SIZE 1024
-static char buf[BUFFER_SIZE];
+static char rbuf[BUFFER_SIZE];
+static size_t rbuf_out, rbuf_in;
+
+static char lbuf[BUFFER_SIZE + 1];
+static size_t lbuf_used;
+
+static char fbuf[BUFFER_SIZE];
 
 /** Response to send to client. */
-static const char *response_msg =
+static const char *ok_msg =
     "HTTP/1.0 200 OK\r\n"
-    "\r\n"
-    "<h1>Hello from HelenOS!</h1>\r\n";
+    "\r\n";
+
+/** Receive one character (with buffering) */
+static int recv_char(int fd, char *c)
+{
+	ssize_t rc;
+
+	if (rbuf_out == rbuf_in) {
+		rbuf_out = 0;
+		rbuf_in = 0;
+
+		rc = recv(fd, rbuf, BUFFER_SIZE, 0);
+		if (rc <= 0) {
+			printf("recv() failed (%zd)\n", rc);
+			return rc;
+		}
+
+		rbuf_in = rc;
+	}
+
+	*c = rbuf[rbuf_out++];
+	return EOK;
+}
+
+/** Receive one line with length limit. */
+static int recv_line(int fd)
+{
+	char c, prev;
+	int rc;
+	char *bp;
+
+	bp = lbuf; c = '\0';
+	while (bp < lbuf + BUFFER_SIZE) {
+		prev = c;
+		rc = recv_char(fd, &c);
+		if (rc != EOK)
+			return rc;
+
+		*bp++ = c;
+		if (prev == '\r' && c == '\n')
+			break;
+	}
+
+	lbuf_used = bp - lbuf;
+	*bp = '\0';
+
+	if (bp == lbuf + BUFFER_SIZE)
+		return ELIMIT;
+
+	return EOK;
+}
+
+static bool uri_is_valid(char *uri)
+{
+	char *cp;
+	char c;
+
+	if (uri[0] != '/')
+		return false;
+	if (uri[1] == '.')
+		return false;
+
+	cp = uri + 1;
+	while (*cp != '\0') {
+		c = *cp++;
+		if (c == '/')
+			return false;
+	}
+
+	return true;
+}
+
+static int send_response(int conn_sd, const char *msg)
+{
+	size_t response_size;
+	ssize_t rc;
+
+	response_size = str_size(msg);
+
+	/* Send a canned response. */
+        printf("Send response...\n");
+	rc = send(conn_sd, (void *) msg, response_size, 0);
+	if (rc < 0) {
+		printf("send() failed.\n");
+		return rc;
+	}
+
+	return EOK;
+}
+
+static int uri_get(const char *uri, int conn_sd)
+{
+	int rc;
+	char *fname;
+	int fd;
+	ssize_t nr;
+
+	if (str_cmp(uri, "/") == 0)
+		uri = "/index.htm";
+
+	rc = asprintf(&fname, "%s%s", WEB_ROOT, uri);
+	if (rc < 0)
+		return ENOMEM;
+
+	fd = open(fname, O_RDONLY);
+	if (fd < 0) {
+		printf("File '%s' not found.\n", fname);
+		free(fname);
+		return ENOENT;
+	}
+
+	free(fname);
+
+	rc = send_response(conn_sd, ok_msg);
+	if (rc != EOK)
+		return rc;
+
+	while (true) {
+		nr = read(fd, fbuf, BUFFER_SIZE);
+		if (nr == 0)
+			break;
+
+		if (nr < 0) {
+			close(fd);
+			return EIO;
+		}
+
+		rc = send(conn_sd, fbuf, nr, 0);
+		if (rc < 0) {
+			printf("send() failed\n");
+			close(fd);
+			return rc;
+		}
+	}
+
+	close(fd);
+
+	return EOK;
+}
+
+static int req_process(int conn_sd)
+{
+	int rc;
+	char *uri, *end_uri;
+
+	rc = recv_line(conn_sd);
+	if (rc != EOK) {
+		printf("recv_line() failed\n");
+		return rc;
+	}
+
+	printf("%s", lbuf);
+
+	if (str_lcmp(lbuf, "GET ", 4) != 0) {
+		printf("Invalid HTTP method.\n");
+		return EINVAL;
+	}
+
+	uri = lbuf + 4;
+	end_uri = str_chr(uri, ' ');
+	if (end_uri == NULL) {
+		end_uri = lbuf + lbuf_used - 2;
+		assert(*end_uri == '\r');
+	}
+
+	*end_uri = '\0';
+	printf("Requested URI '%s'.\n", uri);
+
+	if (!uri_is_valid(uri)) {
+		printf("Invalid request URI.\n");
+		return EINVAL;
+	}
+
+	return uri_get(uri, conn_sd);
+}
 
 int main(int argc, char *argv[])
@@ -64,5 +251,4 @@
 	int rc;
 
-	size_t response_size;
 
 	addr.sin_family = AF_INET;
@@ -94,6 +280,4 @@
 		return 1;
 	}
-
-	response_size = str_size(response_msg);
 
 	printf("Listening for connections at port number %u.\n", PORT_NUMBER);
@@ -105,5 +289,5 @@
 		if (conn_sd < 0) {
 			printf("accept() failed.\n");
-			return 1;
+			continue;
 		}
 
@@ -111,19 +295,9 @@
 
 		printf("Wait for client request\n");
-
-		/* Really we should wait for a blank line. */
-		rc = recv(conn_sd, buf, BUFFER_SIZE, 0);
-		if (rc < 0) {
-			printf("recv() failed\n");
-			return 1;
-		}
-
-		/* Send a canned response. */
-                printf("Send response...\n");
-		rc = send(conn_sd, (void *) response_msg, response_size, 0);
-		if (rc < 0) {
-			printf("send() failed.\n");
-			return 1;
-		}
+		rbuf_out = rbuf_in = 0;
+
+		rc = req_process(conn_sd);
+		if (rc != EOK) 
+			printf("Error processing request.\n");
 
 		rc = closesocket(conn_sd);
Index: uspace/dist/data/web/bar.htm
===================================================================
--- uspace/dist/data/web/bar.htm	(revision 899f1a95f22eca90d14c5209d80a487c9a0979f0)
+++ uspace/dist/data/web/bar.htm	(revision 899f1a95f22eca90d14c5209d80a487c9a0979f0)
@@ -0,0 +1,10 @@
+<html>
+    <head>
+	<title>Bar!</title>
+    </head>
+    <body>
+	<h1>Bar!</h1>
+
+	<a href="/">Back to top</a>
+    </body>
+</html>
Index: uspace/dist/data/web/foo.htm
===================================================================
--- uspace/dist/data/web/foo.htm	(revision 899f1a95f22eca90d14c5209d80a487c9a0979f0)
+++ uspace/dist/data/web/foo.htm	(revision 899f1a95f22eca90d14c5209d80a487c9a0979f0)
@@ -0,0 +1,10 @@
+<html>
+    <head>
+	<title>Foo!</title>
+    </head>
+    <body>
+	<h1>Foo!</h1>
+
+	<a href="/">Back to top</a>
+    </body>
+</html>
Index: uspace/dist/data/web/index.htm
===================================================================
--- uspace/dist/data/web/index.htm	(revision 899f1a95f22eca90d14c5209d80a487c9a0979f0)
+++ uspace/dist/data/web/index.htm	(revision 899f1a95f22eca90d14c5209d80a487c9a0979f0)
@@ -0,0 +1,18 @@
+<html>
+    <head>
+        <title>
+    	    Hello from HelenOS!
+        </title>
+    </head>
+    <body>
+        <h1>Hello from HelenOS!</h1>
+        <p>
+            This web page is brought to you by courtesy of HelenOS web server
+    	    and TCP/IP stack.
+        </p>
+	<p>
+    	    Now <a href="foo.htm">go to page foo</a> or <a href="bar.htm">go
+    	    to bar</a>.
+        </p>
+    </body>
+</html>
Index: uspace/drv/bus/isa/isa.dev
===================================================================
--- uspace/drv/bus/isa/isa.dev	(revision 4c67e52b4043ea5cd4323c9bd8094bfde545d638)
+++ uspace/drv/bus/isa/isa.dev	(revision 899f1a95f22eca90d14c5209d80a487c9a0979f0)
@@ -13,3 +13,7 @@
 	irq 1
 	io_range 060 10
-	
+
+ne2k:
+	match 100 isa/ne2k
+	irq 5
+	io_range 300 20
Index: uspace/drv/nic/ne2k/dp8390.c
===================================================================
--- uspace/drv/nic/ne2k/dp8390.c	(revision 4c67e52b4043ea5cd4323c9bd8094bfde545d638)
+++ uspace/drv/nic/ne2k/dp8390.c	(revision 899f1a95f22eca90d14c5209d80a487c9a0979f0)
@@ -142,8 +142,9 @@
 static void ne2k_upload(ne2k_t *ne2k, void *buf, size_t addr, size_t size)
 {
+	size_t esize_ru = (size + 1) & ~1;
 	size_t esize = size & ~1;
 	
-	pio_write_8(ne2k->port + DP_RBCR0, esize & 0xff);
-	pio_write_8(ne2k->port + DP_RBCR1, (esize >> 8) & 0xff);
+	pio_write_8(ne2k->port + DP_RBCR0, esize_ru & 0xff);
+	pio_write_8(ne2k->port + DP_RBCR1, (esize_ru >> 8) & 0xff);
 	pio_write_8(ne2k->port + DP_RSAR0, addr & 0xff);
 	pio_write_8(ne2k->port + DP_RSAR1, (addr >> 8) & 0xff);
Index: uspace/lib/c/generic/fibril_synch.c
===================================================================
--- uspace/lib/c/generic/fibril_synch.c	(revision 4c67e52b4043ea5cd4323c9bd8094bfde545d638)
+++ uspace/lib/c/generic/fibril_synch.c	(revision 899f1a95f22eca90d14c5209d80a487c9a0979f0)
@@ -447,4 +447,135 @@
 }
 
+/** Timer fibril.
+ *
+ * @param arg	Timer
+ */
+static int fibril_timer_func(void *arg)
+{
+	fibril_timer_t *timer = (fibril_timer_t *) arg;
+	int rc;
+
+	fibril_mutex_lock(&timer->lock);
+
+	while (true) {
+		while (timer->state != fts_active &&
+		    timer->state != fts_cleanup) {
+
+			if (timer->state == fts_cleanup)
+				break;
+
+			fibril_condvar_wait(&timer->cv, &timer->lock);
+		}
+
+		if (timer->state == fts_cleanup)
+			break;
+
+		rc = fibril_condvar_wait_timeout(&timer->cv, &timer->lock,
+		    timer->delay);
+		if (rc == ETIMEOUT) {
+			timer->state = fts_fired;
+			fibril_mutex_unlock(&timer->lock);
+			timer->fun(timer->arg);
+			fibril_mutex_lock(&timer->lock);
+		}
+	}
+
+	fibril_mutex_unlock(&timer->lock);
+	return 0;
+}
+
+/** Create new timer.
+ *
+ * @return		New timer on success, @c NULL if out of memory.
+ */
+fibril_timer_t *fibril_timer_create(void)
+{
+	fid_t fid;
+	fibril_timer_t *timer;
+
+	timer = calloc(1, sizeof(fibril_timer_t));
+	if (timer == NULL)
+		return NULL;
+
+	fid = fibril_create(fibril_timer_func, (void *) timer);
+	if (fid == 0) {
+		free(timer);
+		return NULL;
+	}
+
+	fibril_mutex_initialize(&timer->lock);
+	fibril_condvar_initialize(&timer->cv);
+
+	timer->fibril = fid;
+	timer->state = fts_not_set;
+
+	fibril_add_ready(fid);
+
+	return timer;
+}
+
+/** Destroy timer.
+ *
+ * @param timer		Timer, must not be active or accessed by other threads.
+ */
+void fibril_timer_destroy(fibril_timer_t *timer)
+{
+	fibril_mutex_lock(&timer->lock);
+	assert(timer->state != fts_active);
+	timer->state = fts_cleanup;
+	fibril_condvar_broadcast(&timer->cv);
+	fibril_mutex_unlock(&timer->lock);
+}
+
+/** Set timer.
+ *
+ * Set timer to execute a callback function after the specified
+ * interval.
+ *
+ * @param timer		Timer
+ * @param delay		Delay in microseconds
+ * @param fun		Callback function
+ * @param arg		Argument for @a fun
+ */
+void fibril_timer_set(fibril_timer_t *timer, suseconds_t delay,
+    fibril_timer_fun_t fun, void *arg)
+{
+	fibril_mutex_lock(&timer->lock);
+	timer->state = fts_active;
+	timer->delay = delay;
+	timer->fun = fun;
+	timer->arg = arg;
+	fibril_condvar_broadcast(&timer->cv);
+	fibril_mutex_unlock(&timer->lock);
+}
+
+/** Clear timer.
+ *
+ * Clears (cancels) timer and returns last state of the timer.
+ * This can be one of:
+ *    - fts_not_set	If the timer has not been set or has been cleared
+ *    - fts_active	Timer was set but did not fire
+ *    - fts_fired	Timer fired
+ *
+ * @param timer		Timer
+ * @return		Last timer state
+ */
+fibril_timer_state_t fibril_timer_clear(fibril_timer_t *timer)
+{
+	fibril_timer_state_t old_state;
+
+	fibril_mutex_lock(&timer->lock);
+	old_state = timer->state;
+	timer->state = fts_not_set;
+
+	timer->delay = 0;
+	timer->fun = NULL;
+	timer->arg = NULL;
+	fibril_condvar_broadcast(&timer->cv);
+	fibril_mutex_unlock(&timer->lock);
+
+	return old_state;
+}
+
 /** @}
  */
Index: uspace/lib/c/include/bitops.h
===================================================================
--- uspace/lib/c/include/bitops.h	(revision 4c67e52b4043ea5cd4323c9bd8094bfde545d638)
+++ uspace/lib/c/include/bitops.h	(revision 899f1a95f22eca90d14c5209d80a487c9a0979f0)
@@ -40,9 +40,9 @@
 /** Mask with bit @a n set. */
 #define BIT_V(type, n) \
-    ((type)1 << ((n) - 1))
+    ((type)1 << (n))
 
 /** Mask with rightmost @a n bits set. */
 #define BIT_RRANGE(type, n) \
-    (BIT_V(type, (n) + 1) - 1)
+    (BIT_V(type, (n)) - 1)
 
 /** Mask with bits @a hi .. @a lo set. @a hi >= @a lo. */
Index: uspace/lib/c/include/errno.h
===================================================================
--- uspace/lib/c/include/errno.h	(revision 4c67e52b4043ea5cd4323c9bd8094bfde545d638)
+++ uspace/lib/c/include/errno.h	(revision 899f1a95f22eca90d14c5209d80a487c9a0979f0)
@@ -96,4 +96,8 @@
 #define ENOTCONN  (-10057)
 
+#define ECONNREFUSED  (-10058)
+
+#define ECONNABORTED  (-10059)
+
 /** The requested operation was not performed. Try again later. */
 #define EAGAIN  (-11002)
Index: uspace/lib/c/include/fibril_synch.h
===================================================================
--- uspace/lib/c/include/fibril_synch.h	(revision 4c67e52b4043ea5cd4323c9bd8094bfde545d638)
+++ uspace/lib/c/include/fibril_synch.h	(revision 899f1a95f22eca90d14c5209d80a487c9a0979f0)
@@ -107,4 +107,35 @@
 	fibril_condvar_t name = FIBRIL_CONDVAR_INITIALIZER(name)
 
+typedef void (*fibril_timer_fun_t)(void *);
+
+typedef enum {
+	/** Timer has not been set or has been cleared */
+	fts_not_set,
+	/** Timer was set but did not fire yet */
+	fts_active,
+	/** Timer has fired and has not been cleared since */
+	fts_fired,
+	/** Timer is being destroyed */
+	fts_cleanup
+} fibril_timer_state_t;
+
+/** Fibril timer.
+ *
+ * When a timer is set it executes a callback function (in a separate
+ * fibril) after a specified time interval. The timer can be cleared
+ * (canceled) before that. From the return value of fibril_timer_clear()
+ * one can tell whether the timer fired or not.
+ */
+typedef struct {
+	fibril_mutex_t lock;
+	fibril_condvar_t cv;
+	fid_t fibril;
+	fibril_timer_state_t state;
+
+	suseconds_t delay;
+	fibril_timer_fun_t fun;
+	void *arg;
+} fibril_timer_t;
+
 extern void fibril_mutex_initialize(fibril_mutex_t *);
 extern void fibril_mutex_lock(fibril_mutex_t *);
@@ -129,4 +160,10 @@
 extern void fibril_condvar_broadcast(fibril_condvar_t *);
 
+extern fibril_timer_t *fibril_timer_create(void);
+extern void fibril_timer_destroy(fibril_timer_t *);
+extern void fibril_timer_set(fibril_timer_t *, suseconds_t, fibril_timer_fun_t,
+    void *);
+extern fibril_timer_state_t fibril_timer_clear(fibril_timer_t *);
+
 #endif
 
Index: uspace/srv/fs/exfat/exfat_directory.c
===================================================================
--- uspace/srv/fs/exfat/exfat_directory.c	(revision 4c67e52b4043ea5cd4323c9bd8094bfde545d638)
+++ uspace/srv/fs/exfat/exfat_directory.c	(revision 899f1a95f22eca90d14c5209d80a487c9a0979f0)
@@ -92,6 +92,8 @@
 	int rc = EOK;
 	
-	if (di->b)
+	if (di->b) {
 		rc = block_put(di->b);
+		di->b = NULL;
+	}
 	
 	return rc;
@@ -285,6 +287,8 @@
 	for (i = 0; i < count; i++) {
 		rc = exfat_directory_get(di, &de);
-		if (rc != EOK)
-			return rc;
+		if (rc != EOK) {
+			free(array);
+			return rc;
+		}
 		array[i] = *de;
 		rc = exfat_directory_next(di);
@@ -312,6 +316,8 @@
 	for (i = 0; i < count; i++) {
 		rc = exfat_directory_get(di, &de);
-		if (rc != EOK)
-			return rc;
+		if (rc != EOK) {
+			free(array);
+			return rc;
+		}
 		*de = array[i];
 		di->b->dirty = true;
@@ -424,5 +430,4 @@
 
 		di->b->dirty = true;
-		sname += chars;
 	}
 	
Index: uspace/srv/net/tl/tcp/Makefile
===================================================================
--- uspace/srv/net/tl/tcp/Makefile	(revision 4c67e52b4043ea5cd4323c9bd8094bfde545d638)
+++ uspace/srv/net/tl/tcp/Makefile	(revision 899f1a95f22eca90d14c5209d80a487c9a0979f0)
@@ -1,5 +1,4 @@
 #
-# Copyright (c) 2005 Martin Decky
-# Copyright (c) 2007 Jakub Jermar
+# Copyright (c) 2011 Jiri Svoboda
 # All rights reserved.
 #
@@ -34,5 +33,16 @@
 
 SOURCES = \
-	tcp.c
+	conn.c \
+	iqueue.c \
+	ncsim.c \
+	pdu.c \
+	rqueue.c \
+	segment.c \
+	seq_no.c \
+	sock.c \
+	tcp.c \
+	test.c \
+	tqueue.c \
+	ucall.c
 
 include $(USPACE_PREFIX)/Makefile.common
Index: uspace/srv/net/tl/tcp/conn.c
===================================================================
--- uspace/srv/net/tl/tcp/conn.c	(revision 899f1a95f22eca90d14c5209d80a487c9a0979f0)
+++ uspace/srv/net/tl/tcp/conn.c	(revision 899f1a95f22eca90d14c5209d80a487c9a0979f0)
@@ -0,0 +1,1162 @@
+/*
+ * Copyright (c) 2011 Jiri Svoboda
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ *   notice, this list of conditions and the following disclaimer.
+ * - Redistributions in binary form must reproduce the above copyright
+ *   notice, this list of conditions and the following disclaimer in the
+ *   documentation and/or other materials provided with the distribution.
+ * - The name of the author may not be used to endorse or promote products
+ *   derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/** @addtogroup tcp
+ * @{
+ */
+
+/**
+ * @file TCP connection processing and state machine
+ */
+
+#include <adt/list.h>
+#include <bool.h>
+#include <errno.h>
+#include <io/log.h>
+#include <macros.h>
+#include <stdlib.h>
+#include "conn.h"
+#include "iqueue.h"
+#include "segment.h"
+#include "seq_no.h"
+#include "tcp_type.h"
+#include "tqueue.h"
+#include "ucall.h"
+
+#define RCV_BUF_SIZE 4096/*2*/
+#define SND_BUF_SIZE 4096
+
+#define MAX_SEGMENT_LIFETIME	(15*1000*1000) //(2*60*1000*1000)
+#define TIME_WAIT_TIMEOUT	(2*MAX_SEGMENT_LIFETIME)
+
+LIST_INITIALIZE(conn_list);
+
+static void tcp_conn_seg_process(tcp_conn_t *conn, tcp_segment_t *seg);
+static void tcp_conn_tw_timer_set(tcp_conn_t *conn);
+static void tcp_conn_tw_timer_clear(tcp_conn_t *conn);
+
+/** Create new segment structure.
+ *
+ * @param lsock		Local socket (will be deeply copied)
+ * @param fsock		Foreign socket (will be deeply copied)
+ * @return		New segment or NULL
+ */
+tcp_conn_t *tcp_conn_new(tcp_sock_t *lsock, tcp_sock_t *fsock)
+{
+	tcp_conn_t *conn = NULL;
+	bool tqueue_inited = false;
+
+	/* Allocate connection structure */
+	conn = calloc(1, sizeof(tcp_conn_t));
+	if (conn == NULL)
+		goto error;
+
+	conn->tw_timer = fibril_timer_create();
+	if (conn->tw_timer == NULL)
+		goto error;
+
+	/* Allocate receive buffer */
+	fibril_mutex_initialize(&conn->rcv_buf_lock);
+	fibril_condvar_initialize(&conn->rcv_buf_cv);
+	conn->rcv_buf_size = RCV_BUF_SIZE;
+	conn->rcv_buf_used = 0;
+	conn->rcv_buf_fin = false;
+
+	conn->rcv_buf = calloc(1, conn->rcv_buf_size);
+	if (conn->rcv_buf == NULL)
+		goto error;
+
+	/** Allocate send buffer */
+	conn->snd_buf_size = SND_BUF_SIZE;
+	conn->snd_buf_used = 0;
+	conn->snd_buf_fin = false;
+	conn->snd_buf = calloc(1, conn->snd_buf_size);
+	if (conn->snd_buf == NULL)
+		goto error;
+
+	/* Set up receive window. */
+	conn->rcv_wnd = conn->rcv_buf_size;
+
+	/* Initialize incoming segment queue */
+	tcp_iqueue_init(&conn->incoming, conn);
+
+	/* Initialize retransmission queue */
+	if (tcp_tqueue_init(&conn->retransmit, conn) != EOK)
+		goto error;
+
+	tqueue_inited = true;
+
+	/* Connection state change signalling */
+	fibril_mutex_initialize(&conn->cstate_lock);
+	fibril_condvar_initialize(&conn->cstate_cv);
+
+	conn->cstate = st_listen;
+	conn->reset = false;
+	conn->ap = ap_passive;
+	conn->fin_is_acked = false;
+	conn->ident.local = *lsock;
+	if (fsock != NULL)
+		conn->ident.foreign = *fsock;
+
+	return conn;
+
+error:
+	if (tqueue_inited)
+		tcp_tqueue_fini(&conn->retransmit);
+	if (conn != NULL && conn->rcv_buf != NULL)
+		free(conn->rcv_buf);
+	if (conn != NULL && conn->snd_buf != NULL)
+		free(conn->snd_buf);
+	if (conn != NULL && conn->tw_timer != NULL)
+		fibril_timer_destroy(conn->tw_timer);
+	if (conn != NULL)
+		free(conn);
+
+	return NULL;
+}
+
+/** Enlist connection.
+ *
+ * Add connection to the connection map.
+ */
+void tcp_conn_add(tcp_conn_t *conn)
+{
+	list_append(&conn->link, &conn_list);
+}
+
+/** Delist connection.
+ *
+ * Remove connection from the connection map.
+ */
+void tcp_conn_remove(tcp_conn_t *conn)
+{
+	list_remove(&conn->link);
+}
+
+static void tcp_conn_state_set(tcp_conn_t *conn, tcp_cstate_t nstate)
+{
+	fibril_mutex_lock(&conn->cstate_lock);
+	conn->cstate = nstate;
+	fibril_condvar_broadcast(&conn->cstate_cv);
+	fibril_mutex_unlock(&conn->cstate_lock);
+}
+
+/** Synchronize connection.
+ *
+ * This is the first step of an active connection attempt,
+ * sends out SYN and sets up ISS and SND.xxx.
+ */
+void tcp_conn_sync(tcp_conn_t *conn)
+{
+	/* XXX select ISS */
+	conn->iss = 1;
+	conn->snd_nxt = conn->iss;
+	conn->snd_una = conn->iss;
+	conn->ap = ap_active;
+
+	tcp_tqueue_ctrl_seg(conn, CTL_SYN);
+	tcp_conn_state_set(conn, st_syn_sent);
+}
+
+/** FIN has been sent.
+ *
+ * This function should be called when FIN is sent over the connection,
+ * as a result the connection state is changed appropriately.
+ */
+void tcp_conn_fin_sent(tcp_conn_t *conn)
+{
+	switch (conn->cstate) {
+	case st_syn_received:
+	case st_established:
+		log_msg(LVL_DEBUG, "%s: FIN sent -> Fin-Wait-1", conn->name);
+		tcp_conn_state_set(conn, st_fin_wait_1);
+		break;
+	case st_close_wait:
+		log_msg(LVL_DEBUG, "%s: FIN sent -> Last-Ack", conn->name);
+		tcp_conn_state_set(conn, st_last_ack);
+		break;
+	default:
+		log_msg(LVL_ERROR, "%s: Connection state %d", conn->name,
+		    conn->cstate);
+		assert(false);
+	}
+
+	conn->fin_is_acked = false;
+}
+
+/** Compare two sockets.
+ *
+ * Two sockets are equal if the address is equal and the port number
+ * is equal.
+ */
+static bool tcp_socket_match(tcp_sock_t *sock, tcp_sock_t *patt)
+{
+	log_msg(LVL_DEBUG, "tcp_socket_match(sock=(%x,%u), pat=(%x,%u))",
+	    sock->addr.ipv4, sock->port, patt->addr.ipv4, patt->port);
+
+	if (patt->addr.ipv4 != TCP_IPV4_ANY &&
+	    patt->addr.ipv4 != sock->addr.ipv4)
+		return false;
+
+	if (patt->port != TCP_PORT_ANY &&
+	    patt->port != sock->port)
+		return false;
+
+	log_msg(LVL_DEBUG, " -> match");
+
+	return true;
+}
+
+/** Match socket with pattern. */
+static bool tcp_sockpair_match(tcp_sockpair_t *sp, tcp_sockpair_t *pattern)
+{
+	log_msg(LVL_DEBUG, "tcp_sockpair_match(%p, %p)", sp, pattern);
+
+	if (!tcp_socket_match(&sp->local, &pattern->local))
+		return false;
+
+	if (!tcp_socket_match(&sp->foreign, &pattern->foreign))
+		return false;
+
+	return true;
+}
+
+/** Find connection structure for specified socket pair.
+ *
+ * A connection is uniquely identified by a socket pair. Look up our
+ * connection map and return connection structure based on socket pair.
+ *
+ * @param sp	Socket pair
+ * @return	Connection structure or NULL if not found.
+ */
+tcp_conn_t *tcp_conn_find(tcp_sockpair_t *sp)
+{
+	log_msg(LVL_DEBUG, "tcp_conn_find(%p)", sp);
+
+	list_foreach(conn_list, link) {
+		tcp_conn_t *conn = list_get_instance(link, tcp_conn_t, link);
+		tcp_sockpair_t *csp = &conn->ident;
+		log_msg(LVL_DEBUG, "compare with conn (f:(%x,%u), l:(%x,%u))",
+		    csp->foreign.addr.ipv4, csp->foreign.port,
+		    csp->local.addr.ipv4, csp->local.port);
+		if (tcp_sockpair_match(sp, csp)) {
+			return conn;
+		}
+	}
+
+	return NULL;
+}
+
+/** Reset connection.
+ *
+ * @param conn	Connection
+ */
+static void tcp_conn_reset(tcp_conn_t *conn)
+{
+	log_msg(LVL_DEBUG, "%s: tcp_conn_reset()", conn->name);
+	tcp_conn_state_set(conn, st_closed);
+	conn->reset = true;
+
+	tcp_conn_tw_timer_clear(conn);
+	tcp_tqueue_clear(&conn->retransmit);
+
+	fibril_condvar_broadcast(&conn->rcv_buf_cv);
+}
+
+/** Signal to the user that connection has been reset.
+ *
+ * Send an out-of-band signal to the user.
+ */
+static void tcp_reset_signal(tcp_conn_t *conn)
+{
+	/* TODO */
+	log_msg(LVL_DEBUG, "%s: tcp_reset_signal()", conn->name);
+}
+
+/** Determine if SYN has been received.
+ *
+ * @param conn	Connection
+ * @return	@c true if SYN has been received, @c false otherwise.
+ */
+bool tcp_conn_got_syn(tcp_conn_t *conn)
+{
+	switch (conn->cstate) {
+	case st_listen:
+	case st_syn_sent:
+		return false;
+	case st_syn_received:
+	case st_established:
+	case st_fin_wait_1:
+	case st_fin_wait_2:
+	case st_close_wait:
+	case st_closing:
+	case st_last_ack:
+	case st_time_wait:
+		return true;
+	case st_closed:
+		log_msg(LVL_WARN, "state=%d", (int) conn->cstate);
+		assert(false);
+	}
+
+	assert(false);
+}
+
+/** Segment arrived in Listen state.
+ *
+ * @param conn		Connection
+ * @param seg		Segment
+ */
+static void tcp_conn_sa_listen(tcp_conn_t *conn, tcp_segment_t *seg)
+{
+	log_msg(LVL_DEBUG, "tcp_conn_sa_listen(%p, %p)", conn, seg);
+
+	if ((seg->ctrl & CTL_RST) != 0) {
+		log_msg(LVL_DEBUG, "Ignoring incoming RST.");
+		return;
+	}
+
+	if ((seg->ctrl & CTL_ACK) != 0) {
+		log_msg(LVL_DEBUG, "Incoming ACK, send acceptable RST.");
+		tcp_reply_rst(&conn->ident, seg);
+		return;
+	}
+
+	if ((seg->ctrl & CTL_SYN) == 0) {
+		log_msg(LVL_DEBUG, "SYN not present. Ignoring segment.");
+		return;
+	}
+
+	log_msg(LVL_DEBUG, "Got SYN, sending SYN, ACK.");
+
+	conn->rcv_nxt = seg->seq + 1;
+	conn->irs = seg->seq;
+
+
+	log_msg(LVL_DEBUG, "rcv_nxt=%u", conn->rcv_nxt);
+
+	if (seg->len > 1)
+		log_msg(LVL_WARN, "SYN combined with data, ignoring data.");
+
+	/* XXX select ISS */
+	conn->iss = 1;
+	conn->snd_nxt = conn->iss;
+	conn->snd_una = conn->iss;
+
+	/*
+	 * Surprisingly the spec does not deal with initial window setting.
+	 * Set SND.WND = SEG.WND and set SND.WL1 so that next segment
+	 * will always be accepted as new window setting.
+	 */
+	conn->snd_wnd = seg->wnd;
+	conn->snd_wl1 = seg->seq;
+	conn->snd_wl2 = seg->seq;
+
+	tcp_conn_state_set(conn, st_syn_received);
+
+	tcp_tqueue_ctrl_seg(conn, CTL_SYN | CTL_ACK /* XXX */);
+
+	tcp_segment_delete(seg);
+}
+
+/** Segment arrived in Syn-Sent state.
+ *
+ * @param conn		Connection
+ * @param seg		Segment
+ */
+static void tcp_conn_sa_syn_sent(tcp_conn_t *conn, tcp_segment_t *seg)
+{
+	log_msg(LVL_DEBUG, "tcp_conn_sa_syn_sent(%p, %p)", conn, seg);
+
+	if ((seg->ctrl & CTL_ACK) != 0) {
+		log_msg(LVL_DEBUG, "snd_una=%u, seg.ack=%u, snd_nxt=%u",
+		    conn->snd_una, seg->ack, conn->snd_nxt);
+		if (!seq_no_ack_acceptable(conn, seg->ack)) {
+			log_msg(LVL_WARN, "ACK not acceptable, send RST.");
+			tcp_reply_rst(&conn->ident, seg);
+			return;
+		}
+	}
+
+	if ((seg->ctrl & CTL_RST) != 0) {
+		log_msg(LVL_DEBUG, "%s: Connection reset. -> Closed",
+		    conn->name);
+		/* Reset connection */
+		tcp_conn_reset(conn);
+		/* XXX delete connection */
+		return;
+	}
+
+	/* XXX precedence */
+
+	if ((seg->ctrl & CTL_SYN) == 0) {
+		log_msg(LVL_DEBUG, "No SYN bit, ignoring segment.");
+		return;
+	}
+
+	conn->rcv_nxt = seg->seq + 1;
+	conn->irs = seg->seq;
+
+	if ((seg->ctrl & CTL_ACK) != 0) {
+		conn->snd_una = seg->ack;
+
+		/*
+		 * Prune acked segments from retransmission queue and
+		 * possibly transmit more data.
+		 */
+		tcp_tqueue_ack_received(conn);
+	}
+
+	log_msg(LVL_DEBUG, "Sent SYN, got SYN.");
+
+	/*
+	 * Surprisingly the spec does not deal with initial window setting.
+	 * Set SND.WND = SEG.WND and set SND.WL1 so that next segment
+	 * will always be accepted as new window setting.
+	 */
+	log_msg(LVL_DEBUG, "SND.WND := %" PRIu32 ", SND.WL1 := %" PRIu32 ", "
+	    "SND.WL2 = %" PRIu32, seg->wnd, seg->seq, seg->seq);
+	conn->snd_wnd = seg->wnd;
+	conn->snd_wl1 = seg->seq;
+	conn->snd_wl2 = seg->seq;
+
+	if (seq_no_syn_acked(conn)) {
+		log_msg(LVL_DEBUG, "%s: syn acked -> Established", conn->name);
+		tcp_conn_state_set(conn, st_established);
+		tcp_tqueue_ctrl_seg(conn, CTL_ACK /* XXX */);
+	} else {
+		log_msg(LVL_DEBUG, "%s: syn not acked -> Syn-Received",
+		    conn->name);
+		tcp_conn_state_set(conn, st_syn_received);
+		tcp_tqueue_ctrl_seg(conn, CTL_SYN | CTL_ACK /* XXX */);
+	}
+
+	tcp_segment_delete(seg);
+}
+
+/** Segment arrived in state where segments are processed in sequence order.
+ *
+ * Queue segment in incoming segments queue for processing.
+ *
+ * @param conn		Connection
+ * @param seg		Segment
+ */
+static void tcp_conn_sa_queue(tcp_conn_t *conn, tcp_segment_t *seg)
+{
+	tcp_segment_t *pseg;
+
+	log_msg(LVL_DEBUG, "tcp_conn_sa_seq(%p, %p)", conn, seg);
+
+	/* Discard unacceptable segments ("old duplicates") */
+	if (!seq_no_segment_acceptable(conn, seg)) {
+		log_msg(LVL_DEBUG, "Replying ACK to unacceptable segment.");
+		tcp_tqueue_ctrl_seg(conn, CTL_ACK);
+		tcp_segment_delete(seg);
+		return;
+	}
+
+	/* Queue for processing */
+	tcp_iqueue_insert_seg(&conn->incoming, seg);
+
+	/*
+	 * Process all segments from incoming queue that are ready.
+	 * Unacceptable segments are discarded by tcp_iqueue_get_ready_seg().
+	 *
+	 * XXX Need to return ACK for unacceptable segments
+	 */
+	while (tcp_iqueue_get_ready_seg(&conn->incoming, &pseg) == EOK)
+		tcp_conn_seg_process(conn, pseg);
+}
+
+/** Process segment RST field.
+ *
+ * @param conn		Connection
+ * @param seg		Segment
+ * @return		cp_done if we are done with this segment, cp_continue
+ *			if not
+ */
+static cproc_t tcp_conn_seg_proc_rst(tcp_conn_t *conn, tcp_segment_t *seg)
+{
+	if ((seg->ctrl & CTL_RST) == 0)
+		return cp_continue;
+
+	switch (conn->cstate) {
+	case st_syn_received:
+		/* XXX In case of passive open, revert to Listen state */
+		if (conn->ap == ap_passive) {
+			tcp_conn_state_set(conn, st_listen);
+			/* XXX Revert conn->ident */
+			tcp_conn_tw_timer_clear(conn);
+			tcp_tqueue_clear(&conn->retransmit);
+		} else {
+			tcp_conn_reset(conn);
+		}
+		break;
+	case st_established:
+	case st_fin_wait_1:
+	case st_fin_wait_2:
+	case st_close_wait:
+		/* General "connection reset" signal */
+		tcp_reset_signal(conn);
+		tcp_conn_reset(conn);
+		break;
+	case st_closing:
+	case st_last_ack:
+	case st_time_wait:
+		tcp_conn_reset(conn);
+		break;
+	case st_listen:
+	case st_syn_sent:
+	case st_closed:
+		assert(false);
+	}
+
+	return cp_done;
+}
+
+/** Process segment security and precedence fields.
+ *
+ * @param conn		Connection
+ * @param seg		Segment
+ * @return		cp_done if we are done with this segment, cp_continue
+ *			if not
+ */
+static cproc_t tcp_conn_seg_proc_sp(tcp_conn_t *conn, tcp_segment_t *seg)
+{
+	/* TODO */
+	return cp_continue;
+}
+
+/** Process segment SYN field.
+ *
+ * @param conn		Connection
+ * @param seg		Segment
+ * @return		cp_done if we are done with this segment, cp_continue
+ *			if not
+ */
+static cproc_t tcp_conn_seg_proc_syn(tcp_conn_t *conn, tcp_segment_t *seg)
+{
+	if ((seg->ctrl & CTL_SYN) == 0)
+		return cp_continue;
+
+	/*
+	 * Assert SYN is in receive window, otherwise this step should not
+	 * be reached.
+	 */
+	assert(seq_no_in_rcv_wnd(conn, seg->seq));
+
+	log_msg(LVL_WARN, "SYN is in receive window, should send reset. XXX");
+
+	/*
+	 * TODO
+	 *
+	 * Send a reset, resond "reset" to all outstanding RECEIVEs and SEND,
+	 * flush segment queues. Send unsolicited "connection reset" signal
+	 * to user, connection -> closed state, delete TCB, return.
+	 */
+	return cp_done;
+}
+
+/** Process segment ACK field in Syn-Received state.
+ *
+ * @param conn		Connection
+ * @param seg		Segment
+ * @return		cp_done if we are done with this segment, cp_continue
+ *			if not
+ */
+static cproc_t tcp_conn_seg_proc_ack_sr(tcp_conn_t *conn, tcp_segment_t *seg)
+{
+	if (!seq_no_ack_acceptable(conn, seg->ack)) {
+		/* ACK is not acceptable, send RST. */
+		log_msg(LVL_WARN, "Segment ACK not acceptable, sending RST.");
+		tcp_reply_rst(&conn->ident, seg);
+		tcp_segment_delete(seg);
+		return cp_done;
+	}
+
+	log_msg(LVL_DEBUG, "%s: SYN ACKed -> Established", conn->name);
+
+	tcp_conn_state_set(conn, st_established);
+
+	/* XXX Not mentioned in spec?! */
+	conn->snd_una = seg->ack;
+
+	return cp_continue;
+}
+
+/** Process segment ACK field in Established state.
+ *
+ * @param conn		Connection
+ * @param seg		Segment
+ * @return		cp_done if we are done with this segment, cp_continue
+ *			if not
+ */
+static cproc_t tcp_conn_seg_proc_ack_est(tcp_conn_t *conn, tcp_segment_t *seg)
+{
+	log_msg(LVL_DEBUG, "tcp_conn_seg_proc_ack_est(%p, %p)", conn, seg);
+
+	log_msg(LVL_DEBUG, "SEG.ACK=%u, SND.UNA=%u, SND.NXT=%u",
+	    (unsigned)seg->ack, (unsigned)conn->snd_una,
+	    (unsigned)conn->snd_nxt);
+
+	if (!seq_no_ack_acceptable(conn, seg->ack)) {
+		log_msg(LVL_DEBUG, "ACK not acceptable.");
+		if (!seq_no_ack_duplicate(conn, seg->ack)) {
+			log_msg(LVL_WARN, "Not acceptable, not duplicate. "
+			    "Send ACK and drop.");
+			/* Not acceptable, not duplicate. Send ACK and drop. */
+			tcp_tqueue_ctrl_seg(conn, CTL_ACK);
+			tcp_segment_delete(seg);
+			return cp_done;
+		} else {
+			log_msg(LVL_DEBUG, "Ignoring duplicate ACK.");
+		}
+	} else {
+		/* Update SND.UNA */
+		conn->snd_una = seg->ack;
+	}
+
+	if (seq_no_new_wnd_update(conn, seg)) {
+		conn->snd_wnd = seg->wnd;
+		conn->snd_wl1 = seg->seq;
+		conn->snd_wl2 = seg->ack;
+
+		log_msg(LVL_DEBUG, "Updating send window, SND.WND=%" PRIu32
+		    ", SND.WL1=%" PRIu32 ", SND.WL2=%" PRIu32,
+		    conn->snd_wnd, conn->snd_wl1, conn->snd_wl2);
+	}
+
+	/*
+	 * Prune acked segments from retransmission queue and
+	 * possibly transmit more data.
+	 */
+	tcp_tqueue_ack_received(conn);
+
+	return cp_continue;
+}
+
+/** Process segment ACK field in Fin-Wait-1 state.
+ *
+ * @param conn		Connection
+ * @param seg		Segment
+ * @return		cp_done if we are done with this segment, cp_continue
+ *			if not
+ */
+static cproc_t tcp_conn_seg_proc_ack_fw1(tcp_conn_t *conn, tcp_segment_t *seg)
+{
+	if (tcp_conn_seg_proc_ack_est(conn, seg) == cp_done)
+		return cp_done;
+
+	if (conn->fin_is_acked) {
+		log_msg(LVL_DEBUG, "%s: FIN acked -> Fin-Wait-2", conn->name);
+		tcp_conn_state_set(conn, st_fin_wait_2);
+	}
+
+	return cp_continue;
+}
+
+/** Process segment ACK field in Fin-Wait-2 state.
+ *
+ * @param conn		Connection
+ * @param seg		Segment
+ * @return		cp_done if we are done with this segment, cp_continue
+ *			if not
+ */
+static cproc_t tcp_conn_seg_proc_ack_fw2(tcp_conn_t *conn, tcp_segment_t *seg)
+{
+	if (tcp_conn_seg_proc_ack_est(conn, seg) == cp_done)
+		return cp_done;
+
+	/* TODO */
+	return cp_continue;
+}
+
+/** Process segment ACK field in Close-Wait state.
+ *
+ * @param conn		Connection
+ * @param seg		Segment
+ * @return		cp_done if we are done with this segment, cp_continue
+ *			if not
+ */
+static cproc_t tcp_conn_seg_proc_ack_cw(tcp_conn_t *conn, tcp_segment_t *seg)
+{
+	/* The same processing as in Established state */
+	return tcp_conn_seg_proc_ack_est(conn, seg);
+}
+
+/** Process segment ACK field in Closing state.
+ *
+ * @param conn		Connection
+ * @param seg		Segment
+ * @return		cp_done if we are done with this segment, cp_continue
+ *			if not
+ */
+static cproc_t tcp_conn_seg_proc_ack_cls(tcp_conn_t *conn, tcp_segment_t *seg)
+{
+	if (tcp_conn_seg_proc_ack_est(conn, seg) == cp_done)
+		return cp_done;
+
+	/* TODO */
+	return cp_continue;
+}
+
+/** Process segment ACK field in Last-Ack state.
+ *
+ * @param conn		Connection
+ * @param seg		Segment
+ * @return		cp_done if we are done with this segment, cp_continue
+ *			if not
+ */
+static cproc_t tcp_conn_seg_proc_ack_la(tcp_conn_t *conn, tcp_segment_t *seg)
+{
+	if (tcp_conn_seg_proc_ack_est(conn, seg) == cp_done)
+		return cp_done;
+
+	if (conn->fin_is_acked) {
+		log_msg(LVL_DEBUG, "%s: FIN acked -> Closed", conn->name);
+		tcp_conn_remove(conn);
+		tcp_conn_state_set(conn, st_closed);
+		return cp_done;
+	}
+
+	return cp_continue;
+}
+
+/** Process segment ACK field in Time-Wait state.
+ *
+ * @param conn		Connection
+ * @param seg		Segment
+ * @return		cp_done if we are done with this segment, cp_continue
+ *			if not
+ */
+static cproc_t tcp_conn_seg_proc_ack_tw(tcp_conn_t *conn, tcp_segment_t *seg)
+{
+	/* Nothing to do */
+	return cp_continue;
+}
+
+/** Process segment ACK field.
+ *
+ * @param conn		Connection
+ * @param seg		Segment
+ * @return		cp_done if we are done with this segment, cp_continue
+ *			if not
+ */
+static cproc_t tcp_conn_seg_proc_ack(tcp_conn_t *conn, tcp_segment_t *seg)
+{
+	log_msg(LVL_DEBUG, "%s: tcp_conn_seg_proc_ack(%p, %p)",
+	    conn->name, conn, seg);
+
+	if ((seg->ctrl & CTL_ACK) == 0) {
+		log_msg(LVL_WARN, "Segment has no ACK. Dropping.");
+		tcp_segment_delete(seg);
+		return cp_done;
+	}
+
+	switch (conn->cstate) {
+	case st_syn_received:
+		return tcp_conn_seg_proc_ack_sr(conn, seg);
+	case st_established:
+		return tcp_conn_seg_proc_ack_est(conn, seg);
+	case st_fin_wait_1:
+		return tcp_conn_seg_proc_ack_fw1(conn, seg);
+	case st_fin_wait_2:
+		return tcp_conn_seg_proc_ack_fw2(conn, seg);
+	case st_close_wait:
+		return tcp_conn_seg_proc_ack_cw(conn, seg);
+	case st_closing:
+		return tcp_conn_seg_proc_ack_cls(conn, seg);
+	case st_last_ack:
+		return tcp_conn_seg_proc_ack_la(conn, seg);
+	case st_time_wait:
+		return tcp_conn_seg_proc_ack_tw(conn, seg);
+	case st_listen:
+	case st_syn_sent:
+	case st_closed:
+		assert(false);
+	}
+
+	assert(false);
+}
+
+/** Process segment URG field.
+ *
+ * @param conn		Connection
+ * @param seg		Segment
+ * @return		cp_done if we are done with this segment, cp_continue
+ *			if not
+ */
+static cproc_t tcp_conn_seg_proc_urg(tcp_conn_t *conn, tcp_segment_t *seg)
+{
+	return cp_continue;
+}
+
+/** Process segment text.
+ *
+ * @param conn		Connection
+ * @param seg		Segment
+ * @return		cp_done if we are done with this segment, cp_continue
+ *			if not
+ */
+static cproc_t tcp_conn_seg_proc_text(tcp_conn_t *conn, tcp_segment_t *seg)
+{
+	size_t text_size;
+	size_t xfer_size;
+
+	log_msg(LVL_DEBUG, "%s: tcp_conn_seg_proc_text(%p, %p)",
+	    conn->name, conn, seg);
+
+	switch (conn->cstate) {
+	case st_established:
+	case st_fin_wait_1:
+	case st_fin_wait_2:
+		/* OK */
+		break;
+	case st_close_wait:
+	case st_closing:
+	case st_last_ack:
+	case st_time_wait:
+		/* Invalid since FIN has been received. Ignore text. */
+		return cp_continue;
+	case st_listen:
+	case st_syn_sent:
+	case st_syn_received:
+	case st_closed:
+		assert(false);
+	}
+
+	/*
+	 * Process segment text
+	 */
+	assert(seq_no_segment_ready(conn, seg));
+
+	/* Trim anything outside our receive window */
+	tcp_conn_trim_seg_to_wnd(conn, seg);
+
+	fibril_mutex_lock(&conn->rcv_buf_lock);
+
+	/* Determine how many bytes to copy */
+	text_size = tcp_segment_text_size(seg);
+	xfer_size = min(text_size, conn->rcv_buf_size - conn->rcv_buf_used);
+
+	/* Copy data to receive buffer */
+	tcp_segment_text_copy(seg, conn->rcv_buf + conn->rcv_buf_used,
+	    xfer_size);
+	conn->rcv_buf_used += xfer_size;
+
+	/* Signal to the receive function that new data has arrived */
+	fibril_condvar_broadcast(&conn->rcv_buf_cv);
+	fibril_mutex_unlock(&conn->rcv_buf_lock);
+
+	log_msg(LVL_DEBUG, "Received %zu bytes of data.", xfer_size);
+
+	/* Advance RCV.NXT */
+	conn->rcv_nxt += xfer_size;
+
+	/* Update receive window. XXX Not an efficient strategy. */
+	conn->rcv_wnd -= xfer_size;
+
+	/* Send ACK */
+	if (xfer_size > 0)
+		tcp_tqueue_ctrl_seg(conn, CTL_ACK);
+
+	if (xfer_size < seg->len) {
+		/* Trim part of segment which we just received */
+		tcp_conn_trim_seg_to_wnd(conn, seg);
+	} else {
+		log_msg(LVL_DEBUG, "%s: Nothing left in segment, dropping "
+		    "(xfer_size=%zu, SEG.LEN=%zu, seg->ctrl=%u)",
+		    conn->name, xfer_size, seg->len, (unsigned)seg->ctrl);
+		/* Nothing left in segment */
+		tcp_segment_delete(seg);
+		return cp_done;
+	}
+
+	return cp_continue;
+}
+
+/** Process segment FIN field.
+ *
+ * @param conn		Connection
+ * @param seg		Segment
+ * @return		cp_done if we are done with this segment, cp_continue
+ *			if not
+ */
+static cproc_t tcp_conn_seg_proc_fin(tcp_conn_t *conn, tcp_segment_t *seg)
+{
+	log_msg(LVL_DEBUG, "%s: tcp_conn_seg_proc_fin(%p, %p)",
+	    conn->name, conn, seg);
+	log_msg(LVL_DEBUG, " seg->len=%zu, seg->ctl=%u", (size_t) seg->len,
+	    (unsigned) seg->ctrl);
+
+	/* Only process FIN if no text is left in segment. */
+	if (tcp_segment_text_size(seg) == 0 && (seg->ctrl & CTL_FIN) != 0) {
+		log_msg(LVL_DEBUG, " - FIN found in segment.");
+
+		/* Send ACK */
+		tcp_tqueue_ctrl_seg(conn, CTL_ACK);
+
+		conn->rcv_nxt++;
+		conn->rcv_wnd--;
+
+		/* Change connection state */
+		switch (conn->cstate) {
+		case st_listen:
+		case st_syn_sent:
+		case st_closed:
+			/* Connection not synchronized */
+			assert(false);
+		case st_syn_received:
+		case st_established:
+			log_msg(LVL_DEBUG, "%s: FIN received -> Close-Wait",
+			    conn->name);
+			tcp_conn_state_set(conn, st_close_wait);
+			break;
+		case st_fin_wait_1:
+			log_msg(LVL_DEBUG, "%s: FIN received -> Closing",
+			    conn->name);
+			tcp_conn_state_set(conn, st_closing);
+			break;
+		case st_fin_wait_2:
+			log_msg(LVL_DEBUG, "%s: FIN received -> Time-Wait",
+			    conn->name);
+			tcp_conn_state_set(conn, st_time_wait);
+			/* Start the Time-Wait timer */
+			tcp_conn_tw_timer_set(conn);
+			break;
+		case st_close_wait:
+		case st_closing:
+		case st_last_ack:
+			/* Do nothing */
+			break;
+		case st_time_wait:
+			/* Restart the Time-Wait timer */
+			tcp_conn_tw_timer_set(conn);
+			break;
+		}
+
+		/* Add FIN to the receive buffer */
+		fibril_mutex_lock(&conn->rcv_buf_lock);
+		conn->rcv_buf_fin = true;
+		fibril_condvar_broadcast(&conn->rcv_buf_cv);
+		fibril_mutex_unlock(&conn->rcv_buf_lock);
+
+		tcp_segment_delete(seg);
+		return cp_done;
+	}
+
+	return cp_continue;
+}
+
+/** Process incoming segment.
+ *
+ * We are in connection state where segments are processed in order
+ * of sequence number. This processes one segment taken from the
+ * connection incoming segments queue.
+ *
+ * @param conn		Connection
+ * @param seg		Segment
+ */
+static void tcp_conn_seg_process(tcp_conn_t *conn, tcp_segment_t *seg)
+{
+	log_msg(LVL_DEBUG, "tcp_conn_seg_process(%p, %p)", conn, seg);
+	tcp_segment_dump(seg);
+
+	/* Check whether segment is acceptable */
+	/* XXX Permit valid ACKs, URGs and RSTs */
+/*	if (!seq_no_segment_acceptable(conn, seg)) {
+		log_msg(LVL_WARN, "Segment not acceptable, dropping.");
+		if ((seg->ctrl & CTL_RST) == 0) {
+			tcp_tqueue_ctrl_seg(conn, CTL_ACK);
+		}
+		return;
+	}
+*/
+
+	if (tcp_conn_seg_proc_rst(conn, seg) == cp_done)
+		return;
+
+	if (tcp_conn_seg_proc_sp(conn, seg) == cp_done)
+		return;
+
+	if (tcp_conn_seg_proc_syn(conn, seg) == cp_done)
+		return;
+
+	if (tcp_conn_seg_proc_ack(conn, seg) == cp_done)
+		return;
+
+	if (tcp_conn_seg_proc_urg(conn, seg) == cp_done)
+		return;
+
+	if (tcp_conn_seg_proc_text(conn, seg) == cp_done)
+		return;
+
+	if (tcp_conn_seg_proc_fin(conn, seg) == cp_done)
+		return;
+
+	/*
+	 * If anything is left from the segment, insert it back into the
+	 * incoming segments queue.
+	 */
+	if (seg->len > 0) {
+		log_msg(LVL_DEBUG, "Re-insert segment %p. seg->len=%zu",
+		    seg, (size_t) seg->len);
+		tcp_iqueue_insert_seg(&conn->incoming, seg);
+	} else {
+		tcp_segment_delete(seg);
+	}
+}
+
+/** Segment arrived on a connection.
+ *
+ * @param conn		Connection
+ * @param seg		Segment
+ */
+void tcp_conn_segment_arrived(tcp_conn_t *conn, tcp_segment_t *seg)
+{
+	log_msg(LVL_DEBUG, "%c: tcp_conn_segment_arrived(%p)",
+	    conn->name, seg);
+
+	switch (conn->cstate) {
+	case st_listen:
+		tcp_conn_sa_listen(conn, seg); break;
+	case st_syn_sent:
+		tcp_conn_sa_syn_sent(conn, seg); break;
+	case st_syn_received:
+	case st_established:
+	case st_fin_wait_1:
+	case st_fin_wait_2:
+	case st_close_wait:
+	case st_closing:
+	case st_last_ack:
+	case st_time_wait:
+		/* Process segments in order of sequence number */
+		tcp_conn_sa_queue(conn, seg); break;
+	case st_closed:
+		log_msg(LVL_DEBUG, "state=%d", (int) conn->cstate);
+		assert(false);
+	}
+}
+
+/** Time-Wait timeout handler.
+ *
+ * @param arg	Connection
+ */
+static void tw_timeout_func(void *arg)
+{
+	tcp_conn_t *conn = (tcp_conn_t *) arg;
+
+	log_msg(LVL_DEBUG, "tw_timeout_func(%p)", conn);
+
+	if (conn->cstate == st_closed) {
+		log_msg(LVL_DEBUG, "Connection already closed.");
+		return;
+	}
+
+	log_msg(LVL_DEBUG, "%s: TW Timeout -> Closed", conn->name);
+	tcp_conn_remove(conn);
+	tcp_conn_state_set(conn, st_closed);
+}
+
+/** Start or restart the Time-Wait timeout.
+ *
+ * @param conn		Connection
+ */
+void tcp_conn_tw_timer_set(tcp_conn_t *conn)
+{
+	fibril_timer_set(conn->tw_timer, TIME_WAIT_TIMEOUT, tw_timeout_func,
+	    (void *)conn);
+}
+
+/** Clear the Time-Wait timeout.
+ *
+ * @param conn		Connection
+ */
+void tcp_conn_tw_timer_clear(tcp_conn_t *conn)
+{
+	fibril_timer_clear(conn->tw_timer);
+}
+
+/** Trim segment to the receive window.
+ *
+ * @param conn		Connection
+ * @param seg		Segment
+ */
+void tcp_conn_trim_seg_to_wnd(tcp_conn_t *conn, tcp_segment_t *seg)
+{
+	uint32_t left, right;
+
+	seq_no_seg_trim_calc(conn, seg, &left, &right);
+	tcp_segment_trim(seg, left, right);
+}
+
+/** Handle unexpected segment received on a socket pair.
+ *
+ * We reply with an RST unless the received segment has RST.
+ *
+ * @param sp		Socket pair which received the segment
+ * @param seg		Unexpected segment
+ */
+void tcp_unexpected_segment(tcp_sockpair_t *sp, tcp_segment_t *seg)
+{
+	log_msg(LVL_DEBUG, "tcp_unexpected_segment(%p, %p)", sp, seg);
+
+	if ((seg->ctrl & CTL_RST) == 0)
+		tcp_reply_rst(sp, seg);
+}
+
+/** Compute flipped socket pair for response.
+ *
+ * Flipped socket pair has local and foreign sockets exchanged.
+ *
+ * @param sp		Socket pair
+ * @param fsp		Place to store flipped socket pair
+ */
+void tcp_sockpair_flipped(tcp_sockpair_t *sp, tcp_sockpair_t *fsp)
+{
+	fsp->local = sp->foreign;
+	fsp->foreign = sp->local;
+}
+
+/** Send RST in response to an incoming segment.
+ *
+ * @param sp		Socket pair which received the segment
+ * @param seg		Incoming segment
+ */
+void tcp_reply_rst(tcp_sockpair_t *sp, tcp_segment_t *seg)
+{
+	tcp_segment_t *rseg;
+
+	log_msg(LVL_DEBUG, "tcp_reply_rst(%p, %p)", sp, seg);
+
+	rseg = tcp_segment_make_rst(seg);
+	tcp_transmit_segment(sp, rseg);
+}
+
+/**
+ * @}
+ */
Index: uspace/srv/net/tl/tcp/conn.h
===================================================================
--- uspace/srv/net/tl/tcp/conn.h	(revision 899f1a95f22eca90d14c5209d80a487c9a0979f0)
+++ uspace/srv/net/tl/tcp/conn.h	(revision 899f1a95f22eca90d14c5209d80a487c9a0979f0)
@@ -0,0 +1,58 @@
+/*
+ * Copyright (c) 2011 Jiri Svoboda
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ *   notice, this list of conditions and the following disclaimer.
+ * - Redistributions in binary form must reproduce the above copyright
+ *   notice, this list of conditions and the following disclaimer in the
+ *   documentation and/or other materials provided with the distribution.
+ * - The name of the author may not be used to endorse or promote products
+ *   derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/** @addtogroup tcp
+ * @{
+ */
+/** @file TCP connection processing and state machine
+ */
+
+#ifndef CONN_H
+#define CONN_H
+
+#include <bool.h>
+#include "tcp_type.h"
+
+extern tcp_conn_t *tcp_conn_new(tcp_sock_t *, tcp_sock_t *);
+extern void tcp_conn_add(tcp_conn_t *);
+extern void tcp_conn_remove(tcp_conn_t *);
+extern void tcp_conn_sync(tcp_conn_t *);
+extern void tcp_conn_fin_sent(tcp_conn_t *);
+extern void tcp_conn_ack_of_fin_rcvd(tcp_conn_t *);
+extern tcp_conn_t *tcp_conn_find(tcp_sockpair_t *);
+extern bool tcp_conn_got_syn(tcp_conn_t *);
+extern void tcp_conn_segment_arrived(tcp_conn_t *, tcp_segment_t *);
+extern void tcp_conn_trim_seg_to_wnd(tcp_conn_t *, tcp_segment_t *);
+extern void tcp_unexpected_segment(tcp_sockpair_t *, tcp_segment_t *);
+extern void tcp_sockpair_flipped(tcp_sockpair_t *, tcp_sockpair_t *);
+extern void tcp_reply_rst(tcp_sockpair_t *, tcp_segment_t *);
+
+#endif
+
+/** @}
+ */
Index: uspace/srv/net/tl/tcp/iqueue.c
===================================================================
--- uspace/srv/net/tl/tcp/iqueue.c	(revision 899f1a95f22eca90d14c5209d80a487c9a0979f0)
+++ uspace/srv/net/tl/tcp/iqueue.c	(revision 899f1a95f22eca90d14c5209d80a487c9a0979f0)
@@ -0,0 +1,156 @@
+/*
+ * Copyright (c) 2011 Jiri Svoboda
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ *   notice, this list of conditions and the following disclaimer.
+ * - Redistributions in binary form must reproduce the above copyright
+ *   notice, this list of conditions and the following disclaimer in the
+ *   documentation and/or other materials provided with the distribution.
+ * - The name of the author may not be used to endorse or promote products
+ *   derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/** @addtogroup tcp
+ * @{
+ */
+
+/**
+ * @file Connection incoming segments queue
+ *
+ * Segments are sorted in order of their sequence number.
+ */
+
+#include <adt/list.h>
+#include <errno.h>
+#include <io/log.h>
+#include <stdlib.h>
+#include "iqueue.h"
+#include "segment.h"
+#include "seq_no.h"
+#include "tcp_type.h"
+
+/** Initialize incoming segments queue.
+ *
+ * @param iqueue	Incoming queue
+ * @param conn		Connection the queue is associated with
+ */
+void tcp_iqueue_init(tcp_iqueue_t *iqueue, tcp_conn_t *conn)
+{
+	list_initialize(&iqueue->list);
+	iqueue->conn = conn;
+}
+
+/** Insert segment into incoming queue.
+ *
+ * @param iqueue	Incoming queue
+ * @param seg		Segment
+ */
+void tcp_iqueue_insert_seg(tcp_iqueue_t *iqueue, tcp_segment_t *seg)
+{
+	tcp_iqueue_entry_t *iqe;
+	tcp_iqueue_entry_t *qe;
+	link_t *link;
+	log_msg(LVL_DEBUG, "tcp_iqueue_insert_seg()");
+
+	iqe = calloc(1, sizeof(tcp_iqueue_entry_t));
+	if (iqe == NULL) {
+		log_msg(LVL_ERROR, "Failed allocating IQE.");
+		return;
+	}
+
+	iqe->seg = seg;
+
+	/* Sort by sequence number */
+
+	link = list_first(&iqueue->list);
+	while (link != NULL) {
+		qe = list_get_instance(link,
+		    tcp_iqueue_entry_t, link);
+
+		if (seq_no_seg_cmp(iqueue->conn, iqe->seg, qe->seg) >= 0)
+			break;
+	}
+
+	if (link != NULL)
+		list_insert_before(&iqe->link, &qe->link);
+	else
+		list_append(&iqe->link, &iqueue->list);
+}
+
+/** Get next ready segment from incoming queue.
+ *
+ * Return the segment with the earliest sequence number if it is ready.
+ * A segment is ready if its SEG.SEQ is earlier or equal to RCV.NXT.
+ *
+ * @param iqueue	Incoming queue
+ * @param seg		Place to store pointer to segment
+ * @return		EOK on success, ENOENT if no segment is ready
+ */
+int tcp_iqueue_get_ready_seg(tcp_iqueue_t *iqueue, tcp_segment_t **seg)
+{
+	tcp_iqueue_entry_t *iqe;
+	link_t *link;
+
+	log_msg(LVL_DEBUG, "tcp_get_ready_seg()");
+
+	link = list_first(&iqueue->list);
+	if (link == NULL) {
+		log_msg(LVL_DEBUG, "iqueue is empty");
+		return ENOENT;
+	}
+
+	iqe = list_get_instance(link, tcp_iqueue_entry_t, link);
+
+	while (!seq_no_segment_acceptable(iqueue->conn, iqe->seg)) {
+		log_msg(LVL_DEBUG, "Skipping unacceptable segment (RCV.NXT=%"
+		    PRIu32 ", RCV.NXT+RCV.WND=%" PRIu32 ", SEG.SEQ=%" PRIu32
+		    ", SEG.LEN=%" PRIu32 ")", iqueue->conn->rcv_nxt,
+		    iqueue->conn->rcv_nxt + iqueue->conn->rcv_wnd,
+		    iqe->seg->seq, iqe->seg->len);
+
+		list_remove(&iqe->link);
+		tcp_segment_delete(iqe->seg);
+
+         	link = list_first(&iqueue->list);
+		if (link == NULL) {
+			log_msg(LVL_DEBUG, "iqueue is empty");
+			return ENOENT;
+		}
+
+		iqe = list_get_instance(link, tcp_iqueue_entry_t, link);
+	}
+
+	/* Do not return segments that are not ready for processing */
+	if (!seq_no_segment_ready(iqueue->conn, iqe->seg)) {
+		log_msg(LVL_DEBUG, "Next segment not ready: SEG.SEQ=%u, "
+		    "RCV.NXT=%u, SEG.LEN=%u", iqe->seg->seq,
+		    iqueue->conn->rcv_nxt, iqe->seg->len);
+		return ENOENT;
+	}
+
+	log_msg(LVL_DEBUG, "Returning ready segment %p", iqe->seg);
+	list_remove(&iqe->link);
+	*seg = iqe->seg;
+
+	return EOK;
+}
+
+/**
+ * @}
+ */
Index: uspace/srv/net/tl/tcp/iqueue.h
===================================================================
--- uspace/srv/net/tl/tcp/iqueue.h	(revision 899f1a95f22eca90d14c5209d80a487c9a0979f0)
+++ uspace/srv/net/tl/tcp/iqueue.h	(revision 899f1a95f22eca90d14c5209d80a487c9a0979f0)
@@ -0,0 +1,47 @@
+/*
+ * Copyright (c) 2011 Jiri Svoboda
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ *   notice, this list of conditions and the following disclaimer.
+ * - Redistributions in binary form must reproduce the above copyright
+ *   notice, this list of conditions and the following disclaimer in the
+ *   documentation and/or other materials provided with the distribution.
+ * - The name of the author may not be used to endorse or promote products
+ *   derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/** @addtogroup tcp
+ * @{
+ */
+/** @file Connection incoming segments queue
+ */
+
+#ifndef IQUEUE_H
+#define IQUEUE_H
+
+#include "tcp_type.h"
+
+extern void tcp_iqueue_init(tcp_iqueue_t *, tcp_conn_t *);
+extern void tcp_iqueue_insert_seg(tcp_iqueue_t *, tcp_segment_t *);
+extern int tcp_iqueue_get_ready_seg(tcp_iqueue_t *, tcp_segment_t **);
+
+#endif
+
+/** @}
+ */
Index: uspace/srv/net/tl/tcp/ncsim.c
===================================================================
--- uspace/srv/net/tl/tcp/ncsim.c	(revision 899f1a95f22eca90d14c5209d80a487c9a0979f0)
+++ uspace/srv/net/tl/tcp/ncsim.c	(revision 899f1a95f22eca90d14c5209d80a487c9a0979f0)
@@ -0,0 +1,172 @@
+/*
+ * Copyright (c) 2011 Jiri Svoboda
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ *   notice, this list of conditions and the following disclaimer.
+ * - Redistributions in binary form must reproduce the above copyright
+ *   notice, this list of conditions and the following disclaimer in the
+ *   documentation and/or other materials provided with the distribution.
+ * - The name of the author may not be used to endorse or promote products
+ *   derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/** @addtogroup tcp
+ * @{
+ */
+
+/**
+ * @file Network condition simulator
+ *
+ * Simulate network conditions for testing the reliability implementation:
+ *    - variable latency
+ *    - frame drop
+ */
+
+#include <adt/list.h>
+#include <async.h>
+#include <errno.h>
+#include <io/log.h>
+#include <stdlib.h>
+#include <thread.h>
+#include "conn.h"
+#include "ncsim.h"
+#include "rqueue.h"
+#include "segment.h"
+#include "tcp_type.h"
+
+static list_t sim_queue;
+static fibril_mutex_t sim_queue_lock;
+static fibril_condvar_t sim_queue_cv;
+
+/** Initialize segment receive queue. */
+void tcp_ncsim_init(void)
+{
+	list_initialize(&sim_queue);
+	fibril_mutex_initialize(&sim_queue_lock);
+	fibril_condvar_initialize(&sim_queue_cv);
+}
+
+/** Bounce segment through simulator into receive queue.
+ *
+ * @param sp	Socket pair, oriented for transmission
+ * @param seg	Segment
+ */
+void tcp_ncsim_bounce_seg(tcp_sockpair_t *sp, tcp_segment_t *seg)
+{
+	tcp_squeue_entry_t *sqe;
+	tcp_squeue_entry_t *old_qe;
+	link_t *link;
+
+	log_msg(LVL_DEBUG, "tcp_ncsim_bounce_seg()");
+	tcp_rqueue_bounce_seg(sp, seg);
+	return;
+
+	if (0 /*random() % 4 == 3*/) {
+		/* Drop segment */
+		log_msg(LVL_ERROR, "NCSim dropping segment");
+		tcp_segment_delete(seg);
+		return;
+	}
+
+	sqe = calloc(1, sizeof(tcp_squeue_entry_t));
+	if (sqe == NULL) {
+		log_msg(LVL_ERROR, "Failed allocating SQE.");
+		return;
+	}
+
+	sqe->delay = random() % (1000 * 1000);
+	sqe->sp = *sp;
+	sqe->seg = seg;
+
+	fibril_mutex_lock(&sim_queue_lock);
+
+	link = list_first(&sim_queue);
+	while (link != NULL && sqe->delay > 0) {
+		old_qe = list_get_instance(link, tcp_squeue_entry_t, link);
+		if (sqe->delay < old_qe->delay)
+			break;
+
+		sqe->delay -= old_qe->delay;
+
+		link = link->next;
+		if (link == &sim_queue.head)
+			link = NULL;
+	}
+
+	if (link != NULL)
+		list_insert_after(&sqe->link, link);
+	else
+		list_append(&sqe->link, &sim_queue);
+
+	fibril_condvar_broadcast(&sim_queue_cv);
+	fibril_mutex_unlock(&sim_queue_lock);
+}
+
+/** Network condition simulator handler thread. */
+static void tcp_ncsim_thread(void *arg)
+{
+	link_t *link;
+	tcp_squeue_entry_t *sqe;
+	int rc;
+
+	log_msg(LVL_DEBUG, "tcp_ncsim_thread()");
+
+
+	while (true) {
+		fibril_mutex_lock(&sim_queue_lock);
+
+		while (list_empty(&sim_queue))
+			fibril_condvar_wait(&sim_queue_cv, &sim_queue_lock);
+
+		do {
+			link = list_first(&sim_queue);
+			sqe = list_get_instance(link, tcp_squeue_entry_t, link);
+
+			log_msg(LVL_DEBUG, "NCSim - Sleep");
+			rc = fibril_condvar_wait_timeout(&sim_queue_cv,
+			    &sim_queue_lock, sqe->delay);
+		} while (rc != ETIMEOUT);
+
+		list_remove(link);
+		fibril_mutex_unlock(&sim_queue_lock);
+
+		log_msg(LVL_DEBUG, "NCSim - End Sleep");
+		tcp_rqueue_bounce_seg(&sqe->sp, sqe->seg);
+		free(sqe);
+	}
+}
+
+/** Start simulator handler thread. */
+void tcp_ncsim_thread_start(void)
+{
+	thread_id_t tid;
+        int rc;
+
+	log_msg(LVL_DEBUG, "tcp_ncsim_thread_start()");
+
+	rc = thread_create(tcp_ncsim_thread, NULL, "ncsim", &tid);
+	if (rc != EOK) {
+		log_msg(LVL_ERROR, "Failed creating ncsim thread.");
+		return;
+	}
+}
+
+/**
+ * @}
+ */
Index: uspace/srv/net/tl/tcp/ncsim.h
===================================================================
--- uspace/srv/net/tl/tcp/ncsim.h	(revision 899f1a95f22eca90d14c5209d80a487c9a0979f0)
+++ uspace/srv/net/tl/tcp/ncsim.h	(revision 899f1a95f22eca90d14c5209d80a487c9a0979f0)
@@ -0,0 +1,48 @@
+/*
+ * Copyright (c) 2011 Jiri Svoboda
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ *   notice, this list of conditions and the following disclaimer.
+ * - Redistributions in binary form must reproduce the above copyright
+ *   notice, this list of conditions and the following disclaimer in the
+ *   documentation and/or other materials provided with the distribution.
+ * - The name of the author may not be used to endorse or promote products
+ *   derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/** @addtogroup tcp
+ * @{
+ */
+/** @file Network condition simulator
+ */
+
+#ifndef NCSIM_H
+#define NCSIM_H
+
+#include "tcp_type.h"
+
+extern void tcp_ncsim_init(void);
+extern void tcp_ncsim_bounce_seg(tcp_sockpair_t *, tcp_segment_t *);
+extern void tcp_ncsim_thread_start(void);
+
+
+#endif
+
+/** @}
+ */
Index: uspace/srv/net/tl/tcp/pdu.c
===================================================================
--- uspace/srv/net/tl/tcp/pdu.c	(revision 899f1a95f22eca90d14c5209d80a487c9a0979f0)
+++ uspace/srv/net/tl/tcp/pdu.c	(revision 899f1a95f22eca90d14c5209d80a487c9a0979f0)
@@ -0,0 +1,314 @@
+/*
+ * Copyright (c) 2011 Jiri Svoboda
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ *   notice, this list of conditions and the following disclaimer.
+ * - Redistributions in binary form must reproduce the above copyright
+ *   notice, this list of conditions and the following disclaimer in the
+ *   documentation and/or other materials provided with the distribution.
+ * - The name of the author may not be used to endorse or promote products
+ *   derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/** @addtogroup tcp
+ * @{
+ */
+
+/**
+ * @file TCP header encoding and decoding
+ */
+
+#include <bitops.h>
+#include <byteorder.h>
+#include <errno.h>
+#include <mem.h>
+#include <stdlib.h>
+#include "pdu.h"
+#include "segment.h"
+#include "seq_no.h"
+#include "std.h"
+#include "tcp_type.h"
+
+#define TCP_CHECKSUM_INIT 0xffff
+
+/** One's complement addition.
+ *
+ * Result is a + b + carry.
+ */
+static uint16_t tcp_ocadd16(uint16_t a, uint16_t b)
+{
+	uint32_t s;
+
+	s = (uint32_t)a + (uint32_t)b;
+	return (s & 0xffff) + (s >> 16);
+}
+
+static uint16_t tcp_checksum_calc(uint16_t ivalue, void *data, size_t size)
+{
+	uint16_t sum;
+	uint16_t w;
+	size_t words, i;
+	uint8_t *bdata;
+
+	sum = ~ivalue;
+	words = size / 2;
+	bdata = (uint8_t *)data;
+
+	for (i = 0; i < words; i++) {
+		w = ((uint16_t)bdata[2*i] << 8) | bdata[2*i + 1];
+		sum = tcp_ocadd16(sum, w);
+	}
+
+	if (size % 2 != 0) {
+		w = ((uint16_t)bdata[2*words] << 8);
+		sum = tcp_ocadd16(sum, w);
+	}
+
+	return ~sum;
+}
+
+static void tcp_header_decode_flags(uint16_t doff_flags, tcp_control_t *rctl)
+{
+	tcp_control_t ctl;
+
+	ctl = 0;
+
+	if ((doff_flags & BIT_V(uint16_t, DF_URG)) != 0)
+		ctl |= 0 /* XXX */;
+	if ((doff_flags & BIT_V(uint16_t, DF_ACK)) != 0)
+		ctl |= CTL_ACK;
+	if ((doff_flags & BIT_V(uint16_t, DF_PSH)) != 0)
+		ctl |= 0 /* XXX */;
+	if ((doff_flags & BIT_V(uint16_t, DF_RST)) != 0)
+		ctl |= CTL_RST;
+	if ((doff_flags & BIT_V(uint16_t, DF_SYN)) != 0)
+		ctl |= CTL_SYN;
+	if ((doff_flags & BIT_V(uint16_t, DF_FIN)) != 0)
+		ctl |= CTL_FIN;
+
+	*rctl = ctl;
+}
+
+static void tcp_header_encode_flags(tcp_control_t ctl, uint16_t doff_flags0,
+    uint16_t *rdoff_flags)
+{
+	uint16_t doff_flags;
+
+	doff_flags = doff_flags0;
+
+	if ((ctl & CTL_ACK) != 0)
+		doff_flags |= BIT_V(uint16_t, DF_ACK);
+	if ((ctl & CTL_RST) != 0)
+		doff_flags |= BIT_V(uint16_t, DF_RST);
+	if ((ctl & CTL_SYN) != 0)
+		doff_flags |= BIT_V(uint16_t, DF_SYN);
+	if ((ctl & CTL_FIN) != 0)
+		doff_flags |= BIT_V(uint16_t, DF_FIN);
+
+	*rdoff_flags = doff_flags;
+}
+
+static void tcp_header_setup(tcp_sockpair_t *sp, tcp_segment_t *seg, tcp_header_t *hdr)
+{
+	uint16_t doff_flags;
+	uint16_t doff;
+
+	hdr->src_port = host2uint16_t_be(sp->local.port);
+	hdr->dest_port = host2uint16_t_be(sp->foreign.port);
+	hdr->seq = host2uint32_t_be(seg->seq);
+	hdr->ack = host2uint32_t_be(seg->ack);
+
+	doff = (sizeof(tcp_header_t) / sizeof(uint32_t)) << DF_DATA_OFFSET_l;
+	tcp_header_encode_flags(seg->ctrl, doff, &doff_flags);
+
+	hdr->doff_flags = host2uint16_t_be(doff_flags);
+	hdr->window = host2uint16_t_be(seg->wnd);
+	hdr->checksum = 0;
+	hdr->urg_ptr = host2uint16_t_be(seg->up);
+}
+
+static void tcp_phdr_setup(tcp_pdu_t *pdu, tcp_phdr_t *phdr)
+{
+	phdr->src_addr = host2uint32_t_be(pdu->src_addr.ipv4);
+	phdr->dest_addr = host2uint32_t_be(pdu->dest_addr.ipv4);
+	phdr->zero = 0;
+	phdr->protocol = 6; /* XXX Magic number */
+	phdr->tcp_length = host2uint16_t_be(pdu->header_size + pdu->text_size);
+}
+
+static void tcp_header_decode(tcp_header_t *hdr, tcp_segment_t *seg)
+{
+	tcp_header_decode_flags(uint16_t_be2host(hdr->doff_flags), &seg->ctrl);
+	seg->seq = uint32_t_be2host(hdr->seq);
+	seg->ack = uint32_t_be2host(hdr->ack);
+	seg->wnd = uint16_t_be2host(hdr->window);
+	seg->up = uint16_t_be2host(hdr->urg_ptr);
+}
+
+static int tcp_header_encode(tcp_sockpair_t *sp, tcp_segment_t *seg,
+    void **header, size_t *size)
+{
+	tcp_header_t *hdr;
+
+	hdr = calloc(1, sizeof(tcp_header_t));
+	if (hdr == NULL)
+		return ENOMEM;
+
+	tcp_header_setup(sp, seg, hdr);
+	*header = hdr;
+	*size = sizeof(tcp_header_t);
+
+	return EOK;
+}
+
+static tcp_pdu_t *tcp_pdu_new(void)
+{
+	return calloc(1, sizeof(tcp_pdu_t));
+}
+
+/** Create PDU with the specified header and text data.
+ *
+ * Note that you still need to set addresses in the returned PDU.
+ *
+ * @param hdr		Header data
+ * @param hdr_size      Header size in bytes
+ * @param text		Text data
+ * @param text_size	Text size in bytes
+ * @return		New PDU
+ */
+tcp_pdu_t *tcp_pdu_create(void *hdr, size_t hdr_size, void *text,
+    size_t text_size)
+{
+	tcp_pdu_t *pdu;
+
+	pdu = tcp_pdu_new();
+	if (pdu == NULL)
+		return NULL;
+
+	pdu->header = malloc(hdr_size);
+	pdu->text = malloc(text_size);
+	if (pdu->header == NULL || pdu->text == NULL)
+		goto error;
+
+	memcpy(pdu->header, hdr, hdr_size);
+	memcpy(pdu->text, text, text_size);
+
+	pdu->header_size = hdr_size;
+	pdu->text_size = text_size;
+
+	return pdu;
+
+error:
+	if (pdu->header != NULL)
+		free(pdu->header);
+	if (pdu->text != NULL)
+		free(pdu->text);
+
+	return NULL;
+}
+
+void tcp_pdu_delete(tcp_pdu_t *pdu)
+{
+	free(pdu->header);
+	free(pdu->text);
+	free(pdu);
+}
+
+static uint16_t tcp_pdu_checksum_calc(tcp_pdu_t *pdu)
+{
+	uint16_t cs_phdr;
+	uint16_t cs_headers;
+	uint16_t cs_all;
+	tcp_phdr_t phdr;
+
+	tcp_phdr_setup(pdu, &phdr);
+	cs_phdr = tcp_checksum_calc(TCP_CHECKSUM_INIT, (void *)&phdr,
+	    sizeof(tcp_phdr_t));
+	cs_headers = tcp_checksum_calc(cs_phdr, pdu->header, pdu->header_size);
+	cs_all = tcp_checksum_calc(cs_headers, pdu->text, pdu->text_size);
+
+	return cs_all;
+}
+
+static void tcp_pdu_set_checksum(tcp_pdu_t *pdu, uint16_t checksum)
+{
+	tcp_header_t *hdr;
+
+	hdr = (tcp_header_t *)pdu->header;
+	hdr->checksum = host2uint16_t_be(checksum);
+}
+
+/** Encode outgoing PDU */
+int tcp_pdu_decode(tcp_pdu_t *pdu, tcp_sockpair_t *sp, tcp_segment_t **seg)
+{
+	tcp_segment_t *nseg;
+	tcp_header_t *hdr;
+
+	nseg = tcp_segment_make_data(0, pdu->text, pdu->text_size);
+	if (nseg == NULL)
+		return ENOMEM;
+
+	tcp_header_decode(pdu->header, nseg);
+	nseg->len += seq_no_control_len(nseg->ctrl);
+
+	hdr = (tcp_header_t *)pdu->header;
+
+	sp->local.port = uint16_t_be2host(hdr->dest_port);
+	sp->local.addr = pdu->dest_addr;
+	sp->foreign.port = uint16_t_be2host(hdr->src_port);
+	sp->foreign.addr = pdu->src_addr;
+
+	*seg = nseg;
+	return EOK;
+}
+
+/** Decode incoming PDU */
+int tcp_pdu_encode(tcp_sockpair_t *sp, tcp_segment_t *seg, tcp_pdu_t **pdu)
+{
+	tcp_pdu_t *npdu;
+	size_t text_size;
+	uint16_t checksum;
+
+	npdu = tcp_pdu_new();
+	if (npdu == NULL)
+		return ENOMEM;
+
+	npdu->src_addr = sp->local.addr;
+	npdu->dest_addr = sp->foreign.addr;
+	tcp_header_encode(sp, seg, &npdu->header, &npdu->header_size);
+
+	text_size = tcp_segment_text_size(seg);
+	npdu->text = calloc(1, text_size);
+	if (npdu->text == NULL)
+		return ENOMEM;
+
+	npdu->text_size = text_size;
+	memcpy(npdu->text, seg->data, text_size);
+
+	/* Checksum calculation */
+	checksum = tcp_pdu_checksum_calc(npdu);
+	tcp_pdu_set_checksum(npdu, checksum);
+
+	*pdu = npdu;
+	return EOK;
+}
+
+/**
+ * @}
+ */
Index: uspace/srv/net/tl/tcp/pdu.h
===================================================================
--- uspace/srv/net/tl/tcp/pdu.h	(revision 899f1a95f22eca90d14c5209d80a487c9a0979f0)
+++ uspace/srv/net/tl/tcp/pdu.h	(revision 899f1a95f22eca90d14c5209d80a487c9a0979f0)
@@ -0,0 +1,50 @@
+/*
+ * Copyright (c) 2011 Jiri Svoboda
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ *   notice, this list of conditions and the following disclaimer.
+ * - Redistributions in binary form must reproduce the above copyright
+ *   notice, this list of conditions and the following disclaimer in the
+ *   documentation and/or other materials provided with the distribution.
+ * - The name of the author may not be used to endorse or promote products
+ *   derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/** @addtogroup tcp
+ * @{
+ */
+/** @file TCP PDU (encoded Protocol Data Unit) handling
+ */
+
+#ifndef PDU_H
+#define PDU_H
+
+#include <sys/types.h>
+#include "std.h"
+#include "tcp_type.h"
+
+extern tcp_pdu_t *tcp_pdu_create(void *, size_t, void *, size_t);
+extern void tcp_pdu_delete(tcp_pdu_t *);
+extern int tcp_pdu_decode(tcp_pdu_t *, tcp_sockpair_t *, tcp_segment_t **);
+extern int tcp_pdu_encode(tcp_sockpair_t *, tcp_segment_t *, tcp_pdu_t **);
+
+#endif
+
+/** @}
+ */
Index: uspace/srv/net/tl/tcp/rqueue.c
===================================================================
--- uspace/srv/net/tl/tcp/rqueue.c	(revision 899f1a95f22eca90d14c5209d80a487c9a0979f0)
+++ uspace/srv/net/tl/tcp/rqueue.c	(revision 899f1a95f22eca90d14c5209d80a487c9a0979f0)
@@ -0,0 +1,163 @@
+/*
+ * Copyright (c) 2011 Jiri Svoboda
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ *   notice, this list of conditions and the following disclaimer.
+ * - Redistributions in binary form must reproduce the above copyright
+ *   notice, this list of conditions and the following disclaimer in the
+ *   documentation and/or other materials provided with the distribution.
+ * - The name of the author may not be used to endorse or promote products
+ *   derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/** @addtogroup tcp
+ * @{
+ */
+
+/**
+ * @file Global segment receive queue
+ */
+
+#include <adt/prodcons.h>
+#include <errno.h>
+#include <io/log.h>
+#include <stdlib.h>
+#include <thread.h>
+#include "conn.h"
+#include "pdu.h"
+#include "rqueue.h"
+#include "segment.h"
+#include "tcp_type.h"
+#include "ucall.h"
+
+/** Transcode bounced segments.
+ *
+ * If defined, segments bounced via the internal debugging loopback will
+ * be encoded to a PDU and the decoded. Otherwise they will be bounced back
+ * directly without passing the encoder-decoder.
+ */
+#define BOUNCE_TRANSCODE
+
+static prodcons_t rqueue;
+
+/** Initialize segment receive queue. */
+void tcp_rqueue_init(void)
+{
+	prodcons_initialize(&rqueue);
+}
+
+/** Bounce segment directy into receive queue without constructing the PDU.
+ *
+ * This is for testing purposes only.
+ *
+ * @param sp	Socket pair, oriented for transmission
+ * @param seg	Segment
+ */
+void tcp_rqueue_bounce_seg(tcp_sockpair_t *sp, tcp_segment_t *seg)
+{
+	tcp_sockpair_t rident;
+
+	log_msg(LVL_DEBUG, "tcp_rqueue_bounce_seg()");
+
+#ifdef BOUNCE_TRANSCODE
+	tcp_pdu_t *pdu;
+	tcp_segment_t *dseg;
+
+	if (tcp_pdu_encode(sp, seg, &pdu) != EOK) {
+		log_msg(LVL_WARN, "Not enough memory. Segment dropped.");
+		return;
+	}
+
+	if (tcp_pdu_decode(pdu, &rident, &dseg) != EOK) {
+		log_msg(LVL_WARN, "Not enough memory. Segment dropped.");
+		return;
+	}
+
+	tcp_pdu_delete(pdu);
+
+	/** Insert decoded segment into rqueue */
+	tcp_rqueue_insert_seg(&rident, dseg);
+	tcp_segment_delete(seg);
+#else
+	/* Reverse the identification */
+	tcp_sockpair_flipped(sp, &rident);
+
+	/* Insert segment back into rqueue */
+	tcp_rqueue_insert_seg(&rident, seg);
+#endif
+}
+
+/** Insert segment into receive queue.
+ *
+ * @param sp	Socket pair, oriented for reception
+ * @param seg	Segment
+ */
+void tcp_rqueue_insert_seg(tcp_sockpair_t *sp, tcp_segment_t *seg)
+{
+	tcp_rqueue_entry_t *rqe;
+	log_msg(LVL_DEBUG, "tcp_rqueue_insert_seg()");
+
+	tcp_segment_dump(seg);
+
+	rqe = calloc(1, sizeof(tcp_rqueue_entry_t));
+	if (rqe == NULL) {
+		log_msg(LVL_ERROR, "Failed allocating RQE.");
+		return;
+	}
+
+	rqe->sp = *sp;
+	rqe->seg = seg;
+
+	prodcons_produce(&rqueue, &rqe->link);
+}
+
+/** Receive queue handler thread. */
+static void tcp_rqueue_thread(void *arg)
+{
+	link_t *link;
+	tcp_rqueue_entry_t *rqe;
+
+	log_msg(LVL_DEBUG, "tcp_rqueue_thread()");
+
+	while (true) {
+		link = prodcons_consume(&rqueue);
+		rqe = list_get_instance(link, tcp_rqueue_entry_t, link);
+
+		tcp_as_segment_arrived(&rqe->sp, rqe->seg);
+	}
+}
+
+/** Start receive queue handler thread. */
+void tcp_rqueue_thread_start(void)
+{
+	thread_id_t tid;
+        int rc;
+
+	log_msg(LVL_DEBUG, "tcp_rqueue_thread_start()");
+
+	rc = thread_create(tcp_rqueue_thread, NULL, "rqueue", &tid);
+	if (rc != EOK) {
+		log_msg(LVL_ERROR, "Failed creating rqueue thread.");
+		return;
+	}
+}
+
+/**
+ * @}
+ */
Index: uspace/srv/net/tl/tcp/rqueue.h
===================================================================
--- uspace/srv/net/tl/tcp/rqueue.h	(revision 899f1a95f22eca90d14c5209d80a487c9a0979f0)
+++ uspace/srv/net/tl/tcp/rqueue.h	(revision 899f1a95f22eca90d14c5209d80a487c9a0979f0)
@@ -0,0 +1,50 @@
+/*
+ * Copyright (c) 2011 Jiri Svoboda
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ *   notice, this list of conditions and the following disclaimer.
+ * - Redistributions in binary form must reproduce the above copyright
+ *   notice, this list of conditions and the following disclaimer in the
+ *   documentation and/or other materials provided with the distribution.
+ * - The name of the author may not be used to endorse or promote products
+ *   derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/** @addtogroup tcp
+ * @{
+ */
+/** @file Global segment receive queue
+ */
+
+#ifndef RQUEUE_H
+#define RQUEUE_H
+
+#include "tcp_type.h"
+
+extern void tcp_rqueue_init(void);
+extern void tcp_rqueue_bounce_seg(tcp_sockpair_t *, tcp_segment_t *);
+extern void tcp_rqueue_insert_seg(tcp_sockpair_t *, tcp_segment_t *);
+extern void tcp_rqueue_handler(void *);
+extern void tcp_rqueue_thread_start(void);
+
+
+#endif
+
+/** @}
+ */
Index: uspace/srv/net/tl/tcp/segment.c
===================================================================
--- uspace/srv/net/tl/tcp/segment.c	(revision 899f1a95f22eca90d14c5209d80a487c9a0979f0)
+++ uspace/srv/net/tl/tcp/segment.c	(revision 899f1a95f22eca90d14c5209d80a487c9a0979f0)
@@ -0,0 +1,255 @@
+/*
+ * Copyright (c) 2011 Jiri Svoboda
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ *   notice, this list of conditions and the following disclaimer.
+ * - Redistributions in binary form must reproduce the above copyright
+ *   notice, this list of conditions and the following disclaimer in the
+ *   documentation and/or other materials provided with the distribution.
+ * - The name of the author may not be used to endorse or promote products
+ *   derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/** @addtogroup tcp
+ * @{
+ */
+
+/**
+ * @file Segment processing
+ */
+
+#include <io/log.h>
+#include <mem.h>
+#include <stdlib.h>
+#include "segment.h"
+#include "seq_no.h"
+#include "tcp_type.h"
+
+/** Alocate new segment structure. */
+tcp_segment_t *tcp_segment_new(void)
+{
+	return calloc(1, sizeof(tcp_segment_t));
+}
+
+/** Delete segment. */
+void tcp_segment_delete(tcp_segment_t *seg)
+{
+	free(seg);
+}
+
+/** Create duplicate of segment.
+ *
+ * @param seg	Segment
+ * @return 	Duplicate segment
+ */
+tcp_segment_t *tcp_segment_dup(tcp_segment_t *seg)
+{
+	tcp_segment_t *scopy;
+	size_t tsize;
+
+	scopy = tcp_segment_new();
+	if (scopy == NULL)
+		return NULL;
+
+	scopy->ctrl = seg->ctrl;
+	scopy->seq = seg->seq;
+	scopy->ack = seg->ack;
+	scopy->len = seg->len;
+	scopy->wnd = seg->wnd;
+	scopy->up = seg->up;
+
+	tsize = tcp_segment_text_size(seg);
+	scopy->data = calloc(tsize, 1);
+	if (scopy->data == NULL) {
+		free(scopy);
+		return NULL;
+	}
+
+	memcpy(scopy->data, seg->data, tsize);
+	scopy->dfptr = scopy->data;
+
+	return scopy;
+}
+
+/** Create a control-only segment.
+ *
+  * @return	Segment
+ */
+tcp_segment_t *tcp_segment_make_ctrl(tcp_control_t ctrl)
+{
+	tcp_segment_t *seg;
+
+	seg = tcp_segment_new();
+	if (seg == NULL)
+		return NULL;
+
+	seg->ctrl = ctrl;
+	seg->len = seq_no_control_len(ctrl);
+
+	return seg;
+}
+
+/** Create an RST segment.
+ *
+ * @param seg	Segment we are replying to
+ * @return	RST segment
+ */
+tcp_segment_t *tcp_segment_make_rst(tcp_segment_t *seg)
+{
+	tcp_segment_t *rseg;
+
+	rseg = tcp_segment_new();
+	if (rseg == NULL)
+		return NULL;
+
+	rseg->ctrl = CTL_RST;
+	rseg->seq = seg->ack;
+
+	return rseg;
+}
+
+/** Create a control segment.
+ *
+  * @return	Segment
+ */
+tcp_segment_t *tcp_segment_make_data(tcp_control_t ctrl, void *data,
+    size_t size)
+{
+	tcp_segment_t *seg;
+
+	seg = tcp_segment_new();
+	if (seg == NULL)
+		return NULL;
+
+	seg->ctrl = ctrl;
+	seg->len = seq_no_control_len(ctrl) + size;
+
+	seg->dfptr = seg->data = malloc(size);
+	if (seg->dfptr == NULL) {
+		free(seg);
+		return NULL;
+	}
+
+	memcpy(seg->data, data, size);
+
+	return seg;
+}
+
+
+/** Trim segment from left and right by the specified amount.
+ *
+ * Trim any text or control to remove the specified amount of sequence
+ * numbers from the left (lower sequence numbers) and right side
+ * (higher sequence numbers) of the segment.
+ *
+ * @param seg		Segment, will be modified in place
+ * @param left		Amount of sequence numbers to trim from the left
+ * @param right		Amount of sequence numbers to trim from the right
+ */
+void tcp_segment_trim(tcp_segment_t *seg, uint32_t left, uint32_t right)
+{
+	uint32_t t_size;
+
+	assert(left + right <= seg->len);
+
+	/* Special case, entire segment trimmed from left */
+	if (left == seg->len) {
+		seg->seq = seg->seq + seg->len;
+		seg->len = 0;
+		return;
+	}
+
+	/* Special case, entire segment trimmed from right */
+	if (right == seg->len) {
+		seg->len = 0;
+		return;
+	}
+
+	/* General case */
+
+	t_size = tcp_segment_text_size(seg);
+
+	if (left > 0 && (seg->ctrl & CTL_SYN) != 0) {
+		/* Trim SYN */
+		seg->ctrl &= ~CTL_SYN;
+		seg->seq++;
+		seg->len--;
+		left--;
+	}
+
+	if (right > 0 && (seg->ctrl & CTL_FIN) != 0) {
+		/* Trim FIN */
+		seg->ctrl &= ~CTL_FIN;
+		seg->len--;
+		right--;
+	}
+
+	if (left > 0 || right > 0) {
+		/* Trim segment text */
+		assert(left + right <= t_size);
+
+		seg->data += left;
+		seg->len -= left + right;
+	}
+}
+
+/** Copy out text data from segment.
+ *
+ * Data is copied from the beginning of the segment text up to @a size bytes.
+ * @a size must not be greater than the size of the segment text, but
+ * it can be less.
+ *
+ * @param seg	Segment
+ * @param buf	Destination buffer
+ * @param size	Size of destination buffer
+ */
+void tcp_segment_text_copy(tcp_segment_t *seg, void *buf, size_t size)
+{
+	assert(size <= tcp_segment_text_size(seg));
+	memcpy(buf, seg->data, size);
+}
+
+/** Return number of bytes in segment text.
+ *
+ * @param seg	Segment
+ * @return	Number of bytes in segment text
+ */
+size_t tcp_segment_text_size(tcp_segment_t *seg)
+{
+	return seg->len - seq_no_control_len(seg->ctrl);
+}
+
+/** Dump segment contents to log.
+ *
+ * @param seg	Segment
+ */
+void tcp_segment_dump(tcp_segment_t *seg)
+{
+	log_msg(LVL_DEBUG, "Segment dump:");
+	log_msg(LVL_DEBUG, " - ctrl = %u", (unsigned)seg->ctrl);
+	log_msg(LVL_DEBUG, " - seq = % " PRIu32, seg->seq);
+	log_msg(LVL_DEBUG, " - ack = % " PRIu32, seg->ack);
+	log_msg(LVL_DEBUG, " - len = % " PRIu32, seg->len);
+	log_msg(LVL_DEBUG, " - wnd = % " PRIu32, seg->wnd);
+	log_msg(LVL_DEBUG, " - up = % " PRIu32, seg->up);
+}
+
+/**
+ * @}
+ */
Index: uspace/srv/net/tl/tcp/segment.h
===================================================================
--- uspace/srv/net/tl/tcp/segment.h	(revision 899f1a95f22eca90d14c5209d80a487c9a0979f0)
+++ uspace/srv/net/tl/tcp/segment.h	(revision 899f1a95f22eca90d14c5209d80a487c9a0979f0)
@@ -0,0 +1,57 @@
+/*
+ * Copyright (c) 2011 Jiri Svoboda
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ *   notice, this list of conditions and the following disclaimer.
+ * - Redistributions in binary form must reproduce the above copyright
+ *   notice, this list of conditions and the following disclaimer in the
+ *   documentation and/or other materials provided with the distribution.
+ * - The name of the author may not be used to endorse or promote products
+ *   derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/** @addtogroup tcp
+ * @{
+ */
+/** @file Segment processing
+ */
+
+#ifndef SEGMENT_H
+#define SEGMENT_H
+
+#include <sys/types.h>
+#include "tcp_type.h"
+
+extern tcp_segment_t *tcp_segment_new(void);
+extern void tcp_segment_delete(tcp_segment_t *);
+extern tcp_segment_t *tcp_segment_dup(tcp_segment_t *);
+extern tcp_segment_t *tcp_segment_make_ctrl(tcp_control_t);
+extern tcp_segment_t *tcp_segment_make_rst(tcp_segment_t *);
+extern tcp_segment_t *tcp_segment_make_data(tcp_control_t, void *, size_t);
+extern void tcp_segment_trim(tcp_segment_t *, uint32_t, uint32_t);
+extern void tcp_segment_text_copy(tcp_segment_t *, void *, size_t);
+extern size_t tcp_segment_text_size(tcp_segment_t *);
+extern void tcp_segment_dump(tcp_segment_t *);
+
+
+
+#endif
+
+/** @}
+ */
Index: uspace/srv/net/tl/tcp/seq_no.c
===================================================================
--- uspace/srv/net/tl/tcp/seq_no.c	(revision 899f1a95f22eca90d14c5209d80a487c9a0979f0)
+++ uspace/srv/net/tl/tcp/seq_no.c	(revision 899f1a95f22eca90d14c5209d80a487c9a0979f0)
@@ -0,0 +1,243 @@
+/*
+ * Copyright (c) 2011 Jiri Svoboda
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ *   notice, this list of conditions and the following disclaimer.
+ * - Redistributions in binary form must reproduce the above copyright
+ *   notice, this list of conditions and the following disclaimer in the
+ *   documentation and/or other materials provided with the distribution.
+ * - The name of the author may not be used to endorse or promote products
+ *   derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/** @addtogroup tcp
+ * @{
+ */
+
+/**
+ * @file Sequence number computations
+ */
+
+#include <assert.h>
+#include <bool.h>
+#include <sys/types.h>
+#include "seq_no.h"
+#include "tcp_type.h"
+
+/** a <= b < c modulo sequence space */
+static bool seq_no_le_lt(uint32_t a, uint32_t b, uint32_t c)
+{
+	if (a <= c) {
+		return (a <= b) && (b < c);
+	} else {
+		return (b < c) || (a <= b);
+	}
+}
+
+/** a < b <= c modulo sequence space */
+static bool seq_no_lt_le(uint32_t a, uint32_t b, uint32_t c)
+{
+	if (a <= c) {
+		return (a < b) && (b <= c);
+	} else {
+		return (b <= c) || (a < b);
+	}
+}
+
+/** Determine wheter ack is acceptable (new acknowledgement) */
+bool seq_no_ack_acceptable(tcp_conn_t *conn, uint32_t seg_ack)
+{
+	/* SND.UNA < SEG.ACK <= SND.NXT */
+	return seq_no_lt_le(conn->snd_una, seg_ack, conn->snd_nxt);
+}
+
+/** Determine wheter ack is duplicate.
+ *
+ * ACK is duplicate if it refers to a sequence number that has
+ * aleady been acked (SEG.ACK <= SND.UNA).
+ */
+bool seq_no_ack_duplicate(tcp_conn_t *conn, uint32_t seg_ack)
+{
+	uint32_t diff;
+
+	/*
+	 * There does not seem to be a three-point comparison
+	 * equivalent of SEG.ACK < SND.UNA. Thus we do it
+	 * on a best-effort basis, based on the difference.
+	 * [-2^31, 0) means less-than, 0 means equal, [0, 2^31)
+	 * means greater-than. Less-than or equal means duplicate.
+	 */
+	diff = seg_ack - conn->snd_una;
+	return diff == 0 || (diff & (0x1 << 31)) != 0;
+}
+
+/** Determine if sequence number is in receive window. */
+bool seq_no_in_rcv_wnd(tcp_conn_t *conn, uint32_t sn)
+{
+	return seq_no_le_lt(conn->rcv_nxt, sn, conn->rcv_nxt + conn->rcv_wnd);
+}
+
+/** Determine segment has new window update.
+ *
+ * Window update is new if either SND.WL1 < SEG.SEQ or
+ * (SND.WL1 = SEG.SEQ and SND.WL2 <= SEG.ACK).
+ */
+bool seq_no_new_wnd_update(tcp_conn_t *conn, tcp_segment_t *seg)
+{
+	bool n_seq, n_ack;
+
+	assert(seq_no_segment_acceptable(conn, seg));
+
+	/*
+	 * We make use of the fact that the peer should not ACK anything
+	 * beyond our send window (we surely haven't sent that yet)
+	 * as we should have filtered those acks out.
+	 * We use SND.UNA+SND.WND as the third point of comparison.
+	 */
+
+	n_seq = seq_no_lt_le(conn->snd_wl1, seg->seq,
+	    conn->snd_una + conn->snd_wnd);
+
+	n_ack = conn->snd_wl1 == seg->seq &&
+	    seq_no_le_lt(conn->snd_wl2, seg->ack,
+	    conn->snd_una + conn->snd_wnd + 1);
+
+	return n_seq || n_ack;
+}
+
+/** Determine if segment is ready for processing.
+ *
+ * Assuming segment is acceptable, a segment is ready if it intersects
+ * RCV.NXT, that is we can process it immediately.
+ */
+bool seq_no_segment_ready(tcp_conn_t *conn, tcp_segment_t *seg)
+{
+	assert(seq_no_segment_acceptable(conn, seg));
+
+	return seq_no_le_lt(seg->seq, conn->rcv_nxt, seg->seq + seg->len + 1);
+}
+
+/** Determine whether segment is fully acked */
+bool seq_no_segment_acked(tcp_conn_t *conn, tcp_segment_t *seg, uint32_t ack)
+{
+	assert(seg->len > 0);
+	return seq_no_lt_le(seg->seq, seg->seq + seg->len, ack);
+}
+
+/** Determine whether initial SYN is acked */
+bool seq_no_syn_acked(tcp_conn_t *conn)
+{
+	return seq_no_lt_le(conn->iss, conn->snd_una, conn->snd_nxt);
+}
+
+/** Determine whether segment overlaps the receive window */
+bool seq_no_segment_acceptable(tcp_conn_t *conn, tcp_segment_t *seg)
+{
+	bool b_in, e_in;
+
+	b_in = seq_no_le_lt(conn->rcv_nxt, seg->seq, conn->rcv_nxt
+	    + conn->rcv_wnd);
+
+	e_in = seq_no_le_lt(conn->rcv_nxt, seg->seq + seg->len - 1,
+	    conn->rcv_nxt + conn->rcv_wnd);
+
+	if (seg->len == 0 && conn->rcv_wnd == 0) {
+		return seg->seq == conn->rcv_nxt;
+	} else if (seg->len == 0 && conn->rcv_wnd != 0) {
+		return b_in;
+	} else if (seg->len > 0 && conn->rcv_wnd == 0) {
+		return false;
+	} else {
+		return b_in || e_in;
+	}
+}
+
+/** Determine size that control bits occupy in sequence space. */
+uint32_t seq_no_control_len(tcp_control_t ctrl)
+{
+	uint32_t len = 0;
+
+	if ((ctrl & CTL_SYN) != 0)
+		++len;
+
+	if ((ctrl & CTL_FIN) != 0)
+		++len;
+
+	return len;
+}
+
+/** Calculate the amount of trim needed to fit segment in receive window. */
+void seq_no_seg_trim_calc(tcp_conn_t *conn, tcp_segment_t *seg,
+    uint32_t *left, uint32_t *right)
+{
+	assert(seq_no_segment_acceptable(conn, seg));
+
+	/*
+	 * If RCV.NXT is between SEG.SEQ and RCV.NXT+RCV.WND, then
+	 * left trim amount is positive
+	 */
+	if (seq_no_lt_le(seg->seq, conn->rcv_nxt,
+	    conn->rcv_nxt + conn->rcv_wnd)) {
+		*left = conn->rcv_nxt - seg->seq;
+	} else {
+		*left = 0;
+	}
+
+	/*
+	 * If SEG.SEQ+SEG.LEN is between SEG.SEQ and RCV.NXT+RCV.WND,
+	 * then right trim is zero.
+	 */
+	if (seq_no_lt_le(seg->seq - 1, seg->seq + seg->len,
+	    conn->rcv_nxt + conn->rcv_wnd)) {
+		*right = 0;
+	} else {
+		*right = (seg->seq + seg->len) -
+		    (conn->rcv_nxt + conn->rcv_wnd);
+	}
+}
+
+/** Segment order comparison.
+ *
+ * Compare sequence order of two acceptable segments.
+ *
+ * @param conn		Connection
+ * @param sa		Segment A
+ * @param sb		Segment B
+ *
+ * @return		-1, 0, 1, resp. if A < B, A == B, A > B in terms
+ *			of sequence order of the beginning of the segment.
+ */
+int seq_no_seg_cmp(tcp_conn_t *conn, tcp_segment_t *sa, tcp_segment_t *sb)
+{
+	assert(seq_no_segment_acceptable(conn, sa));
+	assert(seq_no_segment_acceptable(conn, sb));
+
+	if (seq_no_lt_le(sa->seq, sb->seq, conn->rcv_nxt + conn->rcv_wnd))
+		return -1;
+
+	if (seq_no_lt_le(sb->seq, sa->seq, conn->rcv_nxt + conn->rcv_wnd))
+		return +1;
+
+	assert(sa->seq == sb->seq);
+	return 0;
+}
+
+/**
+ * @}
+ */
Index: uspace/srv/net/tl/tcp/seq_no.h
===================================================================
--- uspace/srv/net/tl/tcp/seq_no.h	(revision 899f1a95f22eca90d14c5209d80a487c9a0979f0)
+++ uspace/srv/net/tl/tcp/seq_no.h	(revision 899f1a95f22eca90d14c5209d80a487c9a0979f0)
@@ -0,0 +1,58 @@
+/*
+ * Copyright (c) 2011 Jiri Svoboda
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ *   notice, this list of conditions and the following disclaimer.
+ * - Redistributions in binary form must reproduce the above copyright
+ *   notice, this list of conditions and the following disclaimer in the
+ *   documentation and/or other materials provided with the distribution.
+ * - The name of the author may not be used to endorse or promote products
+ *   derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/** @addtogroup tcp
+ * @{
+ */
+/** @file Sequence number computations
+ */
+
+#ifndef SEQ_NO_H
+#define SEQ_NO_H
+
+#include <sys/types.h>
+#include "tcp_type.h"
+
+extern bool seq_no_ack_acceptable(tcp_conn_t *, uint32_t);
+extern bool seq_no_ack_duplicate(tcp_conn_t *, uint32_t);
+extern bool seq_no_in_rcv_wnd(tcp_conn_t *, uint32_t);
+extern bool seq_no_new_wnd_update(tcp_conn_t *, tcp_segment_t *);
+extern bool seq_no_segment_acked(tcp_conn_t *, tcp_segment_t *, uint32_t);
+extern bool seq_no_syn_acked(tcp_conn_t *);
+extern bool seq_no_segment_ready(tcp_conn_t *, tcp_segment_t *);
+extern bool seq_no_segment_acceptable(tcp_conn_t *, tcp_segment_t *);
+extern void seq_no_seg_trim_calc(tcp_conn_t *, tcp_segment_t *, uint32_t *,
+    uint32_t *);
+extern int seq_no_seg_cmp(tcp_conn_t *, tcp_segment_t *, tcp_segment_t *);
+
+extern uint32_t seq_no_control_len(tcp_control_t);
+
+#endif
+
+/** @}
+ */
Index: uspace/srv/net/tl/tcp/sock.c
===================================================================
--- uspace/srv/net/tl/tcp/sock.c	(revision 899f1a95f22eca90d14c5209d80a487c9a0979f0)
+++ uspace/srv/net/tl/tcp/sock.c	(revision 899f1a95f22eca90d14c5209d80a487c9a0979f0)
@@ -0,0 +1,705 @@
+/*
+ * Copyright (c) 2008 Lukas Mejdrech
+ * Copyright (c) 2011 Jiri Svoboda
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ *   notice, this list of conditions and the following disclaimer.
+ * - Redistributions in binary form must reproduce the above copyright
+ *   notice, this list of conditions and the following disclaimer in the
+ *   documentation and/or other materials provided with the distribution.
+ * - The name of the author may not be used to endorse or promote products
+ *   derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/** @addtogroup tcp
+ * @{
+ */
+
+/**
+ * @file Socket provider
+ */
+
+#include <async.h>
+#include <errno.h>
+#include <io/log.h>
+#include <ip_client.h>
+#include <ipc/socket.h>
+#include <net/modules.h>
+#include <net/socket.h>
+
+#include "sock.h"
+#include "std.h"
+#include "tcp.h"
+#include "tcp_type.h"
+#include "ucall.h"
+
+#define FRAGMENT_SIZE 1024
+
+/** Free ports pool start. */
+#define TCP_FREE_PORTS_START		1025
+
+/** Free ports pool end. */
+#define TCP_FREE_PORTS_END		65535
+
+static int last_used_port = TCP_FREE_PORTS_START - 1;
+static socket_ports_t gsock;
+
+void tcp_sock_init(void)
+{
+	socket_ports_initialize(&gsock);
+}
+
+static void tcp_free_sock_data(socket_core_t *sock_core)
+{
+	tcp_sockdata_t *socket;
+
+	socket = (tcp_sockdata_t *)sock_core->specific_data;
+	(void)socket;
+}
+
+static void tcp_sock_notify_data(socket_core_t *sock_core)
+{
+	log_msg(LVL_DEBUG, "tcp_sock_notify_data(%d)", sock_core->socket_id);
+	async_exch_t *exch = async_exchange_begin(sock_core->sess);
+	async_msg_5(exch, NET_SOCKET_RECEIVED, (sysarg_t)sock_core->socket_id,
+	    FRAGMENT_SIZE, 0, 0, 1);
+	async_exchange_end(exch);
+}
+
+static void tcp_sock_notify_aconn(socket_core_t *lsock_core)
+{
+	log_msg(LVL_DEBUG, "tcp_sock_notify_aconn(%d)", lsock_core->socket_id);
+	async_exch_t *exch = async_exchange_begin(lsock_core->sess);
+	async_msg_5(exch, NET_SOCKET_ACCEPTED, (sysarg_t)lsock_core->socket_id,
+	    FRAGMENT_SIZE, 0, 0, 0);
+	async_exchange_end(exch);
+}
+
+static void tcp_sock_socket(tcp_client_t *client, ipc_callid_t callid, ipc_call_t call)
+{
+	tcp_sockdata_t *sock;
+	int sock_id;
+	int rc;
+	ipc_call_t answer;
+
+	log_msg(LVL_DEBUG, "tcp_sock_socket()");
+	sock = calloc(sizeof(tcp_sockdata_t), 1);
+	if (sock == NULL) {
+		async_answer_0(callid, ENOMEM);
+		return;
+	}
+
+	sock->client = client;
+	sock->laddr.ipv4 = TCP_IPV4_ANY;
+
+	sock_id = SOCKET_GET_SOCKET_ID(call);
+	rc = socket_create(&client->sockets, client->sess, sock, &sock_id);
+	if (rc != EOK) {
+		async_answer_0(callid, rc);
+		return;
+	}
+
+	refresh_answer(&answer, NULL);
+	SOCKET_SET_SOCKET_ID(answer, sock_id);
+
+	SOCKET_SET_DATA_FRAGMENT_SIZE(answer, FRAGMENT_SIZE);
+	SOCKET_SET_HEADER_SIZE(answer, sizeof(tcp_header_t));
+	answer_call(callid, EOK, &answer, 3);
+}
+
+static void tcp_sock_bind(tcp_client_t *client, ipc_callid_t callid, ipc_call_t call)
+{
+	int rc;
+	struct sockaddr *addr;
+	size_t addr_len;
+	socket_core_t *sock_core;
+	tcp_sockdata_t *socket;
+
+	log_msg(LVL_DEBUG, "tcp_sock_bind()");
+	log_msg(LVL_DEBUG, " - async_data_write_accept");
+	rc = async_data_write_accept((void **) &addr, false, 0, 0, 0, &addr_len);
+	if (rc != EOK) {
+		async_answer_0(callid, rc);
+		return;
+	}
+
+	log_msg(LVL_DEBUG, " - call socket_bind");
+	rc = socket_bind(&client->sockets, &gsock, SOCKET_GET_SOCKET_ID(call),
+	    addr, addr_len, TCP_FREE_PORTS_START, TCP_FREE_PORTS_END,
+	    last_used_port);
+	if (rc != EOK) {
+		async_answer_0(callid, rc);
+		return;
+	}
+
+	log_msg(LVL_DEBUG, " - call socket_cores_find");
+	sock_core = socket_cores_find(&client->sockets, SOCKET_GET_SOCKET_ID(call));
+	if (sock_core != NULL) {
+		socket = (tcp_sockdata_t *)sock_core->specific_data;
+		/* XXX Anything to do? */
+		(void) socket;
+	}
+
+	log_msg(LVL_DEBUG, " - success");
+	async_answer_0(callid, EOK);
+}
+
+static void tcp_sock_listen(tcp_client_t *client, ipc_callid_t callid, ipc_call_t call)
+{
+	int socket_id;
+	int backlog;
+	socket_core_t *sock_core;
+	tcp_sockdata_t *socket;
+
+	log_msg(LVL_DEBUG, "tcp_sock_listen()");
+
+	socket_id = SOCKET_GET_SOCKET_ID(call);
+	backlog = SOCKET_GET_BACKLOG(call);
+
+	if (backlog < 0) {
+		async_answer_0(callid, EINVAL);
+		return;
+	}
+
+	sock_core = socket_cores_find(&client->sockets, socket_id);
+	if (sock_core == NULL) {
+		async_answer_0(callid, ENOTSOCK);
+		return;
+	}
+
+	socket = (tcp_sockdata_t *)sock_core->specific_data;
+
+	/*
+	 * XXX We do not do anything and defer action to accept().
+	 * This is a slight difference in semantics, but most servers
+	 * would just listen() and immediately accept() in a loop.
+	 *
+	 * The only difference is that there is a window between
+	 * listen() and accept() or two accept()s where we refuse
+	 * connections.
+	 */
+	(void)backlog;
+	(void)socket;
+
+	async_answer_0(callid, EOK);
+	log_msg(LVL_DEBUG, "tcp_sock_listen(): notify data\n");
+	/* Push one accept notification to client's queue */
+	tcp_sock_notify_aconn(sock_core);
+}
+
+static void tcp_sock_connect(tcp_client_t *client, ipc_callid_t callid, ipc_call_t call)
+{
+	int rc;
+	struct sockaddr_in *addr;
+	int socket_id;
+	size_t addr_len;
+	socket_core_t *sock_core;
+	tcp_sockdata_t *socket;
+	tcp_error_t trc;
+	tcp_sock_t lsocket;
+	tcp_sock_t fsocket;
+	nic_device_id_t dev_id;
+	tcp_phdr_t *phdr;
+	size_t phdr_len;
+
+	log_msg(LVL_DEBUG, "tcp_sock_connect()");
+
+	rc = async_data_write_accept((void **) &addr, false, 0, 0, 0, &addr_len);
+	if (rc != EOK || addr_len != sizeof(struct sockaddr_in)) {
+		async_answer_0(callid, rc);
+		return;
+	}
+
+	socket_id = SOCKET_GET_SOCKET_ID(call);
+
+	sock_core = socket_cores_find(&client->sockets, socket_id);
+	if (sock_core == NULL) {
+		async_answer_0(callid, ENOTSOCK);
+		return;
+	}
+
+	socket = (tcp_sockdata_t *)sock_core->specific_data;
+	if (sock_core->port <= 0) {
+		rc = socket_bind_free_port(&gsock, sock_core,
+		    TCP_FREE_PORTS_START, TCP_FREE_PORTS_END,
+		    last_used_port);
+		if (rc != EOK) {
+			async_answer_0(callid, rc);
+			return;
+		}
+
+		last_used_port = sock_core->port;
+	}
+
+	if (socket->laddr.ipv4 == TCP_IPV4_ANY) {
+		/* Find route to determine local IP address. */
+		rc = ip_get_route_req(ip_sess, IPPROTO_TCP,
+		    (struct sockaddr *)addr, sizeof(*addr), &dev_id,
+		    (void **)&phdr, &phdr_len);
+		if (rc != EOK) {
+			async_answer_0(callid, rc);
+			log_msg(LVL_DEBUG, "tcp_transmit_connect: Failed to find route.");
+			return;
+		}
+
+		socket->laddr.ipv4 = uint32_t_be2host(phdr->src_addr);
+		log_msg(LVL_DEBUG, "Local IP address is %x", socket->laddr.ipv4);
+		free(phdr);
+	}
+
+	lsocket.addr.ipv4 = socket->laddr.ipv4;
+	lsocket.port = sock_core->port;
+	fsocket.addr.ipv4 = uint32_t_be2host(addr->sin_addr.s_addr);
+	fsocket.port = uint16_t_be2host(addr->sin_port);
+
+	trc = tcp_uc_open(&lsocket, &fsocket, ap_active, &socket->conn);
+
+	if (socket->conn != NULL)
+		socket->conn->name = (char *)"C";
+
+	switch (trc) {
+	case TCP_EOK:
+		rc = EOK;
+		break;
+	case TCP_ERESET:
+		rc = ECONNREFUSED;
+		break;
+	default:
+		assert(false);
+	}
+
+	async_answer_0(callid, rc);
+
+	/* Push one fragment notification to client's queue */
+	tcp_sock_notify_data(sock_core);
+	log_msg(LVL_DEBUG, "tcp_sock_connect(): notify conn\n");
+}
+
+static void tcp_sock_accept(tcp_client_t *client, ipc_callid_t callid, ipc_call_t call)
+{
+	ipc_call_t answer;
+	int socket_id;
+	int asock_id;
+	socket_core_t *sock_core;
+	socket_core_t *asock_core;
+	tcp_sockdata_t *socket;
+	tcp_sockdata_t *asocket;
+	tcp_error_t trc;
+	tcp_sock_t lsocket;
+	tcp_sock_t fsocket;
+	tcp_conn_t *conn;
+	int rc;
+
+	log_msg(LVL_DEBUG, "tcp_sock_accept()");
+
+	socket_id = SOCKET_GET_SOCKET_ID(call);
+	asock_id = SOCKET_GET_NEW_SOCKET_ID(call);
+
+	sock_core = socket_cores_find(&client->sockets, socket_id);
+	if (sock_core == NULL) {
+		async_answer_0(callid, ENOTSOCK);
+		return;
+	}
+
+	socket = (tcp_sockdata_t *)sock_core->specific_data;
+
+	log_msg(LVL_DEBUG, " - verify socket->conn");
+	if (socket->conn != NULL) {
+		async_answer_0(callid, EINVAL);
+		return;
+	}
+
+	log_msg(LVL_DEBUG, " - open connection");
+
+	lsocket.addr.ipv4 = TCP_IPV4_ANY;
+	lsocket.port = sock_core->port;
+	fsocket.addr.ipv4 = TCP_IPV4_ANY;
+	fsocket.port = TCP_PORT_ANY;
+
+	trc = tcp_uc_open(&lsocket, &fsocket, ap_passive, &conn);
+	if (conn != NULL)
+		conn->name = (char *)"S";
+
+	log_msg(LVL_DEBUG, " - decode TCP return code");
+
+	switch (trc) {
+	case TCP_EOK:
+		rc = EOK;
+		break;
+	case TCP_ERESET:
+		rc = ECONNABORTED;
+		break;
+	default:
+		assert(false);
+	}
+
+	log_msg(LVL_DEBUG, " - check TCP return code");
+	if (rc != EOK) {
+		async_answer_0(callid, rc);
+		return;
+	}
+
+	log_msg(LVL_DEBUG, "tcp_sock_accept(): allocate asocket\n");
+	asocket = calloc(sizeof(tcp_sockdata_t), 1);
+	if (asocket == NULL) {
+		async_answer_0(callid, ENOMEM);
+		return;
+	}
+
+	asocket->client = client;
+	asocket->conn = conn;
+	log_msg(LVL_DEBUG, "tcp_sock_accept():create asocket\n");
+
+	rc = socket_create(&client->sockets, client->sess, asocket, &asock_id);
+	if (rc != EOK) {
+		async_answer_0(callid, rc);
+		return;
+	}
+	log_msg(LVL_DEBUG, "tcp_sock_accept(): find acore\n");
+
+	asock_core = socket_cores_find(&client->sockets, asock_id);
+	assert(asock_core != NULL);
+
+	refresh_answer(&answer, NULL);
+
+	SOCKET_SET_DATA_FRAGMENT_SIZE(answer, FRAGMENT_SIZE);
+	SOCKET_SET_SOCKET_ID(answer, asock_id);
+	SOCKET_SET_ADDRESS_LENGTH(answer, sizeof(struct sockaddr_in));
+
+	answer_call(callid, asock_core->socket_id, &answer, 3);
+
+	/* Push one accept notification to client's queue */
+	tcp_sock_notify_aconn(sock_core);
+
+	/* Push one fragment notification to client's queue */
+	tcp_sock_notify_data(asock_core);
+	log_msg(LVL_DEBUG, "tcp_sock_accept(): notify aconn\n");
+}
+
+static void tcp_sock_send(tcp_client_t *client, ipc_callid_t callid, ipc_call_t call)
+{
+	int socket_id;
+	int fragments;
+	int index;
+	socket_core_t *sock_core;
+	tcp_sockdata_t *socket;
+	ipc_call_t answer;
+	ipc_callid_t wcallid;
+	size_t length;
+	uint8_t buffer[FRAGMENT_SIZE];
+	tcp_error_t trc;
+	int rc;
+
+	log_msg(LVL_DEBUG, "tcp_sock_send()");
+	socket_id = SOCKET_GET_SOCKET_ID(call);
+	fragments = SOCKET_GET_DATA_FRAGMENTS(call);
+	SOCKET_GET_FLAGS(call);
+
+	sock_core = socket_cores_find(&client->sockets, socket_id);
+	if (sock_core == NULL) {
+		async_answer_0(callid, ENOTSOCK);
+		return;
+	}
+
+	socket = (tcp_sockdata_t *)sock_core->specific_data;
+	if (socket->conn == NULL) {
+		async_answer_0(callid, ENOTCONN);
+		return;
+	}
+
+	for (index = 0; index < fragments; index++) {
+		if (!async_data_write_receive(&wcallid, &length)) {
+			async_answer_0(callid, EINVAL);
+			return;
+		}
+
+		if (length > FRAGMENT_SIZE)
+			length = FRAGMENT_SIZE;
+
+		rc = async_data_write_finalize(wcallid, buffer, length);
+		if (rc != EOK) {
+			async_answer_0(callid, rc);
+			return;
+		}
+
+		trc = tcp_uc_send(socket->conn, buffer, length, 0);
+
+		switch (trc) {
+		case TCP_EOK:
+			rc = EOK;
+			break;
+		case TCP_ENOTEXIST:
+			rc = ENOTCONN;
+			break;
+		case TCP_ECLOSING:
+			rc = ENOTCONN;
+			break;
+		case TCP_ERESET:
+			rc = ECONNABORTED;
+			break;
+		default:
+			assert(false);
+		}
+
+		if (rc != EOK) {
+			async_answer_0(callid, rc);
+			return;
+		}
+	}
+
+	refresh_answer(&answer, NULL);
+	SOCKET_SET_DATA_FRAGMENT_SIZE(answer, FRAGMENT_SIZE);
+	answer_call(callid, EOK, &answer, 2);
+}
+
+static void tcp_sock_sendto(tcp_client_t *client, ipc_callid_t callid, ipc_call_t call)
+{
+	log_msg(LVL_DEBUG, "tcp_sock_sendto()");
+	async_answer_0(callid, ENOTSUP);
+}
+
+static void tcp_sock_recvfrom(tcp_client_t *client, ipc_callid_t callid, ipc_call_t call)
+{
+	int socket_id;
+	int flags;
+	size_t addr_length, length;
+	socket_core_t *sock_core;
+	tcp_sockdata_t *socket;
+	ipc_call_t answer;
+	ipc_callid_t rcallid;
+	uint8_t buffer[FRAGMENT_SIZE];
+	size_t data_len;
+	xflags_t xflags;
+	tcp_error_t trc;
+	struct sockaddr_in addr;
+	tcp_sock_t *rsock;
+	int rc;
+
+	log_msg(LVL_DEBUG, "%p: tcp_sock_recv[from]()", client);
+
+	socket_id = SOCKET_GET_SOCKET_ID(call);
+	flags = SOCKET_GET_FLAGS(call);
+
+	sock_core = socket_cores_find(&client->sockets, socket_id);
+	if (sock_core == NULL) {
+		async_answer_0(callid, ENOTSOCK);
+		return;
+	}
+
+	socket = (tcp_sockdata_t *)sock_core->specific_data;
+	if (socket->conn == NULL) {
+		async_answer_0(callid, ENOTCONN);
+		return;
+	}
+
+	(void)flags;
+
+	trc = tcp_uc_receive(socket->conn, buffer, FRAGMENT_SIZE, &data_len,
+	    &xflags);
+	log_msg(LVL_DEBUG, "**** tcp_uc_receive done");
+
+	switch (trc) {
+	case TCP_EOK:
+		rc = EOK;
+		break;
+	case TCP_ENOTEXIST:
+	case TCP_ECLOSING:
+		rc = ENOTCONN;
+		break;
+	case TCP_ERESET:
+		rc = ECONNABORTED;
+		break;
+	default:
+		assert(false);
+	}
+
+	log_msg(LVL_DEBUG, "**** tcp_uc_receive -> %d", rc);
+	if (rc != EOK) {
+		async_answer_0(callid, rc);
+		return;
+	}
+
+	if (IPC_GET_IMETHOD(call) == NET_SOCKET_RECVFROM) {
+		/* Fill addr */
+		rsock = &socket->conn->ident.foreign;
+		addr.sin_family = AF_INET;
+		addr.sin_addr.s_addr = host2uint32_t_be(rsock->addr.ipv4);
+		addr.sin_port = host2uint16_t_be(rsock->port);
+
+		log_msg(LVL_DEBUG, "addr read receive");
+		if (!async_data_read_receive(&rcallid, &addr_length)) {
+			async_answer_0(callid, EINVAL);
+			return;
+		}
+
+		if (addr_length > sizeof(addr))
+			addr_length = sizeof(addr);
+
+		log_msg(LVL_DEBUG, "addr read finalize");
+		rc = async_data_read_finalize(rcallid, &addr, addr_length);
+		if (rc != EOK) {
+			async_answer_0(callid, EINVAL);
+			return;
+		}
+	}
+
+	log_msg(LVL_DEBUG, "data read receive");
+	if (!async_data_read_receive(&rcallid, &length)) {
+		async_answer_0(callid, EINVAL);
+		return;
+	}
+
+	if (length > data_len)
+		length = data_len;
+
+	log_msg(LVL_DEBUG, "data read finalize");
+	rc = async_data_read_finalize(rcallid, buffer, length);
+
+	if (length < data_len && rc == EOK)
+		rc = EOVERFLOW;
+
+	SOCKET_SET_READ_DATA_LENGTH(answer, length);
+	answer_call(callid, EOK, &answer, 1);
+
+	/* Push one fragment notification to client's queue */
+	tcp_sock_notify_data(sock_core);
+}
+
+static void tcp_sock_close(tcp_client_t *client, ipc_callid_t callid, ipc_call_t call)
+{
+	int socket_id;
+	socket_core_t *sock_core;
+	tcp_sockdata_t *socket;
+	tcp_error_t trc;
+	int rc;
+	uint8_t buffer[FRAGMENT_SIZE];
+	size_t data_len;
+	xflags_t xflags;
+
+	log_msg(LVL_DEBUG, "tcp_sock_close()");
+	socket_id = SOCKET_GET_SOCKET_ID(call);
+
+	sock_core = socket_cores_find(&client->sockets, socket_id);
+	if (sock_core == NULL) {
+		async_answer_0(callid, ENOTSOCK);
+		return;
+	}
+
+	socket = (tcp_sockdata_t *)sock_core->specific_data;
+	rc = tcp_uc_close(socket->conn);
+	if (rc != EOK) {
+		async_answer_0(callid, rc);
+		return;
+	}
+
+	/* Drain incoming data. This should really be done in the background. */
+	do {
+		trc = tcp_uc_receive(socket->conn, buffer, FRAGMENT_SIZE,
+		    &data_len, &xflags);
+	} while (trc == TCP_EOK);
+
+	rc = socket_destroy(net_sess, socket_id, &client->sockets, &gsock,
+	    tcp_free_sock_data);
+	if (rc != EOK) {
+		async_answer_0(callid, rc);
+		return;
+	}
+
+	async_answer_0(callid, EOK);
+}
+
+static void tcp_sock_getsockopt(tcp_client_t *client, ipc_callid_t callid, ipc_call_t call)
+{
+	log_msg(LVL_DEBUG, "tcp_sock_getsockopt()");
+	async_answer_0(callid, ENOTSUP);
+}
+
+static void tcp_sock_setsockopt(tcp_client_t *client, ipc_callid_t callid, ipc_call_t call)
+{
+	log_msg(LVL_DEBUG, "tcp_sock_setsockopt()");
+	async_answer_0(callid, ENOTSUP);
+}
+
+int tcp_sock_connection(async_sess_t *sess, ipc_callid_t iid, ipc_call_t icall)
+{
+	ipc_callid_t callid;
+	ipc_call_t call;
+	tcp_client_t client;
+
+	/* Accept the connection */
+	async_answer_0(iid, EOK);
+
+	client.sess = sess;
+	socket_cores_initialize(&client.sockets);
+
+	while (true) {
+		callid = async_get_call(&call);
+		if (!IPC_GET_IMETHOD(call))
+			break;
+
+		log_msg(LVL_DEBUG, "tcp_sock_connection: METHOD=%d\n",
+		    (int)IPC_GET_IMETHOD(call));
+
+		switch (IPC_GET_IMETHOD(call)) {
+		case NET_SOCKET:
+			tcp_sock_socket(&client, callid, call);
+			break;
+		case NET_SOCKET_BIND:
+			tcp_sock_bind(&client, callid, call);
+			break;
+		case NET_SOCKET_LISTEN:
+			tcp_sock_listen(&client, callid, call);
+			break;
+		case NET_SOCKET_CONNECT:
+			tcp_sock_connect(&client, callid, call);
+			break;
+		case NET_SOCKET_ACCEPT:
+			tcp_sock_accept(&client, callid, call);
+			break;
+		case NET_SOCKET_SEND:
+			tcp_sock_send(&client, callid, call);
+			break;
+		case NET_SOCKET_SENDTO:
+			tcp_sock_sendto(&client, callid, call);
+			break;
+		case NET_SOCKET_RECV:
+		case NET_SOCKET_RECVFROM:
+			tcp_sock_recvfrom(&client, callid, call);
+			break;
+		case NET_SOCKET_CLOSE:
+			tcp_sock_close(&client, callid, call);
+			break;
+		case NET_SOCKET_GETSOCKOPT:
+			tcp_sock_getsockopt(&client, callid, call);
+			break;
+		case NET_SOCKET_SETSOCKOPT:
+			tcp_sock_setsockopt(&client, callid, call);
+			break;
+		default:
+			async_answer_0(callid, ENOTSUP);
+			break;
+		}
+	}
+
+	return EOK;
+}
+
+/**
+ * @}
+ */
Index: uspace/srv/net/tl/tcp/sock.h
===================================================================
--- uspace/srv/net/tl/tcp/sock.h	(revision 899f1a95f22eca90d14c5209d80a487c9a0979f0)
+++ uspace/srv/net/tl/tcp/sock.h	(revision 899f1a95f22eca90d14c5209d80a487c9a0979f0)
@@ -0,0 +1,46 @@
+/*
+ * Copyright (c) 2011 Jiri Svoboda
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ *   notice, this list of conditions and the following disclaimer.
+ * - Redistributions in binary form must reproduce the above copyright
+ *   notice, this list of conditions and the following disclaimer in the
+ *   documentation and/or other materials provided with the distribution.
+ * - The name of the author may not be used to endorse or promote products
+ *   derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/** @addtogroup tcp
+ * @{
+ */
+/** @file Socket provider
+ */
+
+#ifndef SOCK_H
+#define SOCK_H
+
+#include <async.h>
+
+extern void tcp_sock_init(void);
+extern int tcp_sock_connection(async_sess_t *, ipc_callid_t, ipc_call_t);
+
+#endif
+
+/** @}
+ */
Index: uspace/srv/net/tl/tcp/std.h
===================================================================
--- uspace/srv/net/tl/tcp/std.h	(revision 899f1a95f22eca90d14c5209d80a487c9a0979f0)
+++ uspace/srv/net/tl/tcp/std.h	(revision 899f1a95f22eca90d14c5209d80a487c9a0979f0)
@@ -0,0 +1,101 @@
+/*
+ * Copyright (c) 2011 Jiri Svoboda
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ *   notice, this list of conditions and the following disclaimer.
+ * - Redistributions in binary form must reproduce the above copyright
+ *   notice, this list of conditions and the following disclaimer in the
+ *   documentation and/or other materials provided with the distribution.
+ * - The name of the author may not be used to endorse or promote products
+ *   derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/** @addtogroup tcp
+ * @{
+ */
+/** @file TCP header definitions
+ *
+ * Based on IETF RFC 793
+ */
+
+#ifndef STD_H
+#define STD_H
+
+#include <sys/types.h>
+
+/** TCP Header (fixed part) */
+typedef struct {
+	/** Source port */
+	uint16_t src_port;
+	/** Destination port */
+	uint16_t dest_port;
+	/** Sequence number */
+	uint32_t seq;
+	/** Acknowledgement number */
+	uint32_t ack;
+	/** Data Offset, Reserved, Flags */
+	uint16_t doff_flags;
+	/** Window */
+	uint16_t window;
+	/* Checksum */
+	uint16_t checksum;
+	/** Urgent pointer */
+	uint16_t urg_ptr;
+} tcp_header_t;
+
+/** Bits in tcp_header_t.doff_flags */
+enum doff_flags_bits {
+	DF_DATA_OFFSET_h	= 15,
+	DF_DATA_OFFSET_l	= 12,
+	DF_URG			= 5,
+	DF_ACK			= 4,
+	DF_PSH			= 3,
+	DF_RST			= 2,
+	DF_SYN			= 1,
+	DF_FIN			= 0
+};
+
+/** TCP pseudo header */
+typedef struct {
+	/** Source address */
+	uint32_t src_addr;
+	/** Destination address */
+	uint32_t dest_addr;
+	/** Zero */
+	uint8_t zero;
+	/** Protocol */
+	uint8_t protocol;
+	/** TCP length */
+	uint16_t tcp_length;
+} tcp_phdr_t;
+
+/** Option kind */
+enum opt_kind {
+	/** End of option list */
+	OPT_END_LIST		= 0,
+	/** No-operation */
+	OPT_NOP			= 1,
+	/** Maximum segment size */
+	OPT_MAX_SEG_SIZE	= 2
+};
+
+#endif
+
+/** @}
+ */
Index: uspace/srv/net/tl/tcp/tcp.c
===================================================================
--- uspace/srv/net/tl/tcp/tcp.c	(revision 4c67e52b4043ea5cd4323c9bd8094bfde545d638)
+++ uspace/srv/net/tl/tcp/tcp.c	(revision 899f1a95f22eca90d14c5209d80a487c9a0979f0)
@@ -1,4 +1,4 @@
 /*
- * Copyright (c) 2008 Lukas Mejdrech
+ * Copyright (c) 2011 Jiri Svoboda
  * All rights reserved.
  *
@@ -31,229 +31,98 @@
  */
 
-/** @file
- * TCP module implementation.
- * @see tcp.h
+/**
+ * @file TCP (Transmission Control Protocol) network module
  */
 
-#include <assert.h>
 #include <async.h>
-#include <fibril_synch.h>
-#include <malloc.h>
-/* TODO remove stdio */
+#include <byteorder.h>
+#include <errno.h>
+#include <io/log.h>
 #include <stdio.h>
-#include <errno.h>
-
+#include <task.h>
+
+#include <icmp_remote.h>
+#include <ip_client.h>
+#include <ip_interface.h>
 #include <ipc/services.h>
-#include <ipc/net.h>
 #include <ipc/tl.h>
-#include <ipc/socket.h>
-
-#include <net/socket_codes.h>
-#include <net/ip_protocols.h>
-#include <net/in.h>
-#include <net/in6.h>
-#include <net/inet.h>
-#include <net/modules.h>
-
-#include <adt/dynamic_fifo.h>
+#include <tl_common.h>
+#include <tl_skel.h>
 #include <packet_client.h>
 #include <packet_remote.h>
-#include <net_checksum.h>
-#include <ip_client.h>
-#include <ip_interface.h>
-#include <icmp_client.h>
-#include <icmp_remote.h>
-#include <net_interface.h>
-#include <socket_core.h>
-#include <tl_common.h>
-#include <tl_remote.h>
-#include <tl_skel.h>
-
+
+#include "ncsim.h"
+#include "pdu.h"
+#include "rqueue.h"
+#include "sock.h"
+#include "std.h"
 #include "tcp.h"
-#include "tcp_header.h"
-
-/** TCP module name. */
-#define NAME  "tcp"
-
-/** The TCP window default value. */
-#define NET_DEFAULT_TCP_WINDOW		10240
-
-/** Initial timeout for new connections. */
-#define NET_DEFAULT_TCP_INITIAL_TIMEOUT	3000000L
-
-/** Default timeout for closing. */
-#define NET_DEFAULT_TCP_TIME_WAIT_TIMEOUT 2000L
-
-/** The initial outgoing sequence number. */
-#define TCP_INITIAL_SEQUENCE_NUMBER	2999
-
-/** Maximum TCP fragment size. */
-#define MAX_TCP_FRAGMENT_SIZE		65535
-
-/** Free ports pool start. */
-#define TCP_FREE_PORTS_START		1025
-
-/** Free ports pool end. */
-#define TCP_FREE_PORTS_END		65535
-
-/** Timeout for connection initialization, SYN sent. */
-#define TCP_SYN_SENT_TIMEOUT		1000000L
-
-/** The maximum number of timeouts in a row before singaling connection lost. */
-#define TCP_MAX_TIMEOUTS		8
-
-/** The number of acknowledgements before retransmit. */
-#define TCP_FAST_RETRANSMIT_COUNT	3
-
-/** Returns a value indicating whether the value is in the interval respecting
- * the possible overflow.
- *
- * The high end and/or the value may overflow, be lower than the low value.
- *
- * @param[in] lower	The last value before the interval.
- * @param[in] value	The value to be checked.
- * @param[in] higher_equal The last value in the interval.
- */
-#define IS_IN_INTERVAL_OVERFLOW(lower, value, higher_equal) \
-	((((lower) < (value)) && (((value) <= (higher_equal)) || \
-	((higher_equal) < (lower)))) || (((value) <= (higher_equal)) && \
-	((higher_equal) < (lower))))
-
-/** Type definition of the TCP timeout.
- *  @see tcp_timeout
- */
-typedef struct tcp_timeout tcp_timeout_t;
-
-/** TCP reply timeout data.
- *  Used as a timeouting fibril argument.
- *  @see tcp_timeout()
- */
-struct tcp_timeout {
-	/** TCP global data are going to be read only. */
-	int globals_read_only;
-
-	/** Socket port. */
-	int port;
-
-	/** Local sockets. */
-	socket_cores_t *local_sockets;
-
-	/** Socket identifier. */
-	int socket_id;
-
-	/** Socket state. */
-	tcp_socket_state_t state;
-
-	/** Sent packet sequence number. */
-	int sequence_number;
-
-	/** Timeout in microseconds. */
-	suseconds_t timeout;
-
-	/** Port map key. */
-	uint8_t *key;
-
-	/** Port map key length. */
-	size_t key_length;
-};
-
-static int tcp_release_and_return(packet_t *, int);
-static void tcp_prepare_operation_header(socket_core_t *, tcp_socket_data_t *,
-    tcp_header_t *, int synchronize, int);
-static int tcp_prepare_timeout(int (*)(void *), socket_core_t *,
-    tcp_socket_data_t *, size_t, tcp_socket_state_t, suseconds_t, int);
-static void tcp_free_socket_data(socket_core_t *);
-
-static int tcp_timeout(void *);
-
-static int tcp_release_after_timeout(void *);
-
-static int tcp_process_packet(nic_device_id_t, packet_t *, services_t);
-static int tcp_connect_core(socket_core_t *, socket_cores_t *,
-    struct sockaddr *, socklen_t);
-static int tcp_queue_prepare_packet(socket_core_t *, tcp_socket_data_t *,
-    packet_t *, size_t);
-static int tcp_queue_packet(socket_core_t *, tcp_socket_data_t *, packet_t *,
-    size_t);
-static packet_t *tcp_get_packets_to_send(socket_core_t *, tcp_socket_data_t *);
-static void tcp_send_packets(nic_device_id_t, packet_t *);
-
-static void tcp_process_acknowledgement(socket_core_t *, tcp_socket_data_t *,
-    tcp_header_t *);
-static packet_t *tcp_send_prepare_packet(socket_core_t *, tcp_socket_data_t *,
-    packet_t *, size_t, size_t);
-static packet_t *tcp_prepare_copy(socket_core_t *, tcp_socket_data_t *,
-    packet_t *, size_t, size_t);
-/* static */ void tcp_retransmit_packet(socket_core_t *, tcp_socket_data_t *,
-    size_t);
-static int tcp_create_notification_packet(packet_t **, socket_core_t *,
-    tcp_socket_data_t *, int, int);
-static void tcp_refresh_socket_data(tcp_socket_data_t *);
-
-static void tcp_initialize_socket_data(tcp_socket_data_t *);
-
-static int tcp_process_listen(socket_core_t *, tcp_socket_data_t *,
-    tcp_header_t *, packet_t *, struct sockaddr *, struct sockaddr *, size_t);
-static int tcp_process_syn_sent(socket_core_t *, tcp_socket_data_t *,
-    tcp_header_t *, packet_t *);
-static int tcp_process_syn_received(socket_core_t *, tcp_socket_data_t *,
-    tcp_header_t *, packet_t *);
-static int tcp_process_established(socket_core_t *, tcp_socket_data_t *,
-    tcp_header_t *, packet_t *, int, size_t);
-static int tcp_queue_received_packet(socket_core_t *, tcp_socket_data_t *,
-    packet_t *, int, size_t);
-static void tcp_queue_received_end_of_data(socket_core_t *socket);
-
-static int tcp_received_msg(nic_device_id_t, packet_t *, services_t, services_t);
-static int tcp_process_client_messages(async_sess_t *, ipc_callid_t,
-    ipc_call_t);
-
-static int tcp_listen_message(socket_cores_t *, int, int);
-static int tcp_connect_message(socket_cores_t *, int, struct sockaddr *,
-    socklen_t);
-static int tcp_recvfrom_message(socket_cores_t *, int, int, size_t *);
-static int tcp_send_message(socket_cores_t *, int, int, size_t *, int);
-static int tcp_accept_message(socket_cores_t *, int, int, size_t *, size_t *);
-static int tcp_close_message(socket_cores_t *, int);
-
-/** TCP global data. */
-tcp_globals_t tcp_globals;
-
-int tcp_received_msg(nic_device_id_t device_id, packet_t *packet,
-    services_t receiver, services_t error)
+#include "test.h"
+
+#define NAME       "tcp"
+
+async_sess_t *net_sess;
+static async_sess_t *icmp_sess;
+async_sess_t *ip_sess;
+packet_dimensions_t pkt_dims;
+
+static void tcp_received_pdu(tcp_pdu_t *pdu);
+
+/* Pull up packets into a single memory block. */
+static int pq_pullup(packet_t *packet, void **data, size_t *dsize)
+{
+	packet_t *npacket;
+	size_t tot_len;
+	int length;
+
+	npacket = packet;
+	tot_len = 0;
+	do {
+		length = packet_get_data_length(packet);
+		if (length <= 0)
+			return EINVAL;
+
+		tot_len += length;
+	} while ((npacket = pq_next(npacket)) != NULL);
+
+	uint8_t *buf;
+	uint8_t *dp;
+
+	buf = calloc(tot_len, 1);
+	if (buf == NULL) {
+		free(buf);
+		return ENOMEM;
+	}
+
+	npacket = packet;
+	dp = buf;
+	do {
+		length = packet_get_data_length(packet);
+		if (length <= 0) {
+			free(buf);
+			return EINVAL;
+		}
+
+		memcpy(dp, packet_get_data(packet), length);
+		dp += length;
+	} while ((npacket = pq_next(npacket)) != NULL);
+
+	*data = buf;
+	*dsize = tot_len;
+	return EOK;
+}
+
+/** Process packet received from network layer. */
+static int tcp_received_msg(nic_device_id_t device_id, packet_t *packet,
+    services_t error)
 {
 	int rc;
-
-	if (receiver != SERVICE_TCP)
-		return EREFUSED;
-
-	fibril_rwlock_write_lock(&tcp_globals.lock);
-	rc = tcp_process_packet(device_id, packet, error);
-	if (rc != EOK)
-		fibril_rwlock_write_unlock(&tcp_globals.lock);
-
-	printf("receive %d \n", rc);
-
-	return rc;
-}
-
-int tcp_process_packet(nic_device_id_t device_id, packet_t *packet, services_t error)
-{
-	size_t length;
 	size_t offset;
-	int result;
-	tcp_header_t *header;
-	socket_core_t *socket;
-	tcp_socket_data_t *socket_data;
-	packet_t *next_packet;
-	size_t total_length;
-	uint32_t checksum;
-	int fragments;
-	icmp_type_t type;
-	icmp_code_t code;
-	struct sockaddr *src;
-	struct sockaddr *dest;
-	size_t addrlen;
-	int rc;
+	int length;
+	struct sockaddr_in *src_addr;
+	struct sockaddr_in *dest_addr;
+	size_t addr_len;
+
+	log_msg(LVL_DEBUG, "tcp_received_msg()");
 
 	switch (error) {
@@ -261,2256 +130,297 @@
 		break;
 	case SERVICE_ICMP:
-		/* Process error */
-		result = icmp_client_process_packet(packet, &type, &code, NULL,
-		    NULL);
-		if (result < 0)
-			return tcp_release_and_return(packet, result);
-
-		length = (size_t) result;
-		rc = packet_trim(packet, length, 0);
-		if (rc != EOK)
-			return tcp_release_and_return(packet, rc);
-		break;
 	default:
-		return tcp_release_and_return(packet, ENOTSUP);
-	}
-
-	/* TODO process received ipopts? */
-	result = ip_client_process_packet(packet, NULL, NULL, NULL, NULL, NULL);
-	if (result < 0)
-		return tcp_release_and_return(packet, result);
-
-	offset = (size_t) result;
-
+		log_msg(LVL_WARN, "Unsupported service number %u",
+		    (unsigned)error);
+		pq_release_remote(net_sess, packet_get_id(packet));
+		return ENOTSUP;
+	}
+
+	/* Process and trim off IP header */
+	log_msg(LVL_DEBUG, "tcp_received_msg() - IP header");
+
+	rc = ip_client_process_packet(packet, NULL, NULL, NULL, NULL, NULL);
+	if (rc < 0) {
+		log_msg(LVL_WARN, "ip_client_process_packet() failed");
+		pq_release_remote(net_sess, packet_get_id(packet));
+		return rc;
+	}
+
+	offset = (size_t)rc;
 	length = packet_get_data_length(packet);
-	if (length <= 0)
-		return tcp_release_and_return(packet, EINVAL);
-
-	if (length < TCP_HEADER_SIZE + offset)
-		return tcp_release_and_return(packet, NO_DATA);
-
-	/* Trim all but TCP header */
+
+	if (length < 0 || (size_t)length < offset) {
+		log_msg(LVL_WARN, "length=%d, dropping.", length);
+		pq_release_remote(net_sess, packet_get_id(packet));
+		return EINVAL;
+	}
+
+	addr_len = packet_get_addr(packet, (uint8_t **)&src_addr,
+	    (uint8_t **)&dest_addr);
+	if (addr_len <= 0) {
+		log_msg(LVL_WARN, "Failed to get packet address.");
+		pq_release_remote(net_sess, packet_get_id(packet));
+		return EINVAL;
+	}
+
+	if (addr_len != sizeof(struct sockaddr_in)) {
+		log_msg(LVL_WARN, "Unsupported address size %zu (!= %zu)",
+		    addr_len, sizeof(struct sockaddr_in));
+		pq_release_remote(net_sess, packet_get_id(packet));
+		return EINVAL;
+	}
+
 	rc = packet_trim(packet, offset, 0);
-	if (rc != EOK)
-		return tcp_release_and_return(packet, rc);
-
-	/* Get tcp header */
-	header = (tcp_header_t *) packet_get_data(packet);
-	if (!header)
-		return tcp_release_and_return(packet, NO_DATA);
-
-#if 0
-	printf("header len %d, port %d \n", TCP_HEADER_LENGTH(header),
-	    ntohs(header->destination_port));
-#endif
-	result = packet_get_addr(packet, (uint8_t **) &src, (uint8_t **) &dest);
-	if (result <= 0)
-		return tcp_release_and_return(packet, result);
-
-	addrlen = (size_t) result;
-
-	rc = tl_set_address_port(src, addrlen, ntohs(header->source_port));
-	if (rc != EOK)
-		return tcp_release_and_return(packet, rc);
-	
-	/* Find the destination socket */
-	socket = socket_port_find(&tcp_globals.sockets,
-	    ntohs(header->destination_port), (uint8_t *) src, addrlen);
-	if (!socket) {
-		/* Find the listening destination socket */
-		socket = socket_port_find(&tcp_globals.sockets,
-		    ntohs(header->destination_port),
-		    (uint8_t *) SOCKET_MAP_KEY_LISTENING, 0);
-	}
-
-	if (!socket) {
-		if (tl_prepare_icmp_packet(tcp_globals.net_sess,
-		    tcp_globals.icmp_sess, packet, error) == EOK) {
-			icmp_destination_unreachable_msg(tcp_globals.icmp_sess,
-			    ICMP_PORT_UNREACH, 0, packet);
-		}
-		return EADDRNOTAVAIL;
-	}
-
-	printf("socket id %d\n", socket->socket_id);
-	socket_data = (tcp_socket_data_t *) socket->specific_data;
-	assert(socket_data);
-
-	/* Some data received, clear the timeout counter */
-	socket_data->timeout_count = 0;
-
-	/* Count the received packet fragments */
-	next_packet = packet;
-	fragments = 0;
-	checksum = 0;
-	total_length = 0;
-	do {
-		fragments++;
-		length = packet_get_data_length(next_packet);
-		if (length <= 0)
-			return tcp_release_and_return(packet, NO_DATA);
-
-		total_length += length;
-
-		/* Add partial checksum if set */
-		if (!error) {
-			checksum = compute_checksum(checksum,
-			    packet_get_data(packet),
-			    packet_get_data_length(packet));
-		}
-
-	} while ((next_packet = pq_next(next_packet)));
-
-	fibril_rwlock_write_lock(socket_data->local_lock);
-
-	if (error)
-		goto has_error_service;
-	
-	if (socket_data->state == TCP_SOCKET_LISTEN) {
-		if (socket_data->pseudo_header) {
-			free(socket_data->pseudo_header);
-			socket_data->pseudo_header = NULL;
-			socket_data->headerlen = 0;
-		}
-
-		rc = ip_client_get_pseudo_header(IPPROTO_TCP, src, addrlen,
-		    dest, addrlen, total_length, &socket_data->pseudo_header,
-		    &socket_data->headerlen);
-		if (rc != EOK) {
-			fibril_rwlock_write_unlock(socket_data->local_lock);
-			return tcp_release_and_return(packet, rc);
-		}
-	} else {
-		rc = ip_client_set_pseudo_header_data_length(
-		    socket_data->pseudo_header, socket_data->headerlen,
-		    total_length);
-		if (rc != EOK) {
-			fibril_rwlock_write_unlock(socket_data->local_lock);
-			return tcp_release_and_return(packet, rc);
-		}
-	}
-	
-	checksum = compute_checksum(checksum, socket_data->pseudo_header,
-	    socket_data->headerlen);
-	if (flip_checksum(compact_checksum(checksum)) != IP_CHECKSUM_ZERO) {
-		printf("checksum err %x -> %x\n", header->checksum,
-		    flip_checksum(compact_checksum(checksum)));
-		fibril_rwlock_write_unlock(socket_data->local_lock);
-
-		rc = tl_prepare_icmp_packet(tcp_globals.net_sess,
-		    tcp_globals.icmp_sess, packet, error);
-		if (rc == EOK) {
-			/* Checksum error ICMP */
-			icmp_parameter_problem_msg(tcp_globals.icmp_sess,
-			    ICMP_PARAM_POINTER,
-			    ((size_t) ((void *) &header->checksum)) -
-			    ((size_t) ((void *) header)), packet);
-		}
-
+	if (rc != EOK) {
+		log_msg(LVL_WARN, "Failed to trim packet.");
+		pq_release_remote(net_sess, packet_get_id(packet));
+		return rc;
+	}
+
+	/* Pull up packets into a single memory block, pdu_raw. */
+	log_msg(LVL_DEBUG, "tcp_received_msg() - pull up");
+	uint8_t *pdu_raw;
+	size_t pdu_raw_size = 0;
+
+	pq_pullup(packet, (void **)&pdu_raw, &pdu_raw_size);
+
+	/* Split into header and payload. */
+
+	log_msg(LVL_DEBUG, "tcp_received_msg() - split header/payload");
+
+	tcp_pdu_t *pdu;
+	size_t hdr_size;
+
+	/* XXX Header options */
+	hdr_size = sizeof(tcp_header_t);
+
+	if (pdu_raw_size < hdr_size) {
+		log_msg(LVL_WARN, "pdu_raw_size = %zu < hdr_size = %zu",
+		    pdu_raw_size, hdr_size);
+		pq_release_remote(net_sess, packet_get_id(packet));
 		return EINVAL;
 	}
 
-has_error_service:
-	fibril_rwlock_write_unlock(&tcp_globals.lock);
-
-	/* TODO error reporting/handling */
-	switch (socket_data->state) {
-	case TCP_SOCKET_LISTEN:
-		rc = tcp_process_listen(socket, socket_data, header, packet,
-		    src, dest, addrlen);
-		break;
-	case TCP_SOCKET_SYN_RECEIVED:
-		rc = tcp_process_syn_received(socket, socket_data, header,
-		    packet);
-		break;
-	case TCP_SOCKET_SYN_SENT:
-		rc = tcp_process_syn_sent(socket, socket_data, header, packet);
-		break;
-	case TCP_SOCKET_FIN_WAIT_1:
-		/* ack changing the state to FIN_WAIT_2 gets processed later */
-	case TCP_SOCKET_FIN_WAIT_2:
-		/* fin changing state to LAST_ACK gets processed later */
-	case TCP_SOCKET_LAST_ACK:
-		/* ack releasing the socket get processed later */
-	case TCP_SOCKET_CLOSING:
-		/* ack releasing the socket gets processed later */
-	case TCP_SOCKET_ESTABLISHED:
-		rc = tcp_process_established(socket, socket_data, header,
-		    packet, fragments, total_length);
-		break;
-	default:
-		pq_release_remote(tcp_globals.net_sess, packet_get_id(packet));
-	}
-
-	if (rc != EOK) {
-		fibril_rwlock_write_unlock(socket_data->local_lock);
-		printf("process %d\n", rc);
-	}
+	log_msg(LVL_DEBUG, "pdu_raw_size=%zu, hdr_size=%zu",
+	    pdu_raw_size, hdr_size);
+	pdu = tcp_pdu_create(pdu_raw, hdr_size, pdu_raw + hdr_size,
+	    pdu_raw_size - hdr_size);
+	if (pdu == NULL) {
+		log_msg(LVL_WARN, "Failed creating PDU. Dropped.");
+		return ENOMEM;
+	}
+
+	free(pdu_raw);
+
+	pdu->src_addr.ipv4 = uint32_t_be2host(src_addr->sin_addr.s_addr);
+	pdu->dest_addr.ipv4 = uint32_t_be2host(dest_addr->sin_addr.s_addr);
+	log_msg(LVL_DEBUG, "src: 0x%08x, dest: 0x%08x",
+	    pdu->src_addr.ipv4, pdu->dest_addr.ipv4);
+
+	tcp_received_pdu(pdu);
+	tcp_pdu_delete(pdu);
 
 	return EOK;
 }
 
-int tcp_process_established(socket_core_t *socket, tcp_socket_data_t *
-    socket_data, tcp_header_t *header, packet_t *packet, int fragments,
-    size_t total_length)
-{
-	packet_t *next_packet;
-	packet_t *tmp_packet;
-	uint32_t old_incoming;
-	size_t order;
-	uint32_t sequence_number;
-	size_t length;
-	size_t offset;
-	uint32_t new_sequence_number;
-	bool forced_ack;
-	int rc;
-
-	assert(socket);
-	assert(socket_data);
-	assert(socket->specific_data == socket_data);
-	assert(header);
-	assert(packet);
-
-	forced_ack = false;
-
-	new_sequence_number = ntohl(header->sequence_number);
-	old_incoming = socket_data->next_incoming;
-
-	if (GET_TCP_HEADER_FINALIZE(header)) {
-		socket_data->fin_incoming = new_sequence_number +
-		    total_length - TCP_HEADER_LENGTH(header);
-	}
-
-	/* Trim begining if containing expected data */
-	if (IS_IN_INTERVAL_OVERFLOW(new_sequence_number,
-	    socket_data->next_incoming, new_sequence_number + total_length)) {
-
-		/* Get the acknowledged offset */
-		if (socket_data->next_incoming < new_sequence_number) {
-			offset = new_sequence_number -
-			    socket_data->next_incoming;
-		} else {
-			offset = socket_data->next_incoming -
-			    new_sequence_number;
-		}
-
-		new_sequence_number += offset;
-		total_length -= offset;
-		length = packet_get_data_length(packet);
-
-		/* Trim the acknowledged data */
-		while (length <= offset) {
-			/* Release the acknowledged packets */
-			next_packet = pq_next(packet);
-			pq_release_remote(tcp_globals.net_sess,
-			    packet_get_id(packet));
-			packet = next_packet;
-			offset -= length;
-			length = packet_get_data_length(packet);
-		}
-
-		if (offset > 0) {
-			rc = packet_trim(packet, offset, 0);
-			if (rc != EOK)
-				return tcp_release_and_return(packet, rc);
-		}
-
-		assert(new_sequence_number == socket_data->next_incoming);
-	}
-
-	/* Release if overflowing the window */
-/*
-	if (IS_IN_INTERVAL_OVERFLOW(socket_data->next_incoming +
-	    socket_data->window, new_sequence_number, new_sequence_number +
-	    total_length)) {
-		return tcp_release_and_return(packet, EOVERFLOW);
-	}
-
-	// trim end if overflowing the window
-	if (IS_IN_INTERVAL_OVERFLOW(new_sequence_number,
-	    socket_data->next_incoming + socket_data->window,
-	    new_sequence_number + total_length)) {
-		// get the allowed data length
-		if (socket_data->next_incoming + socket_data->window <
-		    new_sequence_number) {
-			offset = new_sequence_number -
-			    socket_data->next_incoming + socket_data->window;
-		} else {
-			offset = socket_data->next_incoming +
-			    socket_data->window - new_sequence_number;
-		}
-		next_packet = packet;
-		// trim the overflowing data
-		while (next_packet && (offset > 0)) {
-			length = packet_get_data_length(packet);
-			if (length <= offset)
-				next_packet = pq_next(next_packet);
-			else {
-				rc = packet_trim(next_packet, 0,
-				    length - offset));
-				if (rc != EOK)
-					return tcp_release_and_return(packet,
-					    rc);
-			}
-			offset -= length;
-			total_length -= length - offset;
-		}
-		// release the overflowing packets
-		next_packet = pq_next(next_packet);
-		if (next_packet) {
-			tmp_packet = next_packet;
-			next_packet = pq_next(next_packet);
-			pq_insert_after(tmp_packet, next_packet);
-			pq_release_remote(tcp_globals.net_sess,
-			    packet_get_id(tmp_packet));
-		}
-		assert(new_sequence_number + total_length ==
-		    socket_data->next_incoming + socket_data->window);
-	}
-*/
-	/* The expected one arrived? */
-	if (new_sequence_number == socket_data->next_incoming) {
-		printf("expected\n");
-		/* Process acknowledgement */
-		tcp_process_acknowledgement(socket, socket_data, header);
-
-		/* Remove the header */
-		total_length -= TCP_HEADER_LENGTH(header);
-		rc = packet_trim(packet, TCP_HEADER_LENGTH(header), 0);
-		if (rc != EOK)
-			return tcp_release_and_return(packet, rc);
-
-		if (total_length) {
-			rc = tcp_queue_received_packet(socket, socket_data,
-			    packet, fragments, total_length);
-			if (rc != EOK)
-				return rc;
-		} else {
-			total_length = 1;
-		}
-
-		socket_data->next_incoming = old_incoming + total_length;
-		packet = socket_data->incoming;
-		while (packet) {
-			rc = pq_get_order(socket_data->incoming, &order, NULL);
-			if (rc != EOK) {
-				/* Remove the corrupted packet */
-				next_packet = pq_detach(packet);
-				if (packet == socket_data->incoming)
-					socket_data->incoming = next_packet;
-				pq_release_remote(tcp_globals.net_sess,
-				    packet_get_id(packet));
-				packet = next_packet;
-				continue;
-			}
-
-			sequence_number = (uint32_t) order;
-			if (IS_IN_INTERVAL_OVERFLOW(sequence_number,
-			    old_incoming, socket_data->next_incoming)) {
-				/* Move to the next */
-				packet = pq_next(packet);
-				/* Coninual data? */
-			} else if (IS_IN_INTERVAL_OVERFLOW(old_incoming,
-			    sequence_number, socket_data->next_incoming)) {
-				/* Detach the packet */
-				next_packet = pq_detach(packet);
-				if (packet == socket_data->incoming)
-					socket_data->incoming = next_packet;
-				/* Get data length */
-				length = packet_get_data_length(packet);
-				new_sequence_number = sequence_number + length;
-				if (length <= 0) {
-					/* Remove the empty packet */
-					pq_release_remote(tcp_globals.net_sess,
-					    packet_get_id(packet));
-					packet = next_packet;
-					continue;
-				}
-				/* Exactly following */
-				if (sequence_number ==
-				    socket_data->next_incoming) {
-					/* Queue received data */
-					rc = tcp_queue_received_packet(socket,
-					    socket_data, packet, 1,
-					    packet_get_data_length(packet));
-					if (rc != EOK)
-						return rc;
-					socket_data->next_incoming =
-					    new_sequence_number;
-					packet = next_packet;
-					continue;
-					/* At least partly following data? */
-				}
-				if (IS_IN_INTERVAL_OVERFLOW(sequence_number,
-				    socket_data->next_incoming, new_sequence_number)) {
-					if (socket_data->next_incoming <
-					    new_sequence_number) {
-						length = new_sequence_number -
-						    socket_data->next_incoming;
-					} else {
-						length =
-						    socket_data->next_incoming -
-						    new_sequence_number;
-					}
-					rc = packet_trim(packet,length, 0);
-					if (rc == EOK) {
-						/* Queue received data */
-						rc = tcp_queue_received_packet(
-						    socket, socket_data, packet,
-						    1, packet_get_data_length(
-						    packet));
-						if (rc != EOK)
-							return rc;
-						socket_data->next_incoming =
-						    new_sequence_number;
-						packet = next_packet;
-						continue;
-					}
-				}
-				/* Remove the duplicit or corrupted packet */
-				pq_release_remote(tcp_globals.net_sess,
-				    packet_get_id(packet));
-				packet = next_packet;
-				continue;
-			} else {
-				break;
-			}
-		}
-	} else if (IS_IN_INTERVAL(socket_data->next_incoming,
-	    new_sequence_number,
-	    socket_data->next_incoming + socket_data->window)) {
-		printf("in window\n");
-		/* Process acknowledgement */
-		tcp_process_acknowledgement(socket, socket_data, header);
-
-		/* Remove the header */
-		total_length -= TCP_HEADER_LENGTH(header);
-		rc = packet_trim(packet, TCP_HEADER_LENGTH(header), 0);
-		if (rc != EOK)
-			return tcp_release_and_return(packet, rc);
-
-		next_packet = pq_detach(packet);
-		length = packet_get_data_length(packet);
-		rc = pq_add(&socket_data->incoming, packet, new_sequence_number,
-		    length);
-		if (rc != EOK) {
-			/* Remove the corrupted packets */
-			pq_release_remote(tcp_globals.net_sess,
-			    packet_get_id(packet));
-			pq_release_remote(tcp_globals.net_sess,
-			    packet_get_id(next_packet));
-		} else {
-			while (next_packet) {
-				new_sequence_number += length;
-				tmp_packet = pq_detach(next_packet);
-				length = packet_get_data_length(next_packet);
-
-				rc = pq_set_order(next_packet,
-				    new_sequence_number, length);
-				if (rc != EOK) {
-					pq_release_remote(tcp_globals.net_sess,
-					    packet_get_id(next_packet));
-				}
-				rc = pq_insert_after(packet, next_packet);
-				if (rc != EOK) {
-					pq_release_remote(tcp_globals.net_sess,
-					    packet_get_id(next_packet));
-				}
-				next_packet = tmp_packet;
-			}
-		}
-	} else {
-		printf("unexpected\n");
-		/* Release duplicite or restricted */
-		pq_release_remote(tcp_globals.net_sess, packet_get_id(packet));
-		forced_ack = true;
-	}
-
-	/* If next in sequence is an incoming FIN */
-	if (socket_data->next_incoming == socket_data->fin_incoming) {
-		/* Advance sequence number */
-		socket_data->next_incoming += 1;
-
-		/* Handle FIN */
-		switch (socket_data->state) {
-		case TCP_SOCKET_FIN_WAIT_1:
-		case TCP_SOCKET_FIN_WAIT_2:
-		case TCP_SOCKET_CLOSING:
-			socket_data->state = TCP_SOCKET_CLOSING;
-			break;
-		case TCP_SOCKET_ESTABLISHED:
-			/* Queue end-of-data marker on the socket. */
-			tcp_queue_received_end_of_data(socket);
-			socket_data->state = TCP_SOCKET_CLOSE_WAIT;
-			break;
-		default:
-			socket_data->state = TCP_SOCKET_CLOSE_WAIT;
-			break;
-		}
-	}
-
-	packet = tcp_get_packets_to_send(socket, socket_data);
-	if (!packet && (socket_data->next_incoming != old_incoming || forced_ack)) {
-		/* Create the notification packet */
-		rc = tcp_create_notification_packet(&packet, socket,
-		    socket_data, 0, 0);
-		if (rc != EOK)
-			return rc;
-		rc = tcp_queue_prepare_packet(socket, socket_data, packet, 1);
-		if (rc != EOK)
-			return rc;
-		packet = tcp_send_prepare_packet(socket, socket_data, packet, 1,
-		    socket_data->last_outgoing + 1);
-	}
-
-	fibril_rwlock_write_unlock(socket_data->local_lock);
-
-	/* Send the packet */
-	tcp_send_packets(socket_data->device_id, packet);
-
-	return EOK;
-}
-
-int tcp_queue_received_packet(socket_core_t *socket,
-    tcp_socket_data_t *socket_data, packet_t *packet, int fragments,
-    size_t total_length)
-{
-	packet_dimension_t *packet_dimension;
-	int rc;
-
-	assert(socket);
-	assert(socket_data);
-	assert(socket->specific_data == socket_data);
-	assert(packet);
-	assert(fragments >= 1);
-	assert(socket_data->window > total_length);
-
-	/* Queue the received packet */
-	rc = dyn_fifo_push(&socket->received, packet_get_id(packet),
-	    SOCKET_MAX_RECEIVED_SIZE);
-	if (rc != EOK)
-		return tcp_release_and_return(packet, rc);
-	rc = tl_get_ip_packet_dimension(tcp_globals.ip_sess,
-	    &tcp_globals.dimensions, socket_data->device_id, &packet_dimension);
-	if (rc != EOK)
-		return tcp_release_and_return(packet, rc);
-
-	/* Decrease the window size */
-	socket_data->window -= total_length;
-
-	/* Notify the destination socket */
-	async_exch_t *exch = async_exchange_begin(socket->sess);
-	async_msg_5(exch, NET_SOCKET_RECEIVED, (sysarg_t) socket->socket_id,
-	    ((packet_dimension->content < socket_data->data_fragment_size) ?
-	    packet_dimension->content : socket_data->data_fragment_size), 0, 0,
-	    (sysarg_t) fragments);
-	async_exchange_end(exch);
-
-	return EOK;
-}
-
-/** Queue end-of-data marker on the socket.
- *
- * Next element in the sequence space is FIN. Queue end-of-data marker
- * on the socket.
- *
- * @param socket	Socket
- */
-static void tcp_queue_received_end_of_data(socket_core_t *socket)
-{
-	assert(socket != NULL);
-
-	/* Notify the destination socket */
-	async_exch_t *exch = async_exchange_begin(socket->sess);
-	async_msg_5(exch, NET_SOCKET_RECEIVED, (sysarg_t) socket->socket_id,
-	    0, 0, 0, (sysarg_t) 0 /* 0 fragments == no more data */);
-	async_exchange_end(exch);
-}
-
-int tcp_process_syn_sent(socket_core_t *socket, tcp_socket_data_t *
-    socket_data, tcp_header_t *header, packet_t *packet)
-{
-	packet_t *next_packet;
-	int rc;
-
-	assert(socket);
-	assert(socket_data);
-	assert(socket->specific_data == socket_data);
-	assert(header);
-	assert(packet);
-
-	if (!GET_TCP_HEADER_SYNCHRONIZE(header))
-		return tcp_release_and_return(packet, EINVAL);
-	
-	/* Process acknowledgement */
-	tcp_process_acknowledgement(socket, socket_data, header);
-
-	socket_data->next_incoming = ntohl(header->sequence_number) + 1;
-
-	/* Release additional packets */
-	next_packet = pq_detach(packet);
-	if (next_packet) {
-		pq_release_remote(tcp_globals.net_sess,
-		    packet_get_id(next_packet));
-	}
-
-	/* Trim if longer than the header */
-	if (packet_get_data_length(packet) > sizeof(*header)) {
-		rc = packet_trim(packet, 0,
-		    packet_get_data_length(packet) - sizeof(*header));
-		if (rc != EOK)
-			return tcp_release_and_return(packet, rc);
-	}
-
-	tcp_prepare_operation_header(socket, socket_data, header, 0, 0);
-	fibril_mutex_lock(&socket_data->operation.mutex);
-	socket_data->operation.result = tcp_queue_packet(socket, socket_data,
-	    packet, 1);
-
-	if (socket_data->operation.result == EOK) {
-		socket_data->state = TCP_SOCKET_ESTABLISHED;
-		packet = tcp_get_packets_to_send(socket, socket_data);
-		if (packet) {
-			fibril_rwlock_write_unlock( socket_data->local_lock);
-			/* Send the packet */
-			tcp_send_packets(socket_data->device_id, packet);
-			/* Signal the result */
-			fibril_condvar_signal( &socket_data->operation.condvar);
-			fibril_mutex_unlock( &socket_data->operation.mutex);
-			return EOK;
-		}
-	}
-
-	fibril_mutex_unlock(&socket_data->operation.mutex);
-	return tcp_release_and_return(packet, EINVAL);
-}
-
-int tcp_process_listen(socket_core_t *listening_socket,
-    tcp_socket_data_t *listening_socket_data, tcp_header_t *header,
-    packet_t *packet, struct sockaddr *src, struct sockaddr *dest,
-    size_t addrlen)
-{
-	packet_t *next_packet;
-	socket_core_t *socket;
-	tcp_socket_data_t *socket_data;
-	int socket_id;
-	int listening_socket_id = listening_socket->socket_id;
-	int listening_port = listening_socket->port;
-	int rc;
-
-	assert(listening_socket);
-	assert(listening_socket_data);
-	assert(listening_socket->specific_data == listening_socket_data);
-	assert(header);
-	assert(packet);
-
-	if (!GET_TCP_HEADER_SYNCHRONIZE(header))
-		return tcp_release_and_return(packet, EINVAL);
-
-	socket_data = (tcp_socket_data_t *) malloc(sizeof(*socket_data));
-	if (!socket_data)
-		return tcp_release_and_return(packet, ENOMEM);
-
-	tcp_initialize_socket_data(socket_data);
-	socket_data->local_lock = listening_socket_data->local_lock;
-	socket_data->local_sockets = listening_socket_data->local_sockets;
-	socket_data->listening_socket_id = listening_socket->socket_id;
-	socket_data->next_incoming = ntohl(header->sequence_number);
-	socket_data->treshold = socket_data->next_incoming +
-	    ntohs(header->window);
-	socket_data->addrlen = addrlen;
-	socket_data->addr = malloc(socket_data->addrlen);
-	if (!socket_data->addr) {
-		free(socket_data);
-		return tcp_release_and_return(packet, ENOMEM);
-	}
-
-	memcpy(socket_data->addr, src, socket_data->addrlen);
-	socket_data->dest_port = ntohs(header->source_port);
-	rc = tl_set_address_port(socket_data->addr, socket_data->addrlen,
-	    socket_data->dest_port);
-	if (rc != EOK) {
-		free(socket_data->addr);
-		free(socket_data);
-		return tcp_release_and_return(packet, rc);
-	}
-
-	/* Create a socket */
-	socket_id = -1;
-	rc = socket_create(socket_data->local_sockets, listening_socket->sess,
-	    socket_data, &socket_id);
-	if (rc != EOK) {
-		free(socket_data->addr);
-		free(socket_data);
-		return tcp_release_and_return(packet, rc);
-	}
-
-	printf("new_sock %d\n", socket_id);
-	socket_data->pseudo_header = listening_socket_data->pseudo_header;
-	socket_data->headerlen = listening_socket_data->headerlen;
-	listening_socket_data->pseudo_header = NULL;
-	listening_socket_data->headerlen = 0;
-
-	fibril_rwlock_write_unlock(socket_data->local_lock);
-	fibril_rwlock_write_lock(&tcp_globals.lock);
-
-	/* Find the destination socket */
-	listening_socket = socket_port_find(&tcp_globals.sockets,
-	    listening_port, (uint8_t *) SOCKET_MAP_KEY_LISTENING, 0);
-	if (!listening_socket ||
-	    (listening_socket->socket_id != listening_socket_id)) {
-		fibril_rwlock_write_unlock(&tcp_globals.lock);
-		/* A shadow may remain until app hangs up */
-		return tcp_release_and_return(packet, EOK /*ENOTSOCK*/);
-	}
-	listening_socket_data =
-	    (tcp_socket_data_t *) listening_socket->specific_data;
-	assert(listening_socket_data);
-
-	fibril_rwlock_write_lock(listening_socket_data->local_lock);
-
-	socket = socket_cores_find(listening_socket_data->local_sockets,
-	    socket_id);
-	if (!socket) {
-		/* Where is the socket?!? */
-		fibril_rwlock_write_unlock(&tcp_globals.lock);
-		return ENOTSOCK;
-	}
-	socket_data = (tcp_socket_data_t *) socket->specific_data;
-	assert(socket_data);
-
-	rc = socket_port_add(&tcp_globals.sockets, listening_port, socket,
-	    (uint8_t *) socket_data->addr, socket_data->addrlen);
-	assert(socket == socket_port_find(&tcp_globals.sockets, listening_port,
-	    (uint8_t *) socket_data->addr, socket_data->addrlen));
-
-//	rc = socket_bind_free_port(&tcp_globals.sockets, socket,
-//	    TCP_FREE_PORTS_START, TCP_FREE_PORTS_END,
-//	    tcp_globals.last_used_port);
-//	tcp_globals.last_used_port = socket->port;
-	fibril_rwlock_write_unlock(&tcp_globals.lock);
-	if (rc != EOK) {
-		socket_destroy(tcp_globals.net_sess, socket->socket_id,
-		    socket_data->local_sockets, &tcp_globals.sockets,
-		    tcp_free_socket_data);
-		return tcp_release_and_return(packet, rc);
-	}
-
-	socket_data->state = TCP_SOCKET_LISTEN;
-	socket_data->next_incoming = ntohl(header->sequence_number) + 1;
-
-	/* Release additional packets */
-	next_packet = pq_detach(packet);
-	if (next_packet) {
-		pq_release_remote(tcp_globals.net_sess,
-		    packet_get_id(next_packet));
-	}
-
-	/* Trim if longer than the header */
-	if (packet_get_data_length(packet) > sizeof(*header)) {
-		rc = packet_trim(packet, 0,
-		    packet_get_data_length(packet) - sizeof(*header));
-		if (rc != EOK) {
-			socket_destroy(tcp_globals.net_sess, socket->socket_id,
-			    socket_data->local_sockets, &tcp_globals.sockets,
-			    tcp_free_socket_data);
-			return tcp_release_and_return(packet, rc);
-		}
-	}
-
-	tcp_prepare_operation_header(socket, socket_data, header, 1, 0);
-
-	rc = tcp_queue_packet(socket, socket_data, packet, 1);
-	if (rc != EOK) {
-		socket_destroy(tcp_globals.net_sess, socket->socket_id,
-		    socket_data->local_sockets, &tcp_globals.sockets,
-		    tcp_free_socket_data);
-		return rc;
-	}
-
-	packet = tcp_get_packets_to_send(socket, socket_data);
-	if (!packet) {
-		socket_destroy(tcp_globals.net_sess, socket->socket_id,
-		    socket_data->local_sockets, &tcp_globals.sockets,
-		    tcp_free_socket_data);
-		return EINVAL;
-	}
-
-	socket_data->state = TCP_SOCKET_SYN_RECEIVED;
-	fibril_rwlock_write_unlock(socket_data->local_lock);
-
-	/* Send the packet */
-	tcp_send_packets(socket_data->device_id, packet);
-
-	return EOK;
-}
-
-int tcp_process_syn_received(socket_core_t *socket,
-    tcp_socket_data_t *socket_data, tcp_header_t *header, packet_t *packet)
-{
-	socket_core_t *listening_socket;
-	tcp_socket_data_t *listening_socket_data;
-	int rc;
-
-	assert(socket);
-	assert(socket_data);
-	assert(socket->specific_data == socket_data);
-	assert(header);
-	assert(packet);
-
-	if (!GET_TCP_HEADER_ACKNOWLEDGE(header))
-		return tcp_release_and_return(packet, EINVAL);
-
-	/* Process acknowledgement */
-	tcp_process_acknowledgement(socket, socket_data, header);
-
-	socket_data->next_incoming = ntohl(header->sequence_number); /* + 1; */
-	pq_release_remote(tcp_globals.net_sess, packet_get_id(packet));
-	socket_data->state = TCP_SOCKET_ESTABLISHED;
-	listening_socket = socket_cores_find(socket_data->local_sockets,
-	    socket_data->listening_socket_id);
-	if (listening_socket) {
-		listening_socket_data =
-		    (tcp_socket_data_t *) listening_socket->specific_data;
-		assert(listening_socket_data);
-
-		/* Queue the received packet */
-		rc = dyn_fifo_push(&listening_socket->accepted,
-		    (-1 * socket->socket_id), listening_socket_data->backlog);
-		if (rc == EOK) {
-			/* Notify the destination socket */
-			async_exch_t *exch = async_exchange_begin(socket->sess);
-			async_msg_5(exch, NET_SOCKET_ACCEPTED,
-			    (sysarg_t) listening_socket->socket_id,
-			    socket_data->data_fragment_size, TCP_HEADER_SIZE,
-			    0, (sysarg_t) socket->socket_id);
-			async_exchange_end(exch);
-
-			fibril_rwlock_write_unlock(socket_data->local_lock);
-			return EOK;
-		}
-	}
-	/* Send FIN */
-	socket_data->state = TCP_SOCKET_FIN_WAIT_1;
-
-	/* Create the notification packet */
-	rc = tcp_create_notification_packet(&packet, socket, socket_data, 0, 1);
-	if (rc != EOK)
-		return rc;
-
-	/* Send the packet */
-	rc = tcp_queue_packet(socket, socket_data, packet, 1);
-	if (rc != EOK)
-		return rc;
-
-	/* Flush packets */
-	packet = tcp_get_packets_to_send(socket, socket_data);
-	fibril_rwlock_write_unlock(socket_data->local_lock);
-	if (packet) {
-		/* Send the packet */
-		tcp_send_packets(socket_data->device_id, packet);
-	}
-
-	return EOK;
-}
-
-void tcp_process_acknowledgement(socket_core_t *socket,
-    tcp_socket_data_t *socket_data, tcp_header_t *header)
-{
-	size_t number;
-	size_t length;
-	packet_t *packet;
-	packet_t *next;
-	packet_t *acknowledged = NULL;
-	uint32_t old;
-
-	assert(socket);
-	assert(socket_data);
-	assert(socket->specific_data == socket_data);
-	assert(header);
-
-	if (!GET_TCP_HEADER_ACKNOWLEDGE(header))
-		return;
-
-	number = ntohl(header->acknowledgement_number);
-
-	/* If more data acknowledged */
-	if (number != socket_data->expected) {
-		old = socket_data->expected;
-		if (IS_IN_INTERVAL_OVERFLOW(old, socket_data->fin_outgoing,
-		    number)) {
-			switch (socket_data->state) {
-			case TCP_SOCKET_FIN_WAIT_1:
-				socket_data->state = TCP_SOCKET_FIN_WAIT_2;
-				break;
-			case TCP_SOCKET_LAST_ACK:
-			case TCP_SOCKET_CLOSING:
-				/*
-				 * FIN acknowledged - release the socket in
-				 * another fibril.
-				 */
-				tcp_prepare_timeout(tcp_release_after_timeout,
-				    socket, socket_data, 0,
-				    TCP_SOCKET_TIME_WAIT,
-				    NET_DEFAULT_TCP_TIME_WAIT_TIMEOUT, true);
-				break;
-			default:
-				break;
-			}
-		}
-
-		/* Update the treshold if higher than set */
-		if (number + ntohs(header->window) >
-		    socket_data->expected + socket_data->treshold) {
-			socket_data->treshold = number + ntohs(header->window) -
-			    socket_data->expected;
-		}
-
-		/* Set new expected sequence number */
-		socket_data->expected = number;
-		socket_data->expected_count = 1;
-		packet = socket_data->outgoing;
-		while (pq_get_order(packet, &number, &length) == EOK) {
-			if (IS_IN_INTERVAL_OVERFLOW((uint32_t) old,
-			    (uint32_t) (number + length),
-			    (uint32_t) socket_data->expected)) {
-				next = pq_detach(packet);
-				if (packet == socket_data->outgoing)
-					socket_data->outgoing = next;
-
-				/* Add to acknowledged or release */
-				if (pq_add(&acknowledged, packet, 0, 0) != EOK)
-					pq_release_remote(tcp_globals.net_sess,
-					    packet_get_id(packet));
-				packet = next;
-			} else if (old < socket_data->expected)
-				break;
-		}
-
-		/* Release acknowledged */
-		if (acknowledged) {
-			pq_release_remote(tcp_globals.net_sess,
-			    packet_get_id(acknowledged));
-		}
-		return;
-		/* If the same as the previous time */
-	}
-
-	if (number == socket_data->expected) {
-		/* Increase the counter */
-		socket_data->expected_count++;
-		if (socket_data->expected_count == TCP_FAST_RETRANSMIT_COUNT) {
-			socket_data->expected_count = 1;
-			/* TODO retransmit lock */
-			//tcp_retransmit_packet(socket, socket_data, number);
-		}
-	}
-}
-
-/** Per-connection initialization
- *
- */
-void tl_connection(void)
-{
-}
-
-/** Processes the TCP message.
- *
- * @param[in] callid	The message identifier.
- * @param[in] call	The message parameters.
- * @param[out] answer	The message answer parameters.
- * @param[out] answer_count The last parameter for the actual answer in the
- *			answer parameter.
- * @return		EOK on success.
- * @return		ENOTSUP if the message is not known.
- *
- * @see tcp_interface.h
- * @see IS_NET_TCP_MESSAGE()
- */
-int tl_message(ipc_callid_t callid, ipc_call_t *call,
-    ipc_call_t *answer, size_t *answer_count)
-{
-	assert(call);
-	assert(answer);
-	assert(answer_count);
-	
-	*answer_count = 0;
-	
-	async_sess_t *callback =
-	    async_callback_receive_start(EXCHANGE_SERIALIZE, call);
-	if (callback)
-		return tcp_process_client_messages(callback, callid, *call);
-	
-	return ENOTSUP;
-}
-
-void tcp_refresh_socket_data(tcp_socket_data_t *socket_data)
-{
-	assert(socket_data);
-
-	bzero(socket_data, sizeof(*socket_data));
-	socket_data->state = TCP_SOCKET_INITIAL;
-	socket_data->device_id = NIC_DEVICE_INVALID_ID;
-	socket_data->window = NET_DEFAULT_TCP_WINDOW;
-	socket_data->treshold = socket_data->window;
-	socket_data->last_outgoing = TCP_INITIAL_SEQUENCE_NUMBER;
-	socket_data->timeout = NET_DEFAULT_TCP_INITIAL_TIMEOUT;
-	socket_data->acknowledged = socket_data->last_outgoing;
-	socket_data->next_outgoing = socket_data->last_outgoing + 1;
-	socket_data->expected = socket_data->next_outgoing;
-}
-
-void tcp_initialize_socket_data(tcp_socket_data_t *socket_data)
-{
-	assert(socket_data);
-
-	tcp_refresh_socket_data(socket_data);
-	fibril_mutex_initialize(&socket_data->operation.mutex);
-	fibril_condvar_initialize(&socket_data->operation.condvar);
-	socket_data->data_fragment_size = MAX_TCP_FRAGMENT_SIZE;
-}
-
-int tcp_process_client_messages(async_sess_t *sess, ipc_callid_t callid,
-    ipc_call_t call)
-{
-	int res;
-	socket_cores_t local_sockets;
-	struct sockaddr *addr;
-	int socket_id;
-	size_t addrlen;
-	size_t size;
-	fibril_rwlock_t lock;
-	ipc_call_t answer;
-	size_t answer_count;
-	tcp_socket_data_t *socket_data;
-	socket_core_t *socket;
-	packet_dimension_t *packet_dimension;
-
-	/*
-	 * Accept the connection
-	 *  - Answer the first IPC_M_CONNECT_ME_TO call.
-	 */
-	res = EOK;
-	answer_count = 0;
-
-	socket_cores_initialize(&local_sockets);
-	fibril_rwlock_initialize(&lock);
-
-	while (true) {
-
-		/* Answer the call */
-		answer_call(callid, res, &answer, answer_count);
-		/* Refresh data */
-		refresh_answer(&answer, &answer_count);
-		/* Get the next call */
-		callid = async_get_call(&call);
-		
-		if (!IPC_GET_IMETHOD(call)) {
-			res = EHANGUP;
-			break;
-		}
-
-		/* Process the call */
-		switch (IPC_GET_IMETHOD(call)) {
-		case NET_SOCKET:
-			socket_data =
-			    (tcp_socket_data_t *) malloc(sizeof(*socket_data));
-			if (!socket_data) {
-				res = ENOMEM;
-				break;
-			}
-			
-			tcp_initialize_socket_data(socket_data);
-			socket_data->local_lock = &lock;
-			socket_data->local_sockets = &local_sockets;
-			fibril_rwlock_write_lock(&lock);
-			socket_id = SOCKET_GET_SOCKET_ID(call);
-			res = socket_create(&local_sockets, sess,
-			    socket_data, &socket_id);
-			SOCKET_SET_SOCKET_ID(answer, socket_id);
-			fibril_rwlock_write_unlock(&lock);
-			if (res != EOK) {
-				free(socket_data);
-				break;
-			}
-			if (tl_get_ip_packet_dimension(tcp_globals.ip_sess,
-			    &tcp_globals.dimensions, NIC_DEVICE_INVALID_ID,
-			    &packet_dimension) == EOK) {
-				SOCKET_SET_DATA_FRAGMENT_SIZE(answer,
-				    ((packet_dimension->content <
-				    socket_data->data_fragment_size) ?
-				    packet_dimension->content :
-				    socket_data->data_fragment_size));
-			}
-//                      SOCKET_SET_DATA_FRAGMENT_SIZE(answer, MAX_TCP_FRAGMENT_SIZE);
-			SOCKET_SET_HEADER_SIZE(answer, TCP_HEADER_SIZE);
-			answer_count = 3;
-			break;
-
-		case NET_SOCKET_BIND:
-			res = async_data_write_accept((void **) &addr, false,
-			    0, 0, 0, &addrlen);
-			if (res != EOK)
-				break;
-			fibril_rwlock_write_lock(&tcp_globals.lock);
-			fibril_rwlock_write_lock(&lock);
-			res = socket_bind(&local_sockets, &tcp_globals.sockets,
-			    SOCKET_GET_SOCKET_ID(call), addr, addrlen,
-			    TCP_FREE_PORTS_START, TCP_FREE_PORTS_END,
-			    tcp_globals.last_used_port);
-			if (res == EOK) {
-				socket = socket_cores_find(&local_sockets,
-				    SOCKET_GET_SOCKET_ID(call));
-				if (socket) {
-					socket_data = (tcp_socket_data_t *)
-					    socket->specific_data;
-					assert(socket_data);
-					socket_data->state = TCP_SOCKET_LISTEN;
-				}
-			}
-			fibril_rwlock_write_unlock(&lock);
-			fibril_rwlock_write_unlock(&tcp_globals.lock);
-			free(addr);
-			break;
-
-		case NET_SOCKET_LISTEN:
-			fibril_rwlock_read_lock(&tcp_globals.lock);
-//                      fibril_rwlock_write_lock(&tcp_globals.lock);
-			fibril_rwlock_write_lock(&lock);
-			res = tcp_listen_message(&local_sockets,
-			    SOCKET_GET_SOCKET_ID(call),
-			    SOCKET_GET_BACKLOG(call));
-			fibril_rwlock_write_unlock(&lock);
-//                      fibril_rwlock_write_unlock(&tcp_globals.lock);
-			fibril_rwlock_read_unlock(&tcp_globals.lock);
-			break;
-
-		case NET_SOCKET_CONNECT:
-			res = async_data_write_accept((void **) &addr, false,
-			    0, 0, 0, &addrlen);
-			if (res != EOK)
-				break;
-			/*
-			 * The global lock may be released in the
-			 * tcp_connect_message() function.
-			 */
-			fibril_rwlock_write_lock(&tcp_globals.lock);
-			fibril_rwlock_write_lock(&lock);
-			res = tcp_connect_message(&local_sockets,
-			    SOCKET_GET_SOCKET_ID(call), addr, addrlen);
-			if (res != EOK) {
-				fibril_rwlock_write_unlock(&lock);
-				fibril_rwlock_write_unlock(&tcp_globals.lock);
-				free(addr);
-			}
-			break;
-
-		case NET_SOCKET_ACCEPT:
-			fibril_rwlock_read_lock(&tcp_globals.lock);
-			fibril_rwlock_write_lock(&lock);
-			res = tcp_accept_message(&local_sockets,
-			    SOCKET_GET_SOCKET_ID(call),
-			    SOCKET_GET_NEW_SOCKET_ID(call), &size, &addrlen);
-			SOCKET_SET_DATA_FRAGMENT_SIZE(answer, size);
-			fibril_rwlock_write_unlock(&lock);
-			fibril_rwlock_read_unlock(&tcp_globals.lock);
-			if (res > 0) {
-				SOCKET_SET_SOCKET_ID(answer, res);
-				SOCKET_SET_ADDRESS_LENGTH(answer, addrlen);
-				answer_count = 3;
-			}
-			break;
-
-		case NET_SOCKET_SEND:
-			fibril_rwlock_read_lock(&tcp_globals.lock);
-			fibril_rwlock_write_lock(&lock);
-			res = tcp_send_message(&local_sockets,
-			    SOCKET_GET_SOCKET_ID(call),
-			    SOCKET_GET_DATA_FRAGMENTS(call), &size,
-			    SOCKET_GET_FLAGS(call));
-			SOCKET_SET_DATA_FRAGMENT_SIZE(answer, size);
-			if (res != EOK) {
-				fibril_rwlock_write_unlock(&lock);
-				fibril_rwlock_read_unlock(&tcp_globals.lock);
-			} else {
-				answer_count = 2;
-			}
-			break;
-
-		case NET_SOCKET_SENDTO:
-			res = async_data_write_accept((void **) &addr, false,
-			    0, 0, 0, &addrlen);
-			if (res != EOK)
-				break;
-			fibril_rwlock_read_lock(&tcp_globals.lock);
-			fibril_rwlock_write_lock(&lock);
-			res = tcp_send_message(&local_sockets,
-			    SOCKET_GET_SOCKET_ID(call),
-			    SOCKET_GET_DATA_FRAGMENTS(call), &size,
-			    SOCKET_GET_FLAGS(call));
-			SOCKET_SET_DATA_FRAGMENT_SIZE(answer, size);
-			if (res != EOK) {
-				fibril_rwlock_write_unlock(&lock);
-				fibril_rwlock_read_unlock(&tcp_globals.lock);
-			} else {
-				answer_count = 2;
-			}
-			free(addr);
-			break;
-
-		case NET_SOCKET_RECV:
-			fibril_rwlock_read_lock(&tcp_globals.lock);
-			fibril_rwlock_write_lock(&lock);
-			res = tcp_recvfrom_message(&local_sockets,
-			    SOCKET_GET_SOCKET_ID(call), SOCKET_GET_FLAGS(call),
-			    NULL);
-			fibril_rwlock_write_unlock(&lock);
-			fibril_rwlock_read_unlock(&tcp_globals.lock);
-			if (res > 0) {
-				SOCKET_SET_READ_DATA_LENGTH(answer, res);
-				answer_count = 1;
-				res = EOK;
-			}
-			break;
-
-		case NET_SOCKET_RECVFROM:
-			fibril_rwlock_read_lock(&tcp_globals.lock);
-			fibril_rwlock_write_lock(&lock);
-			res = tcp_recvfrom_message(&local_sockets,
-			    SOCKET_GET_SOCKET_ID(call), SOCKET_GET_FLAGS(call),
-			    &addrlen);
-			fibril_rwlock_write_unlock(&lock);
-			fibril_rwlock_read_unlock(&tcp_globals.lock);
-			if (res > 0) {
-				SOCKET_SET_READ_DATA_LENGTH(answer, res);
-				SOCKET_SET_ADDRESS_LENGTH(answer, addrlen);
-				answer_count = 3;
-				res = EOK;
-			}
-			break;
-
-		case NET_SOCKET_CLOSE:
-			fibril_rwlock_write_lock(&tcp_globals.lock);
-			fibril_rwlock_write_lock(&lock);
-			res = tcp_close_message(&local_sockets,
-			    SOCKET_GET_SOCKET_ID(call));
-			if (res != EOK) {
-				fibril_rwlock_write_unlock(&lock);
-				fibril_rwlock_write_unlock(&tcp_globals.lock);
-			}
-			break;
-
-		case NET_SOCKET_GETSOCKOPT:
-		case NET_SOCKET_SETSOCKOPT:
-		default:
-			res = ENOTSUP;
-			break;
-		}
-	}
-
-	/* Release the application session */
-	async_hangup(sess);
-
-	printf("release\n");
-	/* Release all local sockets */
-	socket_cores_release(tcp_globals.net_sess, &local_sockets,
-	    &tcp_globals.sockets, tcp_free_socket_data);
-
-	return EOK;
-}
-
-int tcp_timeout(void *data)
-{
-	tcp_timeout_t *timeout = data;
-	int keep_write_lock = false;
-	socket_core_t *socket;
-	tcp_socket_data_t *socket_data;
-
-	assert(timeout);
-
-	/* Sleep the given timeout */
-	async_usleep(timeout->timeout);
-	/* Lock the globals */
-	if (timeout->globals_read_only) 
-		fibril_rwlock_read_lock(&tcp_globals.lock);
-	else 
-		fibril_rwlock_write_lock(&tcp_globals.lock);
-
-	/* Find the pending operation socket */
-	socket = socket_port_find(&tcp_globals.sockets, timeout->port,
-	    timeout->key, timeout->key_length);
-	if (!socket || (socket->socket_id != timeout->socket_id))
-		goto out;
-	
-	socket_data = (tcp_socket_data_t *) socket->specific_data;
-	assert(socket_data);
-	if (socket_data->local_sockets != timeout->local_sockets)
-		goto out;
-	
-	fibril_rwlock_write_lock(socket_data->local_lock);
-	if (timeout->sequence_number) {
-		/* Increase the timeout counter */
-		socket_data->timeout_count++;
-		if (socket_data->timeout_count == TCP_MAX_TIMEOUTS) {
-			/* TODO release as connection lost */
-			//tcp_refresh_socket_data(socket_data);
-			fibril_rwlock_write_unlock(socket_data->local_lock);
-		} else {
-			/* Retransmit */
-//                      tcp_retransmit_packet(socket,
-//			    socket_data, timeout->sequence_number);
-			fibril_rwlock_write_unlock(socket_data->local_lock);
-		}
-	} else {
-		fibril_mutex_lock(&socket_data->operation.mutex);
-		/* Set the timeout operation result if state not changed */
-		if (socket_data->state == timeout->state) {
-			socket_data->operation.result = ETIMEOUT;
-
-			/* Notify the main fibril */
-			fibril_condvar_signal(&socket_data->operation.condvar);
-
-			/* Keep the global write lock */
-			keep_write_lock = true;
-		} else {
-			/*
-			 * Operation is ok, do nothing.
-			 * Unlocking from now on, so the unlocking
-			 * order does not matter.
-			 */
-			fibril_rwlock_write_unlock(socket_data->local_lock);
-		}
-		fibril_mutex_unlock(&socket_data->operation.mutex);
-	}
-
-out:
-	/* Unlock only if no socket */
-	if (timeout->globals_read_only)
-		fibril_rwlock_read_unlock(&tcp_globals.lock);
-	else if (!keep_write_lock)
-		/* Release if not desired */
-		fibril_rwlock_write_unlock(&tcp_globals.lock);
-	
-	/* Release the timeout structure */
-	free(timeout);
-	return EOK;
-}
-
-int tcp_release_after_timeout(void *data)
-{
-	tcp_timeout_t *timeout = data;
-	socket_core_t *socket;
-	tcp_socket_data_t *socket_data;
-	fibril_rwlock_t *local_lock;
-
-	assert(timeout);
-
-	/* Sleep the given timeout */
-	async_usleep(timeout->timeout);
-
-	/* Lock the globals */
-	fibril_rwlock_write_lock(&tcp_globals.lock);
-
-	/* Find the pending operation socket */
-	socket = socket_port_find(&tcp_globals.sockets, timeout->port,
-	    timeout->key, timeout->key_length);
-
-	if (socket && (socket->socket_id == timeout->socket_id)) {
-		socket_data = (tcp_socket_data_t *) socket->specific_data;
-		assert(socket_data);
-		if (socket_data->local_sockets == timeout->local_sockets) {
-			local_lock = socket_data->local_lock;
-			fibril_rwlock_write_lock(local_lock);
-			socket_destroy(tcp_globals.net_sess,
-			    timeout->socket_id, timeout->local_sockets,
-			    &tcp_globals.sockets, tcp_free_socket_data);
-			fibril_rwlock_write_unlock(local_lock);
-		}
-	}
-
-	/* Unlock the globals */
-	fibril_rwlock_write_unlock(&tcp_globals.lock);
-
-	/* Release the timeout structure */
-	free(timeout);
-
-	return EOK;
-}
-
-void tcp_retransmit_packet(socket_core_t *socket, tcp_socket_data_t *
-    socket_data, size_t sequence_number)
-{
-	packet_t *packet;
-	packet_t *copy;
-	size_t data_length;
-
-	assert(socket);
-	assert(socket_data);
-	assert(socket->specific_data == socket_data);
-
-	/* Sent packet? */
-	packet = pq_find(socket_data->outgoing, sequence_number);
-	if (packet) {
-		pq_get_order(packet, NULL, &data_length);
-		copy = tcp_prepare_copy(socket, socket_data, packet,
-		    data_length, sequence_number);
-		fibril_rwlock_write_unlock(socket_data->local_lock);
-//              printf("r send %d\n", packet_get_id(packet));
-		if (copy) 
-			tcp_send_packets(socket_data->device_id, copy);
-	} else {
-		fibril_rwlock_write_unlock(socket_data->local_lock);
-	}
-}
-
-int tcp_listen_message(socket_cores_t *local_sockets, int socket_id,
-    int backlog)
-{
-	socket_core_t *socket;
-	tcp_socket_data_t *socket_data;
-
-	assert(local_sockets);
-
-	if (backlog < 0) 
-		return EINVAL;
-
-	/* Find the socket */
-	socket = socket_cores_find(local_sockets, socket_id);
-	if (!socket) 
-		return ENOTSOCK;
-	
-	/* Get the socket specific data */
-	socket_data = (tcp_socket_data_t *) socket->specific_data;
-	assert(socket_data);
-
-	/* Set the backlog */
-	socket_data->backlog = backlog;
-
-	return EOK;
-}
-
-int tcp_connect_message(socket_cores_t *local_sockets, int socket_id,
-    struct sockaddr *addr, socklen_t addrlen)
-{
-	socket_core_t *socket;
-	int rc;
-
-	assert(local_sockets);
-	assert(addr);
-	assert(addrlen > 0);
-
-	/* Find the socket */
-	socket = socket_cores_find(local_sockets, socket_id);
-	if (!socket)
-		return ENOTSOCK;
-	
-	rc = tcp_connect_core(socket, local_sockets, addr, addrlen);
-	if (rc != EOK) {
-		tcp_free_socket_data(socket);
-		/* Unbind if bound */
-		if (socket->port > 0) {
-			socket_ports_exclude(&tcp_globals.sockets,
-			    socket->port, free);
-			socket->port = 0;
-		}
-	}
-	return rc;
-}
-
-int tcp_connect_core(socket_core_t *socket, socket_cores_t *local_sockets,
-    struct sockaddr *addr, socklen_t addrlen)
-{
-	tcp_socket_data_t *socket_data;
+/** Receive packets from network layer. */
+static void tcp_receiver(ipc_callid_t iid, ipc_call_t *icall, void *arg)
+{
 	packet_t *packet;
 	int rc;
 
-	assert(socket);
-	assert(addr);
-	assert(addrlen > 0);
-
-	/* Get the socket specific data */
-	socket_data = (tcp_socket_data_t *) socket->specific_data;
-	assert(socket_data);
-	assert(socket->specific_data == socket_data);
-	if ((socket_data->state != TCP_SOCKET_INITIAL) &&
-	    ((socket_data->state != TCP_SOCKET_LISTEN) ||
-	    (socket->port <= 0)))
-		return EINVAL;
-
-	/* Get the destination port */
-	rc = tl_get_address_port(addr, addrlen, &socket_data->dest_port);
-	if (rc != EOK)
-		return rc;
-	
-	if (socket->port <= 0) {
-		/* Try to find a free port */
-		rc = socket_bind_free_port(&tcp_globals.sockets, socket,
-		    TCP_FREE_PORTS_START, TCP_FREE_PORTS_END,
-		    tcp_globals.last_used_port);
-		if (rc != EOK)
-			return rc;
-		/* Set the next port as the search starting port number */
-		tcp_globals.last_used_port = socket->port;
-	}
-
-	rc = ip_get_route_req(tcp_globals.ip_sess, IPPROTO_TCP,
-	    addr, addrlen, &socket_data->device_id,
-	    &socket_data->pseudo_header, &socket_data->headerlen);
-	if (rc != EOK)
-		return rc;
-
-	/* Create the notification packet */
-	rc = tcp_create_notification_packet(&packet, socket, socket_data, 1, 0);
-	if (rc != EOK)
-		return rc;
-
-	/* Unlock the globals and wait for an operation */
-	fibril_rwlock_write_unlock(&tcp_globals.lock);
-
-	socket_data->addr = addr;
-	socket_data->addrlen = addrlen;
-
-	/* Send the packet */
-
-	if (((rc = tcp_queue_packet(socket, socket_data, packet, 1)) != EOK) ||
-	    ((rc = tcp_prepare_timeout(tcp_timeout, socket, socket_data, 0,
-	    TCP_SOCKET_INITIAL, NET_DEFAULT_TCP_INITIAL_TIMEOUT, false)) !=
-	    EOK)) {
-		socket_data->addr = NULL;
-		socket_data->addrlen = 0;
-		fibril_rwlock_write_lock(&tcp_globals.lock);
-	} else {
-		packet = tcp_get_packets_to_send(socket, socket_data);
-		if (packet) {
-			fibril_mutex_lock(&socket_data->operation.mutex);
-			fibril_rwlock_write_unlock(socket_data->local_lock);
-
-			socket_data->state = TCP_SOCKET_SYN_SENT;
-
-			/* Send the packet */
-			tcp_send_packets(socket_data->device_id, packet);
-
-			/* Wait for a reply */
-			fibril_condvar_wait(&socket_data->operation.condvar,
-			    &socket_data->operation.mutex);
-			rc = socket_data->operation.result;
-			if (rc != EOK) {
-				socket_data->addr = NULL;
-				socket_data->addrlen = 0;
-			}
-		} else {
-			socket_data->addr = NULL;
-			socket_data->addrlen = 0;
-			rc = EINTR;
-		}
-	}
-
-	fibril_mutex_unlock(&socket_data->operation.mutex);
-	return rc;
-}
-
-int tcp_queue_prepare_packet(socket_core_t *socket,
-    tcp_socket_data_t *socket_data, packet_t *packet, size_t data_length)
-{
-	tcp_header_t *header;
-	int rc;
-
-	assert(socket);
-	assert(socket_data);
-	assert(socket->specific_data == socket_data);
-
-	/* Get TCP header */
-	header = (tcp_header_t *) packet_get_data(packet);
-	if (!header)
-		return NO_DATA;
-	
-	header->destination_port = htons(socket_data->dest_port);
-	header->source_port = htons(socket->port);
-	header->sequence_number = htonl(socket_data->next_outgoing);
-
-	rc = packet_set_addr(packet, NULL, (uint8_t *) socket_data->addr,
-	    socket_data->addrlen);
-	if (rc != EOK)
-		return tcp_release_and_return(packet, EINVAL);
-
-	/* Remember the outgoing FIN */
-	if (GET_TCP_HEADER_FINALIZE(header))
-		socket_data->fin_outgoing = socket_data->next_outgoing;
-	
-	return EOK;
-}
-
-int tcp_queue_packet(socket_core_t *socket, tcp_socket_data_t *socket_data,
-    packet_t *packet, size_t data_length)
-{
-	int rc;
-
-	assert(socket);
-	assert(socket_data);
-	assert(socket->specific_data == socket_data);
-
-	rc = tcp_queue_prepare_packet(socket, socket_data, packet, data_length);
-	if (rc != EOK)
-		return rc;
-
-	rc = pq_add(&socket_data->outgoing, packet, socket_data->next_outgoing,
-	    data_length);
-	if (rc != EOK)
-		return tcp_release_and_return(packet, rc);
-
-	socket_data->next_outgoing += data_length;
-	return EOK;
-}
-
-packet_t *tcp_get_packets_to_send(socket_core_t *socket, tcp_socket_data_t *
-    socket_data)
-{
-	packet_t *packet;
-	packet_t *copy;
-	packet_t *sending = NULL;
-	packet_t *previous = NULL;
-	size_t data_length;
-	int rc;
-
-	assert(socket);
-	assert(socket_data);
-	assert(socket->specific_data == socket_data);
-
-	packet = pq_find(socket_data->outgoing, socket_data->last_outgoing + 1);
-	while (packet) {
-		pq_get_order(packet, NULL, &data_length);
-
-		/*
-		 * Send only if fits into the window, respecting the possible
-		 * overflow.
-		 */
-		if (!IS_IN_INTERVAL_OVERFLOW(
-		    (uint32_t) socket_data->last_outgoing,
-		    (uint32_t) (socket_data->last_outgoing + data_length),
-		    (uint32_t) (socket_data->expected + socket_data->treshold)))
-			break;
-
-		copy = tcp_prepare_copy(socket, socket_data, packet,
-		    data_length, socket_data->last_outgoing + 1);
-		if (!copy) 
-			return sending;
-			
-		if (!sending) {
-			sending = copy;
-		} else {
-			rc = pq_insert_after(previous, copy);
-			if (rc != EOK) {
-				pq_release_remote(tcp_globals.net_sess,
-				    packet_get_id(copy));
-				return sending;
-			}
-		}
-
-		previous = copy;
-		packet = pq_next(packet);
-
-		/* Overflow occurred? */
-		if (!packet &&
-		    (socket_data->last_outgoing > socket_data->next_outgoing)) {
-			printf("gpts overflow\n");
-			/* Continue from the beginning */
-			packet = socket_data->outgoing;
-		}
-		socket_data->last_outgoing += data_length;
-	}
-
-	return sending;
-}
-
-packet_t *tcp_send_prepare_packet(socket_core_t *socket, tcp_socket_data_t *
-    socket_data, packet_t *packet, size_t data_length, size_t sequence_number)
-{
-	tcp_header_t *header;
-	uint32_t checksum;
-	int rc;
-
-	assert(socket);
-	assert(socket_data);
-	assert(socket->specific_data == socket_data);
-
-	/* Adjust the pseudo header */
-	rc = ip_client_set_pseudo_header_data_length(socket_data->pseudo_header,
-	    socket_data->headerlen, packet_get_data_length(packet));
-	if (rc != EOK) {
-		pq_release_remote(tcp_globals.net_sess, packet_get_id(packet));
-		return NULL;
-	}
-
-	/* Get the header */
-	header = (tcp_header_t *) packet_get_data(packet);
-	if (!header) {
-		pq_release_remote(tcp_globals.net_sess, packet_get_id(packet));
-		return NULL;
-	}
-	assert(ntohl(header->sequence_number) == sequence_number);
-
-	/* Adjust the header */
-	if (socket_data->next_incoming) {
-		header->acknowledgement_number =
-		    htonl(socket_data->next_incoming);
-		SET_TCP_HEADER_ACKNOWLEDGE(header, 1);
-	}
-	header->window = htons(socket_data->window);
-
-	/* Checksum */
-	header->checksum = 0;
-	checksum = compute_checksum(0, socket_data->pseudo_header,
-	    socket_data->headerlen);
-	checksum = compute_checksum(checksum,
-	    (uint8_t *) packet_get_data(packet),
-	    packet_get_data_length(packet));
-	header->checksum = htons(flip_checksum(compact_checksum(checksum)));
-
-	/* Prepare the packet */
-	rc = ip_client_prepare_packet(packet, IPPROTO_TCP, 0, 0, 0, 0);
-	if (rc != EOK) {
-		pq_release_remote(tcp_globals.net_sess, packet_get_id(packet));
-		return NULL;
-	}
-
-	rc = tcp_prepare_timeout(tcp_timeout, socket, socket_data,
-	    sequence_number, socket_data->state, socket_data->timeout, true);
-	if (rc != EOK) {
-		pq_release_remote(tcp_globals.net_sess, packet_get_id(packet));
-		return NULL;
-	}
-
-	return packet;
-}
-
-packet_t *tcp_prepare_copy(socket_core_t *socket, tcp_socket_data_t *
-    socket_data, packet_t *packet, size_t data_length, size_t sequence_number)
-{
-	packet_t *copy;
-
-	assert(socket);
-	assert(socket_data);
-	assert(socket->specific_data == socket_data);
-
-	/* Make a copy of the packet */
-	copy = packet_get_copy(tcp_globals.net_sess, packet);
-	if (!copy)
-		return NULL;
-
-	return tcp_send_prepare_packet(socket, socket_data, copy, data_length,
-	    sequence_number);
-}
-
-void tcp_send_packets(nic_device_id_t device_id, packet_t *packet)
-{
-	packet_t *next;
-
-	while (packet) {
-		next = pq_detach(packet);
-		ip_send_msg(tcp_globals.ip_sess, device_id, packet,
-		    SERVICE_TCP, 0);
-		packet = next;
-	}
-}
-
-void tcp_prepare_operation_header(socket_core_t *socket,
-    tcp_socket_data_t *socket_data, tcp_header_t *header, int synchronize,
-    int finalize)
-{
-	assert(socket);
-	assert(socket_data);
-	assert(socket->specific_data == socket_data);
-	assert(header);
-
-	bzero(header, sizeof(*header));
-	header->source_port = htons(socket->port);
-	header->source_port = htons(socket_data->dest_port);
-	SET_TCP_HEADER_LENGTH(header,
-	    TCP_COMPUTE_HEADER_LENGTH(sizeof(*header)));
-	SET_TCP_HEADER_SYNCHRONIZE(header, synchronize);
-	SET_TCP_HEADER_FINALIZE(header, finalize);
-}
-
-int tcp_prepare_timeout(int (*timeout_function)(void *tcp_timeout_t),
-    socket_core_t *socket, tcp_socket_data_t *socket_data,
-    size_t sequence_number, tcp_socket_state_t state, suseconds_t timeout,
-    int globals_read_only)
-{
-	tcp_timeout_t *operation_timeout;
-	fid_t fibril;
-
-	assert(socket);
-	assert(socket_data);
-	assert(socket->specific_data == socket_data);
-
-	/* Prepare the timeout with key bundle structure */
-	operation_timeout = malloc(sizeof(*operation_timeout) +
-	    socket->key_length + 1);
-	if (!operation_timeout)
-		return ENOMEM;
-
-	bzero(operation_timeout, sizeof(*operation_timeout));
-	operation_timeout->globals_read_only = globals_read_only;
-	operation_timeout->port = socket->port;
-	operation_timeout->local_sockets = socket_data->local_sockets;
-	operation_timeout->socket_id = socket->socket_id;
-	operation_timeout->timeout = timeout;
-	operation_timeout->sequence_number = sequence_number;
-	operation_timeout->state = state;
-
-	/* Copy the key */
-	operation_timeout->key = ((uint8_t *) operation_timeout) +
-	    sizeof(*operation_timeout);
-	operation_timeout->key_length = socket->key_length;
-	memcpy(operation_timeout->key, socket->key, socket->key_length);
-	operation_timeout->key[operation_timeout->key_length] = '\0';
-
-	/* Prepare the timeouting thread */
-	fibril = fibril_create(timeout_function, operation_timeout);
-	if (!fibril) {
-		free(operation_timeout);
-		return ENOMEM;
-	}
-
-//      fibril_mutex_lock(&socket_data->operation.mutex);
-	/* Start the timeout fibril */
-	fibril_add_ready(fibril);
-	//socket_data->state = state;
-	return EOK;
-}
-
-int tcp_recvfrom_message(socket_cores_t *local_sockets, int socket_id,
-    int flags, size_t *addrlen)
-{
-	socket_core_t *socket;
-	tcp_socket_data_t *socket_data;
-	int packet_id;
-	packet_t *packet;
-	size_t length;
-	int rc;
-
-	assert(local_sockets);
-
-	/* Find the socket */
-	socket = socket_cores_find(local_sockets, socket_id);
-	if (!socket)
-		return ENOTSOCK;
-
-	/* Get the socket specific data */
-	if (!socket->specific_data)
-		return NO_DATA;
-
-	socket_data = (tcp_socket_data_t *) socket->specific_data;
-
-	/* Check state */
-	if ((socket_data->state != TCP_SOCKET_ESTABLISHED) &&
-	    (socket_data->state != TCP_SOCKET_CLOSE_WAIT))
-		return ENOTCONN;
-
-	/* Send the source address if desired */
-	if (addrlen) {
-		rc = data_reply(socket_data->addr, socket_data->addrlen);
-		if (rc != EOK)
-			return rc;
-		*addrlen = socket_data->addrlen;
-	}
-
-	/* Get the next received packet */
-	packet_id = dyn_fifo_value(&socket->received);
-	if (packet_id < 0)
-		return NO_DATA;
-
-	rc = packet_translate_remote(tcp_globals.net_sess, &packet, packet_id);
-	if (rc != EOK)
-		return rc;
-
-	/* Reply the packets */
-	rc = socket_reply_packets(packet, &length);
-	if (rc != EOK)
-		return rc;
-
-	/* Release the packet */
-	dyn_fifo_pop(&socket->received);
-	pq_release_remote(tcp_globals.net_sess, packet_get_id(packet));
-
-	/* Return the total length */
-	return (int) length;
-}
-
-int tcp_send_message(socket_cores_t *local_sockets, int socket_id,
-    int fragments, size_t *data_fragment_size, int flags)
-{
-	socket_core_t *socket;
-	tcp_socket_data_t *socket_data;
-	packet_dimension_t *packet_dimension;
-	packet_t *packet;
-	size_t total_length;
-	tcp_header_t *header;
-	int index;
-	int result;
-	int rc;
-
-	assert(local_sockets);
-	assert(data_fragment_size);
-
-	/* Find the socket */
-	socket = socket_cores_find(local_sockets, socket_id);
-	if (!socket)
-		return ENOTSOCK;
-
-	/* Get the socket specific data */
-	if (!socket->specific_data)
-		return NO_DATA;
-
-	socket_data = (tcp_socket_data_t *) socket->specific_data;
-
-	/* Check state */
-	if ((socket_data->state != TCP_SOCKET_ESTABLISHED) &&
-	    (socket_data->state != TCP_SOCKET_CLOSE_WAIT))
-		return ENOTCONN;
-
-	rc = tl_get_ip_packet_dimension(tcp_globals.ip_sess,
-	    &tcp_globals.dimensions, socket_data->device_id, &packet_dimension);
-	if (rc != EOK)
-		return rc;
-
-	*data_fragment_size =
-	    ((packet_dimension->content < socket_data->data_fragment_size) ?
-	    packet_dimension->content : socket_data->data_fragment_size);
-
-	for (index = 0; index < fragments; index++) {
-		/* Read the data fragment */
-		result = tl_socket_read_packet_data(tcp_globals.net_sess,
-		    &packet, TCP_HEADER_SIZE, packet_dimension,
-		    socket_data->addr, socket_data->addrlen);
-		if (result < 0)
-			return result;
-
-		total_length = (size_t) result;
-
-		/* Prefix the TCP header */
-		header = PACKET_PREFIX(packet, tcp_header_t);
-		if (!header)
-			return tcp_release_and_return(packet, ENOMEM);
-
-		tcp_prepare_operation_header(socket, socket_data, header, 0, 0);
-		rc = tcp_queue_packet(socket, socket_data, packet, total_length);
-		if (rc != EOK)
-			return rc;
-	}
-
-	/* Flush packets */
-	packet = tcp_get_packets_to_send(socket, socket_data);
-	fibril_rwlock_write_unlock(socket_data->local_lock);
-	fibril_rwlock_read_unlock(&tcp_globals.lock);
-
-	if (packet) {
-		/* Send the packet */
-		tcp_send_packets(socket_data->device_id, packet);
-	}
-
-	return EOK;
-}
-
-int
-tcp_close_message(socket_cores_t *local_sockets, int socket_id)
-{
-	socket_core_t *socket;
-	tcp_socket_data_t *socket_data;
-	packet_t *packet;
-	int rc;
-
-	/* Find the socket */
-	socket = socket_cores_find(local_sockets, socket_id);
-	if (!socket)
-		return ENOTSOCK;
-
-	/* Get the socket specific data */
-	socket_data = (tcp_socket_data_t *) socket->specific_data;
-	assert(socket_data);
-
-	/* Check state */
-	switch (socket_data->state) {
-	case TCP_SOCKET_ESTABLISHED:
-		socket_data->state = TCP_SOCKET_FIN_WAIT_1;
-		break;
-
-	case TCP_SOCKET_CLOSE_WAIT:
-		socket_data->state = TCP_SOCKET_LAST_ACK;
-		break;
-
-//      case TCP_SOCKET_LISTEN:
-
-	default:
-		/* Just destroy */
-		rc = socket_destroy(tcp_globals.net_sess, socket_id,
-		    local_sockets, &tcp_globals.sockets,
-		    tcp_free_socket_data);
-		if (rc == EOK) {
-			fibril_rwlock_write_unlock(socket_data->local_lock);
-			fibril_rwlock_write_unlock(&tcp_globals.lock);
-		}
-		return rc;
-	}
-
-	/*
-	 * Send FIN.
-	 * TODO should I wait to complete?
-	 */
-
-	/* Create the notification packet */
-	rc = tcp_create_notification_packet(&packet, socket, socket_data, 0, 1);
-	if (rc != EOK)
-		return rc;
-
-	/* Send the packet */
-	rc = tcp_queue_packet(socket, socket_data, packet, 1);
-	if (rc != EOK)
-		return rc;
-
-	/* Flush packets */
-	packet = tcp_get_packets_to_send(socket, socket_data);
-	fibril_rwlock_write_unlock(socket_data->local_lock);
-	fibril_rwlock_write_unlock(&tcp_globals.lock);
-
-	if (packet) {
-		/* Send the packet */
-		tcp_send_packets(socket_data->device_id, packet);
-	}
-
-	return EOK;
-}
-
-int tcp_create_notification_packet(packet_t **packet, socket_core_t *socket,
-    tcp_socket_data_t *socket_data, int synchronize, int finalize)
-{
-	packet_dimension_t *packet_dimension;
-	tcp_header_t *header;
-	int rc;
-
-	assert(packet);
-
-	/* Get the device packet dimension */
-	rc = tl_get_ip_packet_dimension(tcp_globals.ip_sess,
-	    &tcp_globals.dimensions, socket_data->device_id, &packet_dimension);
-	if (rc != EOK)
-		return rc;
-
-	/* Get a new packet */
-	*packet = packet_get_4_remote(tcp_globals.net_sess, TCP_HEADER_SIZE,
-	    packet_dimension->addr_len, packet_dimension->prefix,
-	    packet_dimension->suffix);
-	
-	if (!*packet) 
-		return ENOMEM;
-
-	/* Allocate space in the packet */
-	header = PACKET_SUFFIX(*packet, tcp_header_t);
-	if (!header)
-		tcp_release_and_return(*packet, ENOMEM);
-
-	tcp_prepare_operation_header(socket, socket_data, header, synchronize,
-	    finalize);
-
-	return EOK;
-}
-
-int tcp_accept_message(socket_cores_t *local_sockets, int socket_id,
-    int new_socket_id, size_t *data_fragment_size, size_t *addrlen)
-{
-	socket_core_t *accepted;
-	socket_core_t *socket;
-	tcp_socket_data_t *socket_data;
-	packet_dimension_t *packet_dimension;
-	int rc;
-
-	assert(local_sockets);
-	assert(data_fragment_size);
-	assert(addrlen);
-
-	/* Find the socket */
-	socket = socket_cores_find(local_sockets, socket_id);
-	if (!socket)
-		return ENOTSOCK;
-
-	/* Get the socket specific data */
-	socket_data = (tcp_socket_data_t *) socket->specific_data;
-	assert(socket_data);
-
-	/* Check state */
-	if (socket_data->state != TCP_SOCKET_LISTEN)
-		return EINVAL;
-
-	do {
-		socket_id = dyn_fifo_value(&socket->accepted);
-		if (socket_id < 0)
-			return ENOTSOCK;
-		socket_id *= -1;
-
-		accepted = socket_cores_find(local_sockets, socket_id);
-		if (!accepted)
-			return ENOTSOCK;
-
-		/* Get the socket specific data */
-		socket_data = (tcp_socket_data_t *) accepted->specific_data;
-		assert(socket_data);
-		/* TODO can it be in another state? */
-		if (socket_data->state == TCP_SOCKET_ESTABLISHED) {
-			rc = data_reply(socket_data->addr,
-			    socket_data->addrlen);
-			if (rc != EOK)
-				return rc;
-			rc = tl_get_ip_packet_dimension(tcp_globals.ip_sess,
-			    &tcp_globals.dimensions, socket_data->device_id,
-			    &packet_dimension);
-			if (rc != EOK)
-				return rc;
-			*addrlen = socket_data->addrlen;
-
-			*data_fragment_size =
-			    ((packet_dimension->content <
-			    socket_data->data_fragment_size) ?
-			    packet_dimension->content :
-			    socket_data->data_fragment_size);
-	
-			if (new_socket_id > 0) {
-				rc = socket_cores_update(local_sockets,
-				    accepted->socket_id, new_socket_id);
-				if (rc != EOK)
-					return rc;
-				accepted->socket_id = new_socket_id;
-			}
-		}
-		dyn_fifo_pop(&socket->accepted);
-	} while (socket_data->state != TCP_SOCKET_ESTABLISHED);
-
-	printf("ret accept %d\n", accepted->socket_id);
-	return accepted->socket_id;
-}
-
-void tcp_free_socket_data(socket_core_t *socket)
-{
-	tcp_socket_data_t *socket_data;
-
-	assert(socket);
-
-	printf("destroy_socket %d\n", socket->socket_id);
-
-	/* Get the socket specific data */
-	socket_data = (tcp_socket_data_t *) socket->specific_data;
-	assert(socket_data);
-
-	/* Free the pseudo header */
-	if (socket_data->pseudo_header) {
-		if (socket_data->headerlen) {
-			printf("d pseudo\n");
-			free(socket_data->pseudo_header);
-			socket_data->headerlen = 0;
-		}
-		socket_data->pseudo_header = NULL;
-	}
-
-	socket_data->headerlen = 0;
-
-	/* Free the address */
-	if (socket_data->addr) {
-		if (socket_data->addrlen) {
-			printf("d addr\n");
-			free(socket_data->addr);
-			socket_data->addrlen = 0;
-		}
-		socket_data->addr = NULL;
-	}
-	socket_data->addrlen = 0;
-}
-
-/** Releases the packet and returns the result.
- *
- * @param[in] packet	The packet queue to be released.
- * @param[in] result	The result to be returned.
- * @return		The result parameter.
- */
-int tcp_release_and_return(packet_t *packet, int result)
-{
-	pq_release_remote(tcp_globals.net_sess, packet_get_id(packet));
-	return result;
-}
-
-/** Process IPC messages from the IP module
- *
- * @param[in]     iid   Message identifier.
- * @param[in,out] icall Message parameters.
- * @param[in]     arg   Local argument.
- *
- */
-static void tcp_receiver(ipc_callid_t iid, ipc_call_t *icall, void *arg)
-{
-	packet_t *packet;
-	int rc;
-	
+	log_msg(LVL_DEBUG, "tcp_receiver()");
+
 	while (true) {
 		switch (IPC_GET_IMETHOD(*icall)) {
 		case NET_TL_RECEIVED:
-			rc = packet_translate_remote(tcp_globals.net_sess, &packet,
+			log_msg(LVL_DEBUG, "method = NET_TL_RECEIVED");
+			rc = packet_translate_remote(net_sess, &packet,
 			    IPC_GET_PACKET(*icall));
-			if (rc == EOK)
-				rc = tcp_received_msg(IPC_GET_DEVICE(*icall), packet,
-				    SERVICE_TCP, IPC_GET_ERROR(*icall));
-			
-			async_answer_0(iid, (sysarg_t) rc);
+			if (rc != EOK) {
+				log_msg(LVL_DEBUG, "Error %d translating packet.", rc);
+				async_answer_0(iid, (sysarg_t)rc);
+				break;
+			}
+			rc = tcp_received_msg(IPC_GET_DEVICE(*icall), packet,
+			    IPC_GET_ERROR(*icall));
+			async_answer_0(iid, (sysarg_t)rc);
 			break;
 		default:
-			async_answer_0(iid, (sysarg_t) ENOTSUP);
+			log_msg(LVL_DEBUG, "method = %u",
+			    (unsigned)IPC_GET_IMETHOD(*icall));
+			async_answer_0(iid, ENOTSUP);
+			break;
 		}
-		
+
 		iid = async_get_call(icall);
 	}
 }
 
-/** Initialize the TCP module.
- *
- * @param[in] sess Network module session.
- *
- * @return EOK on success.
- * @return ENOMEM if there is not enough memory left.
- *
+/** Transmit PDU over network layer. */
+void tcp_transmit_pdu(tcp_pdu_t *pdu)
+{
+	struct sockaddr_in dest;
+	nic_device_id_t dev_id;
+	void *phdr;
+	size_t phdr_len;
+	packet_dimension_t *pkt_dim;
+	int rc;
+	packet_t *packet;
+	void *pkt_data;
+	size_t pdu_size;
+
+	dest.sin_family = AF_INET;
+	dest.sin_port = 0; /* not needed */
+	dest.sin_addr.s_addr = host2uint32_t_be(pdu->dest_addr.ipv4);
+
+	/* Find route. Obtained pseudo-header is not used. */
+	rc = ip_get_route_req(ip_sess, IPPROTO_TCP, (struct sockaddr *)&dest,
+	    sizeof(dest), &dev_id, &phdr, &phdr_len);
+	if (rc != EOK) {
+		log_msg(LVL_DEBUG, "tcp_transmit_pdu: Failed to find route.");
+		return;
+	}
+
+	rc = tl_get_ip_packet_dimension(ip_sess, &pkt_dims, dev_id, &pkt_dim);
+	if (rc != EOK) {
+		log_msg(LVL_DEBUG, "tcp_transmit_pdu: Failed to get dimension.");
+		return;
+	}
+
+	pdu_size = pdu->header_size + pdu->text_size;
+
+	packet = packet_get_4_remote(net_sess, pdu_size, pkt_dim->addr_len,
+	    pkt_dim->prefix, pkt_dim->suffix);
+	if (!packet) {
+		log_msg(LVL_DEBUG, "tcp_transmit_pdu: Failed to get packet.");
+		return;
+	}
+
+	pkt_data = packet_suffix(packet, pdu_size);
+	if (!pkt_data) {
+		log_msg(LVL_DEBUG, "tcp_transmit_pdu: Failed to get pkt_data ptr.");
+		pq_release_remote(net_sess, packet_get_id(packet));
+		return;
+	}
+
+	rc = ip_client_prepare_packet(packet, IPPROTO_TCP, 0, 0, 0, 0);
+	if (rc != EOK) {
+		log_msg(LVL_DEBUG, "tcp_transmit_pdu: Failed to prepare IP packet part.");
+		pq_release_remote(net_sess, packet_get_id(packet));
+		return;
+	}
+
+	rc = packet_set_addr(packet, NULL, (uint8_t *)&dest, sizeof(dest));
+	if (rc != EOK) {
+		log_msg(LVL_DEBUG, "tcp_transmit_pdu: Failed to set packet address.");
+		pq_release_remote(net_sess, packet_get_id(packet));
+		return;
+	}
+
+	/* Copy PDU data to packet */
+	memcpy(pkt_data, pdu->header, pdu->header_size);
+	memcpy((uint8_t *)pkt_data + pdu->header_size, pdu->text,
+	    pdu->text_size);
+
+	/* Transmit packet. XXX Transfers packet ownership to IP? */
+	ip_send_msg(ip_sess, dev_id, packet, SERVICE_TCP, 0);
+}
+
+/** Process received PDU. */
+static void tcp_received_pdu(tcp_pdu_t *pdu)
+{
+	tcp_segment_t *dseg;
+	tcp_sockpair_t rident;
+
+	log_msg(LVL_DEBUG, "tcp_received_pdu()");
+
+	if (tcp_pdu_decode(pdu, &rident, &dseg) != EOK) {
+		log_msg(LVL_WARN, "Not enough memory. PDU dropped.");
+		return;
+	}
+
+	/* Insert decoded segment into rqueue */
+	tcp_rqueue_insert_seg(&rident, dseg);
+}
+
+/* Called from libnet */
+void tl_connection(void)
+{
+	log_msg(LVL_DEBUG, "tl_connection()");
+}
+
+/* Called from libnet */
+int tl_message(ipc_callid_t callid, ipc_call_t *call, ipc_call_t *answer,
+    size_t *answer_count)
+{
+	async_sess_t *callback;
+
+	log_msg(LVL_DEBUG, "tl_message()");
+
+	*answer_count = 0;
+	callback = async_callback_receive_start(EXCHANGE_SERIALIZE, call);
+	if (callback)
+		return tcp_sock_connection(callback, callid, *call);
+
+	return ENOTSUP;
+}
+
+/* Called from libnet */
+int tl_initialize(async_sess_t *sess)
+{
+	int rc;
+
+	net_sess = sess;
+	icmp_sess = icmp_connect_module();
+
+	log_msg(LVL_DEBUG, "tl_initialize()");
+
+	tcp_sock_init();
+
+	ip_sess = ip_bind_service(SERVICE_IP, IPPROTO_TCP, SERVICE_TCP,
+	    tcp_receiver);
+	if (ip_sess == NULL)
+		return ENOENT;
+
+	rc = packet_dimensions_initialize(&pkt_dims);
+	if (rc != EOK)
+		return rc;
+
+	return EOK;
+}
+
+int main(int argc, char **argv)
+{
+	int rc;
+
+	printf(NAME ": TCP (Transmission Control Protocol) network module\n");
+
+	rc = log_init(NAME, LVL_ERROR);
+	if (rc != EOK) {
+		printf(NAME ": Failed to initialize log.\n");
+		return 1;
+	}
+
+//	printf(NAME ": Accepting connections\n");
+//	task_retval(0);
+
+	tcp_rqueue_init();
+	tcp_rqueue_thread_start();
+
+	tcp_ncsim_init();
+	tcp_ncsim_thread_start();
+
+	if (0) tcp_test();
+/*
+	async_manager();
+*/
+	tl_module_start(SERVICE_TCP);
+
+	/* Not reached */
+	return 0;
+}
+
+/**
+ * @}
  */
-int tl_initialize(async_sess_t *sess)
-{
-	fibril_rwlock_initialize(&tcp_globals.lock);
-	fibril_rwlock_write_lock(&tcp_globals.lock);
-	
-	tcp_globals.net_sess = sess;
-	
-	tcp_globals.icmp_sess = icmp_connect_module();
-	tcp_globals.ip_sess = ip_bind_service(SERVICE_IP, IPPROTO_TCP,
-	    SERVICE_TCP, tcp_receiver);
-	if (tcp_globals.ip_sess == NULL) {
-		fibril_rwlock_write_unlock(&tcp_globals.lock);
-		return ENOENT;
-	}
-	
-	int rc = socket_ports_initialize(&tcp_globals.sockets);
-	if (rc != EOK)
-		goto out;
-
-	rc = packet_dimensions_initialize(&tcp_globals.dimensions);
-	if (rc != EOK) {
-		socket_ports_destroy(&tcp_globals.sockets, free);
-		goto out;
-	}
-
-	tcp_globals.last_used_port = TCP_FREE_PORTS_START - 1;
-
-out:
-	fibril_rwlock_write_unlock(&tcp_globals.lock);
-	return rc;
-}
-
-int main(int argc, char *argv[])
-{
-	return tl_module_start(SERVICE_TCP);
-}
-
-/** @}
- */
Index: uspace/srv/net/tl/tcp/tcp.h
===================================================================
--- uspace/srv/net/tl/tcp/tcp.h	(revision 4c67e52b4043ea5cd4323c9bd8094bfde545d638)
+++ uspace/srv/net/tl/tcp/tcp.h	(revision 899f1a95f22eca90d14c5209d80a487c9a0979f0)
@@ -1,4 +1,4 @@
 /*
- * Copyright (c) 2008 Lukas Mejdrech
+ * Copyright (c) 2011 Jiri Svoboda
  * All rights reserved.
  *
@@ -30,277 +30,17 @@
  * @{
  */
-
-/** @file
- * TCP module.
+/** @file TCP (Transmission Control Protocol) network module
  */
 
-#ifndef NET_TCP_H_
-#define NET_TCP_H_
+#ifndef TCP_H
+#define TCP_H
 
 #include <async.h>
-#include <fibril_synch.h>
-#include <net/packet.h>
-#include <net/device.h>
-#include <socket_core.h>
-#include <tl_common.h>
+#include <packet_remote.h>
+#include "tcp_type.h"
 
-/** Type definition of the TCP global data.
- * @see tcp_globals
- */
-typedef struct tcp_globals tcp_globals_t;
-
-/** Type definition of the TCP socket specific data.
- * @see tcp_socket_data
- */
-typedef struct tcp_socket_data tcp_socket_data_t;
-
-/** Type definition of the TCP operation data.
- * @see tcp_operation
- */
-typedef struct tcp_operation tcp_operation_t;
-
-/** TCP socket state type definition.
- * @see tcp_socket_state
- */
-typedef enum tcp_socket_state tcp_socket_state_t;
-
-/** TCP socket state. */
-enum tcp_socket_state {
-	/** Initial.
-	 *
-	 * Not connected or bound.
-	 */
-	TCP_SOCKET_INITIAL,
-	
-	/** Listening.
-	 *
-	 * Awaiting a connection request from another TCP layer.
-	 * When SYN is received a new bound socket in the
-	 * TCP_SOCKET_SYN_RECEIVED state should be created.
-	 */
-	TCP_SOCKET_LISTEN,
-	
-	/** Connecting issued.
-	 *
-	 * A SYN has been sent, and TCP is awaiting the response SYN.
-	 * Should continue to the TCP_SOCKET_ESTABLISHED state.
-	 */
-	TCP_SOCKET_SYN_SENT,
-	
-	/** Connecting received.
-	 *
-	 * A SYN has been received, a SYN has been sent, and TCP is awaiting an
-	 * ACK. Should continue to the TCP_SOCKET_ESTABLISHED state.
-	 */
-	TCP_SOCKET_SYN_RECEIVED,
-	
-	/** Connected.
-	 *
-	 * The three-way handshake has been completed.
-	 */
-	TCP_SOCKET_ESTABLISHED,
-	
-	/** Closing started.
-	 *
-	 * The local application has issued a CLOSE.
-	 * TCP has sent a FIN, and is awaiting an ACK or a FIN.
-	 * Should continue to the TCP_SOCKET_FIN_WAIT_2 state when an ACK is
-	 * received.
-	 * Should continue to the TCP_SOCKET_CLOSING state when a FIN is
-	 * received.
-	 */
-	TCP_SOCKET_FIN_WAIT_1,
-	
-	/** Closing confirmed.
-	 *
-	 * A FIN has been sent, and an ACK received.
-	 * TCP is awaiting a~FIN from the remote TCP layer.
-	 * Should continue to the TCP_SOCKET_CLOSING state.
-	 */
-	TCP_SOCKET_FIN_WAIT_2,
-	
-	/** Closing.
-	 *
-	 * A FIN has been sent, a FIN has been received, and an ACK has been
-	 * sent.
-	 * TCP is awaiting an ACK for the FIN that was sent.
-	 * Should continue to the TCP_SOCKET_TIME_WAIT state.
-	 */
-	TCP_SOCKET_CLOSING,
-	
-	/** Closing received.
-	 *
-	 * TCP has received a FIN, and has sent an ACK.
-	 * It is awaiting a close request from the local application before
-	 * sending a FIN.
-	 * Should continue to the TCP_SOCKET_SOCKET_LAST_ACK state.
-	 */
-	TCP_SOCKET_CLOSE_WAIT,
-	
-	/**
-	 * A FIN has been received, and an ACK and a FIN have been sent.
-	 * TCP is awaiting an ACK.
-	 * Should continue to the TCP_SOCKET_TIME_WAIT state.
-	 */
-	TCP_SOCKET_LAST_ACK,
-	
-	/** Closing finished.
-	 *
-	 * FINs have been received and ACK’d, and TCP is waiting two MSLs to
-	 * remove the connection from the table.
-	 */
-	TCP_SOCKET_TIME_WAIT,
-	
-	/** Closed.
-	 *
-	 * Imaginary, this indicates that a connection has been removed from
-	 * the connection table.
-	 */
-	TCP_SOCKET_CLOSED
-};
-
-/** TCP operation data. */
-struct tcp_operation {
-	/** Operation result. */
-	int result;
-	/** Safety lock. */
-	fibril_mutex_t mutex;
-	/** Operation result signaling. */
-	fibril_condvar_t condvar;
-};
-
-/** TCP socket specific data. */
-struct tcp_socket_data {
-	/** TCP socket state. */
-	tcp_socket_state_t state;
-	
-	/**
-	 * Data fragment size.
-	 * Sending optimalization.
-	 */
-	size_t data_fragment_size;
-	
-	/** Device identifier. */
-	nic_device_id_t device_id;
-	
-	/**
-	 * Listening backlog.
-	 * The maximal number of connected but not yet accepted sockets.
-	 */
-	int backlog;
-	
-	/**
-	 * Parent listening socket identifier.
-	 * Set if this socket is an accepted one.
-	 */
-	int listening_socket_id;
-	
-	/** Treshold size in bytes. */
-	size_t treshold;
-	/** Window size in bytes. */
-	size_t window;
-	/** Acknowledgement timeout. */
-	suseconds_t timeout;
-	/** Last acknowledged byte. */
-	uint32_t acknowledged;
-	/** Next incoming sequence number. */
-	uint32_t next_incoming;
-	/** Incoming FIN. */
-	uint32_t fin_incoming;
-	/** Next outgoing sequence number. */
-	uint32_t next_outgoing;
-	/** Last outgoing sequence number. */
-	uint32_t last_outgoing;
-	/** Outgoing FIN. */
-	uint32_t fin_outgoing;
-	
-	/**
-	 * Expected sequence number by the remote host.
-	 * The sequence number the other host expects.
-	 * The notification is sent only upon a packet reecival.
-	 */
-	uint32_t expected;
-	
-	/**
-	 * Expected sequence number counter.
-	 * Counts the number of received notifications for the same sequence
-	 * number.
-	 */
-	int expected_count;
-	
-	/** Incoming packet queue.
-	 *
-	 * Packets are buffered until received in the right order.
-	 * The packets are excluded after successfully read.
-	 * Packets are sorted by their starting byte.
-	 * Packets metric is set as their data length.
-	 */
-	packet_t *incoming;
-	
-	/** Outgoing packet queue.
-	 *
-	 * Packets are buffered until acknowledged by the remote host in the
-	 * right order.
-	 * The packets are excluded after acknowledged.
-	 * Packets are sorted by their starting byte.
-	 * Packets metric is set as their data length.
-	 */
-	packet_t *outgoing;
-	
-	/** IP pseudo header. */
-	void *pseudo_header;
-	/** IP pseudo header length. */
-	size_t headerlen;
-	/** Remote host address. */
-	struct sockaddr *addr;
-	/** Remote host address length. */
-	socklen_t addrlen;
-	/** Remote host port. */
-	uint16_t dest_port;
-	/** Parent local sockets. */
-	socket_cores_t *local_sockets;
-	
-	/** Local sockets safety lock.
-	 *
-	 * May be locked for writing while holding the global lock for reading
-	 * when changing the local sockets only.
-	 * The global lock may be locked only before locking the local lock.
-	 * The global lock may be locked more weakly than the local lock.
-	 * The global lock may be released before releasing the local lock.
-	 * @see tcp_globals:lock
-	 */
-	fibril_rwlock_t *local_lock;
-	
-	/** Pending operation data. */
-	tcp_operation_t operation;
-	
-	/**
-	 * Timeouts in a row counter.
-	 * If TCP_MAX_TIMEOUTS is reached, the connection is lost.
-	 */
-	int timeout_count;
-};
-
-/** TCP global data. */
-struct tcp_globals {
-	/** Networking module session. */
-	async_sess_t *net_sess;
-	/** IP module session. */
-	async_sess_t *ip_sess;
-	/** ICMP module session. */
-	async_sess_t *icmp_sess;
-	/** Last used free port. */
-	int last_used_port;
-	/** Active sockets. */
-	socket_ports_t sockets;
-	/** Device packet dimensions. */
-	packet_dimensions_t dimensions;
-	
-	/**
-	 * Safety lock.
-	 * Write lock is used only for adding or removing socket ports.
-	 */
-	fibril_rwlock_t lock;
-};
+extern async_sess_t *net_sess;
+extern async_sess_t *ip_sess;
+extern void tcp_transmit_pdu(tcp_pdu_t *);
 
 #endif
Index: uspace/srv/net/tl/tcp/tcp_header.h
===================================================================
--- uspace/srv/net/tl/tcp/tcp_header.h	(revision 4c67e52b4043ea5cd4323c9bd8094bfde545d638)
+++ 	(revision )
@@ -1,163 +1,0 @@
-/*
- * Copyright (c) 2009 Lukas Mejdrech
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- *
- * - Redistributions of source code must retain the above copyright
- *   notice, this list of conditions and the following disclaimer.
- * - Redistributions in binary form must reproduce the above copyright
- *   notice, this list of conditions and the following disclaimer in the
- *   documentation and/or other materials provided with the distribution.
- * - The name of the author may not be used to endorse or promote products
- *   derived from this software without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
- * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
- * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
- * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
- * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
- * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
- * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
- * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
- * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- */
-
-/** @addtogroup tcp
- * @{
- */
-
-/** @file
- * TCP header definition.
- * Based on the RFC 793.
- */
-
-#ifndef NET_TCP_HEADER_H_
-#define NET_TCP_HEADER_H_
-
-#include <sys/types.h>
-
-/** TCP header size in bytes. */
-#define TCP_HEADER_SIZE				sizeof(tcp_header_t)
-
-/** Returns the actual TCP header length in bytes.
- * @param[in] header The TCP packet header.
- */
-#define TCP_HEADER_LENGTH(header)		(GET_TCP_HEADER_LENGTH(header) * 4U)
-
-/** Returns the TCP header length.
- * @param[in] length The TCP header length in bytes.
- */
-#define TCP_COMPUTE_HEADER_LENGTH(length)	((uint8_t) ((length) / 4U))
-
-/** Type definition of the transmission datagram header.
- * @see tcp_header
- */
-typedef struct tcp_header tcp_header_t;
-
-/** Type definition of the transmission datagram header option.
- * @see tcp_option
- */
-typedef struct tcp_option tcp_option_t;
-
-/** Type definition of the Maximum segment size TCP option. */
-typedef struct tcp_max_segment_size_option tcp_max_segment_size_option_t;
-
-/** Transmission datagram header. */
-struct tcp_header {
-	uint16_t source_port;
-	uint16_t destination_port;
-	uint32_t sequence_number;
-	uint32_t acknowledgement_number;
-
-	uint8_t hlr; /* header length, reserved1 */
-
-#define GET_TCP_HEADER_LENGTH(header) \
-	(((header)->hlr & 0xf0) >> 4)
-#define SET_TCP_HEADER_LENGTH(header, length) \
-	((header)->hlr = \
-	 ((length & 0x0f) << 4) | ((header)->hlr & 0x0f))
-
-#define GET_TCP_HEADER_RESERVED1(header) \
-	((header)->hlr & 0x0f)
-#define SET_TCP_HEADER_RESERVED1(header, reserved1) \
-	((header)->hlr = \
-	 (reserved1 & 0x0f) | ((header)->hlr & 0xf0))
-
-	/* reserved2, urgent, acknowledge, push, reset, synchronize, finalize */
-	uint8_t ruaprsf;  
-
-#define GET_TCP_HEADER_RESERVED2(header) \
-	(((header)->ruaprsf & 0xc0) >> 6)
-#define SET_TCP_HEADER_RESERVED2(header, reserved2) \
-	((header)->ruaprsf = \
-	 ((reserved2 & 0x03) << 6) | ((header)->ruaprsf & 0x3f))
-
-#define GET_TCP_HEADER_URGENT(header) \
-	(((header)->ruaprsf & 0x20) >> 5)
-#define SET_TCP_HEADER_URGENT(header, urgent) \
-	((header)->ruaprsf = \
-	 ((urgent & 0x01) << 5) | ((header)->ruaprsf & 0xdf))
-
-#define GET_TCP_HEADER_ACKNOWLEDGE(header) \
-	(((header)->ruaprsf & 0x10) >> 4)
-#define SET_TCP_HEADER_ACKNOWLEDGE(header, acknowledge) \
-	((header)->ruaprsf = \
-	 ((acknowledge & 0x01) << 4) | ((header)->ruaprsf & 0xef))
-
-#define GET_TCP_HEADER_PUSH(header) \
-	(((header)->ruaprsf & 0x08) >> 3)
-#define SET_TCP_HEADER_PUSH(header, push) \
-	((header)->ruaprsf = \
-	 ((push & 0x01) << 3) | ((header)->ruaprsf & 0xf7))
-
-#define GET_TCP_HEADER_RESET(header) \
-	(((header)->ruaprsf & 0x04) >> 2)
-#define SET_TCP_HEADER_RESET(header, reset) \
-	((header)->ruaprsf = \
-	 ((reset & 0x01) << 2) | ((header)->ruaprsf & 0xfb))
-
-#define GET_TCP_HEADER_SYNCHRONIZE(header) \
-	(((header)->ruaprsf & 0x02) >> 1)
-#define SET_TCP_HEADER_SYNCHRONIZE(header, synchronize) \
-	((header)->ruaprsf = \
-	 ((synchronize & 0x01) << 1) | ((header)->ruaprsf & 0xfd))
-
-#define GET_TCP_HEADER_FINALIZE(header) \
-	((header)->ruaprsf & 0x01)
-#define SET_TCP_HEADER_FINALIZE(header, finalize) \
-	((header)->ruaprsf = \
-	 (finalize & 0x01) | ((header)->ruaprsf & 0xfe))
-
-	uint16_t window;
-	uint16_t checksum;
-	uint16_t urgent_pointer;
-} __attribute__ ((packed));
-
-/** Transmission datagram header option. */
-struct tcp_option {
-	/** Option type. */
-	uint8_t type;
-	/** Option length. */
-	uint8_t length;
-};
-
-/** Maximum segment size TCP option. */
-struct tcp_max_segment_size_option {
-	/** TCP option.
-	 * @see TCPOPT_MAX_SEGMENT_SIZE
-	 * @see TCPOPT_MAX_SEGMENT_SIZE_LENGTH
-	 */
-	tcp_option_t option;
-	
-	/** Maximum segment size in bytes. */
-	uint16_t max_segment_size;
-} __attribute__ ((packed));
-
-#endif
-
-/** @}
- */
Index: uspace/srv/net/tl/tcp/tcp_type.h
===================================================================
--- uspace/srv/net/tl/tcp/tcp_type.h	(revision 899f1a95f22eca90d14c5209d80a487c9a0979f0)
+++ uspace/srv/net/tl/tcp/tcp_type.h	(revision 899f1a95f22eca90d14c5209d80a487c9a0979f0)
@@ -0,0 +1,323 @@
+/*
+ * Copyright (c) 2011 Jiri Svoboda
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ *   notice, this list of conditions and the following disclaimer.
+ * - Redistributions in binary form must reproduce the above copyright
+ *   notice, this list of conditions and the following disclaimer in the
+ *   documentation and/or other materials provided with the distribution.
+ * - The name of the author may not be used to endorse or promote products
+ *   derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/** @addtogroup tcp
+ * @{
+ */
+/** @file TCP type definitions
+ */
+
+#ifndef TCP_TYPE_H
+#define TCP_TYPE_H
+
+#include <adt/list.h>
+#include <async.h>
+#include <bool.h>
+#include <fibril_synch.h>
+#include <socket_core.h>
+#include <sys/types.h>
+
+struct tcp_conn;
+
+typedef enum {
+	/** Listen */
+	st_listen,
+	/** Syn-sent */
+	st_syn_sent,
+	/** Syn-received */
+	st_syn_received,
+	/** Established */
+	st_established,
+	/** Fin-wait-1 */
+	st_fin_wait_1,
+	/** Fin-wait-2 */
+	st_fin_wait_2,
+	/** Close-wait */
+	st_close_wait,
+	/** Closing */
+	st_closing,
+	/** Last-ack */
+	st_last_ack,
+	/** Time-wait */
+	st_time_wait,
+	/** Closed */
+	st_closed
+} tcp_cstate_t;
+
+/** Error codes returned by TCP user calls (per the spec). */
+typedef enum {
+	/* OK */
+	TCP_EOK,
+	/* Connection aborted due to user timeout */
+	TCP_EABORTED,
+	/* Connection already exists */
+	TCP_EEXISTS,
+	/* Connection closing */
+	TCP_ECLOSING,
+	/* Connection does not exist */
+	TCP_ENOTEXIST,
+	/* Connection illegal for this process */
+	TCP_EILLEGAL,
+	/* Connection not open */
+	TCP_ENOTOPEN,
+	/* Connection reset */
+	TCP_ERESET,
+	/* Foreign socket unspecified */
+	TCP_EUNSPEC,
+	/* Insufficient resources */
+	TCP_ENORES,
+	/* Precedence not allowed */
+	TCP_EINVPREC,
+	/* Security/compartment not allowed */
+	TCP_EINVCOMP
+} tcp_error_t;
+
+typedef enum {
+	XF_PUSH		= 0x1,
+	XF_URGENT	= 0x2
+} xflags_t;
+
+typedef enum {
+	CTL_SYN		= 0x1,
+	CTL_FIN		= 0x2,
+	CTL_RST		= 0x4,
+	CTL_ACK		= 0x8
+} tcp_control_t;
+
+typedef struct {
+	uint32_t ipv4;
+} netaddr_t;
+
+typedef struct {
+	netaddr_t addr;
+	uint16_t port;
+} tcp_sock_t;
+
+enum netaddr {
+	TCP_IPV4_ANY = 0
+};
+
+enum tcp_port {
+	TCP_PORT_ANY = 0
+};
+
+typedef struct {
+	tcp_sock_t local;
+	tcp_sock_t foreign;
+} tcp_sockpair_t;
+
+/** Connection incoming segments queue */
+typedef struct {
+	struct tcp_conn *conn;
+	list_t list;
+} tcp_iqueue_t;
+
+/** Retransmission queue */
+typedef struct {
+	struct tcp_conn *conn;
+	list_t list;
+
+	/** Retransmission timer */
+	fibril_timer_t *timer;
+} tcp_tqueue_t;
+
+typedef enum {
+	ap_active,
+	ap_passive
+} acpass_t;
+
+typedef struct tcp_conn {
+	char *name;
+	link_t link;
+
+	/** Connection identification (local and foreign socket) */
+	tcp_sockpair_t ident;
+
+	/** Active or passive connection */
+	acpass_t ap;
+
+	/** Connection state */
+	tcp_cstate_t cstate;
+	/** True if connection was reset */
+	bool reset;
+	/** Protects @c cstate */
+	fibril_mutex_t cstate_lock;
+	/** Signalled when @c cstate changes */
+	fibril_condvar_t cstate_cv;
+
+	/** Set when FIN is removed from the retransmission queue */
+	bool fin_is_acked;
+
+	/** Queue of incoming segments */
+	tcp_iqueue_t incoming;
+
+	/** Retransmission queue */
+	tcp_tqueue_t retransmit;
+
+	/** Time-Wait timeout timer */
+	fibril_timer_t *tw_timer;
+
+	/** Receive buffer */
+	uint8_t *rcv_buf;
+	/** Receive buffer size */
+	size_t rcv_buf_size;
+	/** Receive buffer number of bytes used */
+	size_t rcv_buf_used;
+	/** Receive buffer contains FIN */
+	bool rcv_buf_fin;
+	/** Receive buffer lock */
+	fibril_mutex_t rcv_buf_lock;
+	/** Receive buffer CV. Broadcast when new data is inserted */
+	fibril_condvar_t rcv_buf_cv;
+
+	/** Send buffer */
+	uint8_t *snd_buf;
+	/** Send buffer size */
+	size_t snd_buf_size;
+	/** Send buffer number of bytes used */
+	size_t snd_buf_used;
+	/** Send buffer contains FIN */
+	bool snd_buf_fin;
+
+	/** Send unacknowledged */
+	uint32_t snd_una;
+	/** Send next */
+	uint32_t snd_nxt;
+	/** Send window */
+	uint32_t snd_wnd;
+	/** Send urgent pointer */
+	uint32_t snd_up;
+	/** Segment sequence number used for last window update */
+	uint32_t snd_wl1;
+	/** Segment acknowledgement number used for last window update */
+	uint32_t snd_wl2;
+	/** Initial send sequence number */
+	uint32_t iss;
+
+	/** Receive next */
+	uint32_t rcv_nxt;
+	/** Receive window */
+	uint32_t rcv_wnd;
+	/** Receive urgent pointer */
+	uint32_t rcv_up;
+	/** Initial receive sequence number */
+	uint32_t irs;
+} tcp_conn_t;
+
+typedef struct {
+	unsigned dummy;
+} tcp_conn_status_t;
+
+typedef struct {
+	/** SYN, FIN */
+	tcp_control_t ctrl;
+
+	/** Segment sequence number */
+	uint32_t seq;
+	/** Segment acknowledgement number */
+	uint32_t ack;
+	/** Segment length in sequence space */
+	uint32_t len;
+	/** Segment window */
+	uint32_t wnd;
+	/** Segment urgent pointer */
+	uint32_t up;
+
+	/** Segment data, may be moved when trimming segment */
+	void *data;
+	/** Segment data, original pointer used to free data */
+	void *dfptr;
+} tcp_segment_t;
+
+
+typedef struct {
+	link_t link;
+	tcp_sockpair_t sp;
+	tcp_segment_t *seg;
+} tcp_rqueue_entry_t;
+
+/** NCSim queue entry */
+typedef struct {
+	link_t link;
+	suseconds_t delay;
+	tcp_sockpair_t sp;
+	tcp_segment_t *seg;
+} tcp_squeue_entry_t;
+
+typedef struct {
+	link_t link;
+	tcp_segment_t *seg;
+} tcp_iqueue_entry_t;
+
+/** Retransmission queue entry */
+typedef struct {
+	link_t link;
+	tcp_conn_t *conn;
+	tcp_segment_t *seg;
+} tcp_tqueue_entry_t;
+
+typedef enum {
+	cp_continue,
+	cp_done
+} cproc_t;
+
+/** Encoded PDU */
+typedef struct {
+	/** Source address */
+	netaddr_t src_addr;
+	/** Destination address */
+	netaddr_t dest_addr;
+
+	/** Encoded header */
+	void *header;
+	/** Encoded header size */
+	size_t header_size;
+	/** Text */
+	void *text;
+	/** Text size */
+	size_t text_size;
+} tcp_pdu_t;
+
+typedef struct {
+	async_sess_t *sess;
+	socket_cores_t sockets;
+} tcp_client_t;
+
+typedef struct {
+	/** Client */
+	tcp_client_t *client;
+	/** Connection */
+	tcp_conn_t *conn;
+	/** Local address */
+	netaddr_t laddr;
+} tcp_sockdata_t;
+
+#endif
+
+/** @}
+ */
Index: uspace/srv/net/tl/tcp/test.c
===================================================================
--- uspace/srv/net/tl/tcp/test.c	(revision 899f1a95f22eca90d14c5209d80a487c9a0979f0)
+++ uspace/srv/net/tl/tcp/test.c	(revision 899f1a95f22eca90d14c5209d80a487c9a0979f0)
@@ -0,0 +1,145 @@
+/*
+ * Copyright (c) 2011 Jiri Svoboda
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ *   notice, this list of conditions and the following disclaimer.
+ * - Redistributions in binary form must reproduce the above copyright
+ *   notice, this list of conditions and the following disclaimer in the
+ *   documentation and/or other materials provided with the distribution.
+ * - The name of the author may not be used to endorse or promote products
+ *   derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/** @addtogroup tcp
+ * @{
+ */
+
+/**
+ * @file Internal TCP test
+ */
+
+#include <async.h>
+#include <errno.h>
+#include <stdio.h>
+#include <thread.h>
+#include <str.h>
+#include "tcp_type.h"
+#include "ucall.h"
+
+#include "test.h"
+
+#define RCV_BUF_SIZE 64
+
+static void test_srv(void *arg)
+{
+	tcp_conn_t *conn;
+	tcp_sock_t lsock;
+	tcp_sock_t fsock;
+	char rcv_buf[RCV_BUF_SIZE + 1];
+	size_t rcvd;
+	xflags_t xflags;
+
+	printf("test_srv()\n");
+	lsock.port = 80;
+	lsock.addr.ipv4 = 0x7f000001;
+	fsock.port = 1024;
+	fsock.addr.ipv4 = 0x7f000001;
+	printf("S: User open...\n");
+	tcp_uc_open(&lsock, &fsock, ap_passive, &conn);
+	conn->name = (char *) "S";
+
+	while (true) {
+		printf("S: User receive...\n");
+		tcp_uc_receive(conn, rcv_buf, RCV_BUF_SIZE, &rcvd, &xflags);
+		if (rcvd == 0) {
+			printf("S: End of data reached.\n");
+			break;
+		}
+		rcv_buf[rcvd] = '\0';
+		printf("S: User received %zu bytes '%s'.\n", rcvd, rcv_buf);
+
+		async_usleep(1000*1000*2);
+	}
+
+	async_usleep(/*10**/1000*1000);
+
+	printf("S: User close...\n");
+	tcp_uc_close(conn);
+
+	printf("test_srv() terminating\n");
+}
+
+static void test_cli(void *arg)
+{
+	tcp_conn_t *conn;
+	tcp_sock_t lsock;
+	tcp_sock_t fsock;
+	const char *msg = "Hello World!";
+
+	printf("test_cli()\n");
+
+	lsock.port = 1024;
+	lsock.addr.ipv4 = 0x7f000001;
+	fsock.port = 80;
+	fsock.addr.ipv4 = 0x7f000001;
+
+	async_usleep(1000*1000*3);
+	printf("C: User open...\n");
+	tcp_uc_open(&lsock, &fsock, ap_active, &conn);
+	conn->name = (char *) "C";
+
+	async_usleep(1000*1000*10);
+	printf("C: User send...\n");
+	tcp_uc_send(conn, (void *)msg, str_size(msg), 0);
+
+	async_usleep(1000*1000*20/**20*2*/);
+	printf("C: User close...\n");
+	tcp_uc_close(conn);
+}
+
+void tcp_test(void)
+{
+	thread_id_t srv_tid;
+	thread_id_t cli_tid;
+	int rc;
+
+	printf("tcp_test()\n");
+
+	async_usleep(1000*1000);
+
+	if (0) {
+		rc = thread_create(test_srv, NULL, "test_srv", &srv_tid);
+		if (rc != EOK) {
+			printf("Failed to create server thread.\n");
+			return;
+		}
+	}
+
+	if (0) {
+		rc = thread_create(test_cli, NULL, "test_cli", &cli_tid);
+		if (rc != EOK) {
+			printf("Failed to create client thread.\n");
+			return;
+		}
+	}
+}
+
+/**
+ * @}
+ */
Index: uspace/srv/net/tl/tcp/test.h
===================================================================
--- uspace/srv/net/tl/tcp/test.h	(revision 899f1a95f22eca90d14c5209d80a487c9a0979f0)
+++ uspace/srv/net/tl/tcp/test.h	(revision 899f1a95f22eca90d14c5209d80a487c9a0979f0)
@@ -0,0 +1,43 @@
+/*
+ * Copyright (c) 2011 Jiri Svoboda
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ *   notice, this list of conditions and the following disclaimer.
+ * - Redistributions in binary form must reproduce the above copyright
+ *   notice, this list of conditions and the following disclaimer in the
+ *   documentation and/or other materials provided with the distribution.
+ * - The name of the author may not be used to endorse or promote products
+ *   derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/** @addtogroup tcp
+ * @{
+ */
+/** @file Internal TCP test
+ */
+
+#ifndef TEST_H
+#define TEST_H
+
+extern void tcp_test(void);
+
+#endif
+
+/** @}
+ */
Index: uspace/srv/net/tl/tcp/tqueue.c
===================================================================
--- uspace/srv/net/tl/tcp/tqueue.c	(revision 899f1a95f22eca90d14c5209d80a487c9a0979f0)
+++ uspace/srv/net/tl/tcp/tqueue.c	(revision 899f1a95f22eca90d14c5209d80a487c9a0979f0)
@@ -0,0 +1,362 @@
+/*
+ * Copyright (c) 2011 Jiri Svoboda
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ *   notice, this list of conditions and the following disclaimer.
+ * - Redistributions in binary form must reproduce the above copyright
+ *   notice, this list of conditions and the following disclaimer in the
+ *   documentation and/or other materials provided with the distribution.
+ * - The name of the author may not be used to endorse or promote products
+ *   derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/** @addtogroup tcp
+ * @{
+ */
+
+/**
+ * @file TCP transmission queue
+ */
+
+#include <adt/list.h>
+#include <errno.h>
+#include <fibril_synch.h>
+#include <byteorder.h>
+#include <io/log.h>
+#include <macros.h>
+#include <mem.h>
+#include <stdlib.h>
+#include "conn.h"
+#include "ncsim.h"
+#include "pdu.h"
+#include "rqueue.h"
+#include "segment.h"
+#include "seq_no.h"
+#include "tqueue.h"
+#include "tcp.h"
+#include "tcp_type.h"
+
+#define RETRANSMIT_TIMEOUT	(2*1000*1000)
+
+static void retransmit_timeout_func(void *arg);
+static void tcp_tqueue_timer_set(tcp_conn_t *conn);
+static void tcp_tqueue_timer_clear(tcp_conn_t *conn);
+
+int tcp_tqueue_init(tcp_tqueue_t *tqueue, tcp_conn_t *conn)
+{
+	tqueue->conn = conn;
+	tqueue->timer = fibril_timer_create();
+	if (tqueue->timer == NULL)
+		return ENOMEM;
+
+	list_initialize(&tqueue->list);
+
+	return EOK;
+}
+
+void tcp_tqueue_clear(tcp_tqueue_t *tqueue)
+{
+	tcp_tqueue_timer_clear(tqueue->conn);
+}
+
+void tcp_tqueue_fini(tcp_tqueue_t *tqueue)
+{
+	if (tqueue->timer != NULL) {
+		fibril_timer_destroy(tqueue->timer);
+		tqueue->timer = NULL;
+	}
+}
+
+void tcp_tqueue_ctrl_seg(tcp_conn_t *conn, tcp_control_t ctrl)
+{
+	tcp_segment_t *seg;
+
+	log_msg(LVL_DEBUG, "tcp_tqueue_ctrl_seg(%p, %u)", conn, ctrl);
+
+	seg = tcp_segment_make_ctrl(ctrl);
+	tcp_tqueue_seg(conn, seg);
+}
+
+void tcp_tqueue_seg(tcp_conn_t *conn, tcp_segment_t *seg)
+{
+	tcp_segment_t *rt_seg;
+	tcp_tqueue_entry_t *tqe;
+
+	log_msg(LVL_DEBUG, "%s: tcp_tqueue_seg(%p, %p)", conn->name, conn,
+	    seg);
+
+	/*
+	 * Add segment to retransmission queue
+	 */
+
+	if (seg->len > 0) {
+		rt_seg = tcp_segment_dup(seg);
+		if (rt_seg == NULL) {
+			log_msg(LVL_ERROR, "Memory allocation failed.");
+			/* XXX Handle properly */
+			return;
+		}
+
+		tqe = calloc(1, sizeof(tcp_tqueue_entry_t));
+		if (tqe == NULL) {
+			log_msg(LVL_ERROR, "Memory allocation failed.");
+			/* XXX Handle properly */
+			return;
+		}
+
+		tqe->conn = conn;
+		tqe->seg = rt_seg;
+		rt_seg->seq = conn->snd_nxt;
+
+		list_append(&tqe->link, &conn->retransmit.list);
+
+		/* Set retransmission timer */
+		tcp_tqueue_timer_set(conn);
+	}
+
+	tcp_prepare_transmit_segment(conn, seg);
+}
+
+void tcp_prepare_transmit_segment(tcp_conn_t *conn, tcp_segment_t *seg)
+{
+	/*
+	 * Always send ACK once we have received SYN, except for RST segments.
+	 * (Spec says we should always send ACK once connection has been
+	 * established.)
+	 */
+	if (tcp_conn_got_syn(conn) && (seg->ctrl & CTL_RST) == 0)
+		seg->ctrl |= CTL_ACK;
+
+	seg->seq = conn->snd_nxt;
+	conn->snd_nxt += seg->len;
+
+	tcp_conn_transmit_segment(conn, seg);
+}
+
+/** Transmit data from the send buffer.
+ *
+ * @param conn	Connection
+ */
+void tcp_tqueue_new_data(tcp_conn_t *conn)
+{
+	size_t avail_wnd;
+	size_t xfer_seqlen;
+	size_t snd_buf_seqlen;
+	size_t data_size;
+	tcp_control_t ctrl;
+	bool send_fin;
+
+	tcp_segment_t *seg;
+
+	log_msg(LVL_DEBUG, "%s: tcp_tqueue_new_data()", conn->name);
+
+	/* Number of free sequence numbers in send window */
+	avail_wnd = (conn->snd_una + conn->snd_wnd) - conn->snd_nxt;
+	snd_buf_seqlen = conn->snd_buf_used + (conn->snd_buf_fin ? 1 : 0);
+
+	xfer_seqlen = min(snd_buf_seqlen, avail_wnd);
+	log_msg(LVL_DEBUG, "%s: snd_buf_seqlen = %zu, SND.WND = %zu, "
+	    "xfer_seqlen = %zu", conn->name, snd_buf_seqlen, conn->snd_wnd,
+	    xfer_seqlen);
+
+	if (xfer_seqlen == 0)
+		return;
+
+	/* XXX Do not always send immediately */
+
+	send_fin = conn->snd_buf_fin && xfer_seqlen == snd_buf_seqlen;
+	data_size = xfer_seqlen - (send_fin ? 1 : 0);
+
+	if (send_fin) {
+		log_msg(LVL_DEBUG, "%s: Sending out FIN.", conn->name);
+		/* We are sending out FIN */
+		ctrl = CTL_FIN;
+		tcp_conn_fin_sent(conn);
+	} else {
+		ctrl = 0;
+	}
+
+	seg = tcp_segment_make_data(ctrl, conn->snd_buf, data_size);
+	if (seg == NULL) {
+		log_msg(LVL_ERROR, "Memory allocation failure.");
+		return;
+	}
+
+	/* Remove data from send buffer */
+	memmove(conn->snd_buf, conn->snd_buf + data_size,
+	    conn->snd_buf_used - data_size);
+	conn->snd_buf_used -= data_size;
+
+	if (send_fin)
+		conn->snd_buf_fin = false;
+
+	tcp_tqueue_seg(conn, seg);
+}
+
+/** Remove ACKed segments from retransmission queue and possibly transmit
+ * more data.
+ *
+ * This should be called when SND.UNA is updated due to incoming ACK.
+ */
+void tcp_tqueue_ack_received(tcp_conn_t *conn)
+{
+	link_t *cur, *next;
+
+	log_msg(LVL_DEBUG, "%s: tcp_tqueue_ack_received(%p)", conn->name,
+	    conn);
+
+	cur = conn->retransmit.list.head.next;
+
+	while (cur != &conn->retransmit.list.head) {
+		next = cur->next;
+
+		tcp_tqueue_entry_t *tqe = list_get_instance(cur,
+		    tcp_tqueue_entry_t, link);
+
+		if (seq_no_segment_acked(conn, tqe->seg, conn->snd_una)) {
+			/* Remove acknowledged segment */
+			list_remove(cur);
+
+			if ((tqe->seg->ctrl & CTL_FIN) != 0) {
+				log_msg(LVL_DEBUG, "Fin has been acked");
+				log_msg(LVL_DEBUG, "SND.UNA=%" PRIu32
+				    " SEG.SEQ=%" PRIu32 " SEG.LEN=%" PRIu32,
+				    conn->snd_una, tqe->seg->seq, tqe->seg->len);
+				/* Our FIN has been acked */
+				conn->fin_is_acked = true;
+			}
+
+			tcp_segment_delete(tqe->seg);
+			free(tqe);
+
+			/* Reset retransmission timer */
+			tcp_tqueue_timer_set(conn);
+		}
+
+		cur = next;
+	}
+
+	/* Clear retransmission timer if the queue is empty. */
+	if (list_empty(&conn->retransmit.list))
+		tcp_tqueue_timer_clear(conn);
+
+	/* Possibly transmit more data */
+	tcp_tqueue_new_data(conn);
+}
+
+void tcp_conn_transmit_segment(tcp_conn_t *conn, tcp_segment_t *seg)
+{
+	log_msg(LVL_DEBUG, "%s: tcp_conn_transmit_segment(%p, %p)",
+	    conn->name, conn, seg);
+
+	seg->wnd = conn->rcv_wnd;
+
+	if ((seg->ctrl & CTL_ACK) != 0)
+		seg->ack = conn->rcv_nxt;
+	else
+		seg->ack = 0;
+
+	tcp_transmit_segment(&conn->ident, seg);
+}
+
+void tcp_transmit_segment(tcp_sockpair_t *sp, tcp_segment_t *seg)
+{
+	log_msg(LVL_DEBUG, "tcp_transmit_segment(f:(%x,%u),l:(%x,%u), %p)",
+	    sp->foreign.addr.ipv4, sp->foreign.port,
+	    sp->local.addr.ipv4, sp->local.port, seg);
+
+	log_msg(LVL_DEBUG, "SEG.SEQ=%" PRIu32 ", SEG.WND=%" PRIu32,
+	    seg->seq, seg->wnd);
+
+	tcp_segment_dump(seg);
+/*
+	tcp_pdu_prepare(conn, seg, &data, &len);
+	tcp_pdu_transmit(data, len);
+*/
+//	tcp_rqueue_bounce_seg(sp, seg);
+//	tcp_ncsim_bounce_seg(sp, seg);
+
+	tcp_pdu_t *pdu;
+
+	if (tcp_pdu_encode(sp, seg, &pdu) != EOK) {
+		log_msg(LVL_WARN, "Not enough memory. Segment dropped.");
+		return;
+	}
+
+	tcp_transmit_pdu(pdu);
+	tcp_pdu_delete(pdu);
+}
+
+static void retransmit_timeout_func(void *arg)
+{
+	tcp_conn_t *conn = (tcp_conn_t *) arg;
+	tcp_tqueue_entry_t *tqe;
+	tcp_segment_t *rt_seg;
+	link_t *link;
+
+	log_msg(LVL_DEBUG, "### %s: retransmit_timeout_func(%p)", conn->name, conn);
+
+	if (conn->cstate == st_closed) {
+		log_msg(LVL_DEBUG, "Connection already closed.");
+		return;
+	}
+
+	link = list_first(&conn->retransmit.list);
+	if (link == NULL) {
+		log_msg(LVL_DEBUG, "Nothing to retransmit");
+		return;
+	}
+
+	tqe = list_get_instance(link, tcp_tqueue_entry_t, link);
+
+	rt_seg = tcp_segment_dup(tqe->seg);
+	if (rt_seg == NULL) {
+		log_msg(LVL_ERROR, "Memory allocation failed.");
+		/* XXX Handle properly */
+		return;
+	}
+
+	log_msg(LVL_DEBUG, "### %s: retransmitting segment", conn->name);
+	tcp_conn_transmit_segment(tqe->conn, rt_seg);
+
+	/* Reset retransmission timer */
+	tcp_tqueue_timer_set(tqe->conn);
+}
+
+/** Set or re-set retransmission timer */
+static void tcp_tqueue_timer_set(tcp_conn_t *conn)
+{
+	log_msg(LVL_DEBUG, "### %s: tcp_tqueue_timer_set()", conn->name);
+
+	(void) retransmit_timeout_func;
+	fibril_timer_set(conn->retransmit.timer, RETRANSMIT_TIMEOUT,
+	    retransmit_timeout_func, (void *) conn);
+}
+
+/** Clear retransmission timer */
+static void tcp_tqueue_timer_clear(tcp_conn_t *conn)
+{
+	log_msg(LVL_DEBUG, "### %s: tcp_tqueue_timer_clear()", conn->name);
+
+	fibril_timer_clear(conn->retransmit.timer);
+}
+
+/**
+ * @}
+ */
Index: uspace/srv/net/tl/tcp/tqueue.h
===================================================================
--- uspace/srv/net/tl/tcp/tqueue.h	(revision 899f1a95f22eca90d14c5209d80a487c9a0979f0)
+++ uspace/srv/net/tl/tcp/tqueue.h	(revision 899f1a95f22eca90d14c5209d80a487c9a0979f0)
@@ -0,0 +1,57 @@
+/*
+ * Copyright (c) 2011 Jiri Svoboda
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ *   notice, this list of conditions and the following disclaimer.
+ * - Redistributions in binary form must reproduce the above copyright
+ *   notice, this list of conditions and the following disclaimer in the
+ *   documentation and/or other materials provided with the distribution.
+ * - The name of the author may not be used to endorse or promote products
+ *   derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/** @addtogroup tcp
+ * @{
+ */
+/** @file TCP transmission queue
+ */
+
+#ifndef TQUEUE_H
+#define TQUEUE_H
+
+#include "std.h"
+#include "tcp_type.h"
+
+extern int tcp_tqueue_init(tcp_tqueue_t *, tcp_conn_t *);
+extern void tcp_tqueue_clear(tcp_tqueue_t *);
+extern void tcp_tqueue_fini(tcp_tqueue_t *);
+extern void tcp_tqueue_ctrl_seg(tcp_conn_t *, tcp_control_t);
+extern void tcp_tqueue_seg(tcp_conn_t *, tcp_segment_t *);
+extern void tcp_tqueue_new_data(tcp_conn_t *);
+extern void tcp_tqueue_ack_received(tcp_conn_t *);
+extern void tcp_prepare_transmit_segment(tcp_conn_t *, tcp_segment_t *);
+extern void tcp_conn_transmit_segment(tcp_conn_t *, tcp_segment_t *);
+extern void tcp_transmit_segment(tcp_sockpair_t *, tcp_segment_t *);
+extern void tcp_header_setup(tcp_conn_t *, tcp_segment_t *, tcp_header_t *);
+extern void tcp_phdr_setup(tcp_conn_t *, tcp_segment_t *, tcp_phdr_t *);
+
+#endif
+
+/** @}
+ */
Index: uspace/srv/net/tl/tcp/ucall.c
===================================================================
--- uspace/srv/net/tl/tcp/ucall.c	(revision 899f1a95f22eca90d14c5209d80a487c9a0979f0)
+++ uspace/srv/net/tl/tcp/ucall.c	(revision 899f1a95f22eca90d14c5209d80a487c9a0979f0)
@@ -0,0 +1,282 @@
+/*
+ * Copyright (c) 2011 Jiri Svoboda
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ *   notice, this list of conditions and the following disclaimer.
+ * - Redistributions in binary form must reproduce the above copyright
+ *   notice, this list of conditions and the following disclaimer in the
+ *   documentation and/or other materials provided with the distribution.
+ * - The name of the author may not be used to endorse or promote products
+ *   derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/** @addtogroup tcp
+ * @{
+ */
+
+/**
+ * @file TCP entry points (close to those defined in the RFC)
+ */
+
+#include <fibril_synch.h>
+#include <io/log.h>
+#include <macros.h>
+#include <mem.h>
+#include "conn.h"
+#include "tcp_type.h"
+#include "tqueue.h"
+#include "ucall.h"
+
+/*
+ * User calls
+ */
+
+/** OPEN user call
+ *
+ * @param lsock		Local socket
+ * @param fsock		Foreign socket
+ * @param acpass	Active/passive
+ * @param conn		Connection
+ *
+ * Unlike in the spec we allow specifying the local address. This means
+ * the implementation does not need to magically guess it, especially
+ * considering there can be more than one local address.
+ *
+ * XXX We should be able to call active open on an existing listening
+ * connection.
+ * XXX We should be able to get connection structure immediately, before
+ * establishment.
+ */
+tcp_error_t tcp_uc_open(tcp_sock_t *lsock, tcp_sock_t *fsock, acpass_t acpass,
+    tcp_conn_t **conn)
+{
+	tcp_conn_t *nconn;
+
+	log_msg(LVL_DEBUG, "tcp_uc_open(%p, %p, %s, %p)",
+	    lsock, fsock, acpass == ap_active ? "active" : "passive",
+	    conn);
+
+	nconn = tcp_conn_new(lsock, fsock);
+	tcp_conn_add(nconn);
+
+	if (acpass == ap_active) {
+		/* Synchronize (initiate) connection */
+		tcp_conn_sync(nconn);
+	}
+
+	/* Wait for connection to be established or reset */
+	log_msg(LVL_DEBUG, "tcp_uc_open: Wait for connection.");
+	fibril_mutex_lock(&nconn->cstate_lock);
+	while (nconn->cstate == st_listen ||
+	    nconn->cstate == st_syn_sent ||
+	    nconn->cstate == st_syn_received) {
+		fibril_condvar_wait(&nconn->cstate_cv, &nconn->cstate_lock);
+	}
+
+	if (nconn->cstate != st_established) {
+		log_msg(LVL_DEBUG, "tcp_uc_open: Connection was reset.");
+		assert(nconn->cstate == st_closed);
+		fibril_mutex_unlock(&nconn->cstate_lock);
+		return TCP_ERESET;
+	}
+
+	fibril_mutex_unlock(&nconn->cstate_lock);
+	log_msg(LVL_DEBUG, "tcp_uc_open: Connection was established.");
+
+	*conn = nconn;
+	return TCP_EOK;
+}
+
+/** SEND user call */
+tcp_error_t tcp_uc_send(tcp_conn_t *conn, void *data, size_t size,
+    xflags_t flags)
+{
+	size_t buf_free;
+	size_t xfer_size;
+
+	log_msg(LVL_DEBUG, "%s: tcp_uc_send()", conn->name);
+
+	if (conn->cstate == st_closed)
+		return TCP_ENOTEXIST;
+
+	if (conn->cstate == st_listen) {
+		/* Change connection to active */
+		tcp_conn_sync(conn);
+	}
+
+	if (conn->snd_buf_fin)
+		return TCP_ECLOSING;
+
+	while (size > 0) {
+		buf_free = conn->snd_buf_size - conn->snd_buf_used;
+		while (buf_free == 0 && !conn->reset)
+			tcp_tqueue_new_data(conn);
+
+		if (conn->reset)
+			return TCP_ERESET;
+
+		xfer_size = min(size, buf_free);
+
+		/* Copy data to buffer */
+		memcpy(conn->snd_buf + conn->snd_buf_used, data, xfer_size);
+		data += xfer_size;
+		conn->snd_buf_used += xfer_size;
+		size -= xfer_size;
+	}
+
+	tcp_tqueue_new_data(conn);
+
+	return TCP_EOK;
+}
+
+/** RECEIVE user call */
+tcp_error_t tcp_uc_receive(tcp_conn_t *conn, void *buf, size_t size,
+    size_t *rcvd, xflags_t *xflags)
+{
+	size_t xfer_size;
+
+	log_msg(LVL_DEBUG, "%s: tcp_uc_receive()", conn->name);
+
+	if (conn->cstate == st_closed)
+		return TCP_ENOTEXIST;
+
+	fibril_mutex_lock(&conn->rcv_buf_lock);
+
+	/* Wait for data to become available */
+	while (conn->rcv_buf_used == 0 && !conn->rcv_buf_fin && !conn->reset) {
+		log_msg(LVL_DEBUG, "tcp_uc_receive() - wait for data");
+		fibril_condvar_wait(&conn->rcv_buf_cv, &conn->rcv_buf_lock);
+	}
+
+	if (conn->rcv_buf_used == 0) {
+		fibril_mutex_unlock(&conn->rcv_buf_lock);
+
+		*rcvd = 0;
+		*xflags = 0;
+
+		if (conn->rcv_buf_fin) {
+			/* End of data, peer closed connection */
+			return TCP_ECLOSING;
+		} else {
+			/* Connection was reset */
+			assert(conn->reset);
+			return TCP_ERESET;
+		}
+	}
+
+	/* Copy data from receive buffer to user buffer */
+	xfer_size = min(size, conn->rcv_buf_used);
+	memcpy(buf, conn->rcv_buf, xfer_size);
+	*rcvd = xfer_size;
+
+	/* Remove data from receive buffer */
+	memmove(conn->rcv_buf, conn->rcv_buf + xfer_size, conn->rcv_buf_used -
+	    xfer_size);
+	conn->rcv_buf_used -= xfer_size;
+	conn->rcv_wnd += xfer_size;
+
+	fibril_mutex_unlock(&conn->rcv_buf_lock);
+
+	/* TODO */
+	*xflags = 0;
+
+	/* Send new size of receive window */
+	tcp_tqueue_ctrl_seg(conn, CTL_ACK);
+
+	log_msg(LVL_DEBUG, "%s: tcp_uc_receive() - returning %zu bytes",
+	    conn->name, xfer_size);
+
+	return TCP_EOK;
+}
+
+/** CLOSE user call */
+tcp_error_t tcp_uc_close(tcp_conn_t *conn)
+{
+	log_msg(LVL_DEBUG, "%s: tcp_uc_close()", conn->name);
+
+	if (conn->cstate == st_closed)
+		return TCP_ENOTEXIST;
+
+	if (conn->snd_buf_fin)
+		return TCP_ECLOSING;
+
+	conn->snd_buf_fin = true;
+	tcp_tqueue_new_data(conn);
+
+	return TCP_EOK;
+}
+
+/** ABORT user call */
+void tcp_uc_abort(tcp_conn_t *conn)
+{
+	log_msg(LVL_DEBUG, "tcp_uc_abort()");
+}
+
+/** STATUS user call */
+void tcp_uc_status(tcp_conn_t *conn, tcp_conn_status_t *cstatus)
+{
+	log_msg(LVL_DEBUG, "tcp_uc_status()");
+}
+
+
+/*
+ * Arriving segments
+ */
+
+/** Segment arrived */
+void tcp_as_segment_arrived(tcp_sockpair_t *sp, tcp_segment_t *seg)
+{
+	tcp_conn_t *conn;
+
+	log_msg(LVL_DEBUG, "tcp_as_segment_arrived(f:(%x,%u), l:(%x,%u))",
+	    sp->foreign.addr.ipv4, sp->foreign.port,
+	    sp->local.addr.ipv4, sp->local.port);
+
+	conn = tcp_conn_find(sp);
+	if (conn != NULL && conn->cstate != st_closed) {
+		if (conn->ident.foreign.addr.ipv4 == TCP_IPV4_ANY)
+			conn->ident.foreign.addr.ipv4 = sp->foreign.addr.ipv4;
+		if (conn->ident.foreign.port == TCP_PORT_ANY)
+			conn->ident.foreign.port = sp->foreign.port;
+		if (conn->ident.local.addr.ipv4 == TCP_IPV4_ANY)
+			conn->ident.local.addr.ipv4 = sp->local.addr.ipv4;
+
+		tcp_conn_segment_arrived(conn, seg);
+	} else {
+		if (conn == NULL)
+			log_msg(LVL_WARN, "No connection found.");
+		else
+			log_msg(LVL_WARN, "Connection is closed.");
+		tcp_unexpected_segment(sp, seg);
+	}
+}
+
+/*
+ * Timeouts
+ */
+
+/** User timeout */
+void tcp_to_user(void)
+{
+	log_msg(LVL_DEBUG, "tcp_to_user()");
+}
+
+/**
+ * @}
+ */
Index: uspace/srv/net/tl/tcp/ucall.h
===================================================================
--- uspace/srv/net/tl/tcp/ucall.h	(revision 899f1a95f22eca90d14c5209d80a487c9a0979f0)
+++ uspace/srv/net/tl/tcp/ucall.h	(revision 899f1a95f22eca90d14c5209d80a487c9a0979f0)
@@ -0,0 +1,64 @@
+/*
+ * Copyright (c) 2011 Jiri Svoboda
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ *   notice, this list of conditions and the following disclaimer.
+ * - Redistributions in binary form must reproduce the above copyright
+ *   notice, this list of conditions and the following disclaimer in the
+ *   documentation and/or other materials provided with the distribution.
+ * - The name of the author may not be used to endorse or promote products
+ *   derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/** @addtogroup tcp
+ * @{
+ */
+/** @file TCP user calls (close to those defined in the RFC)
+ */
+
+#ifndef UCALL_H
+#define UCALL_H
+
+#include <sys/types.h>
+#include "tcp_type.h"
+
+/*
+ * User calls
+ */
+extern tcp_error_t tcp_uc_open(tcp_sock_t *, tcp_sock_t *, acpass_t, tcp_conn_t **);
+extern tcp_error_t tcp_uc_send(tcp_conn_t *, void *, size_t, xflags_t);
+extern tcp_error_t tcp_uc_receive(tcp_conn_t *, void *, size_t, size_t *, xflags_t *);
+extern tcp_error_t tcp_uc_close(tcp_conn_t *);
+extern void tcp_uc_abort(tcp_conn_t *);
+extern void tcp_uc_status(tcp_conn_t *, tcp_conn_status_t *);
+
+/*
+ * Arriving segments
+ */
+extern void tcp_as_segment_arrived(tcp_sockpair_t *, tcp_segment_t *);
+
+/*
+ * Timeouts
+ */
+extern void tcp_to_user(void);
+
+#endif
+
+/** @}
+ */
