Index: uspace/srv/net/tl/tcp/conn.c
===================================================================
--- uspace/srv/net/tl/tcp/conn.c	(revision 8218b6bbf08ac4c9a5e61142d2b5a6fa28d09f5f)
+++ uspace/srv/net/tl/tcp/conn.c	(revision 7cf7ded59bfcb0ce3986ba191f580a7814749ae4)
@@ -69,4 +69,5 @@
 {
 	tcp_conn_t *conn = NULL;
+	bool tqueue_inited = false;
 
 	/* Allocate connection structure */
@@ -105,5 +106,8 @@
 
 	/* Initialize retransmission queue */
-	tcp_tqueue_init(&conn->retransmit, conn);
+	if (tcp_tqueue_init(&conn->retransmit, conn) != EOK)
+		goto error;
+
+	tqueue_inited = true;
 
 	conn->cstate = st_listen;
@@ -116,4 +120,6 @@
 
 error:
+	if (tqueue_inited)
+		tcp_tqueue_fini(&conn->retransmit);
 	if (conn != NULL && conn->rcv_buf != NULL)
 		free(conn->rcv_buf);
@@ -180,4 +186,5 @@
 		break;
 	default:
+		log_msg(LVL_ERROR, "Connection state %d", conn->cstate);
 		assert(false);
 	}
@@ -264,4 +271,5 @@
 		return true;
 	case st_closed:
+		log_msg(LVL_WARN, "state=%d", (int) conn->cstate);
 		assert(false);
 	}
@@ -347,5 +355,5 @@
 
 	if ((seg->ctrl & CTL_RST) != 0) {
-		log_msg(LVL_DEBUG, "Connection reset.");
+		log_msg(LVL_DEBUG, "Connection reset. -> Closed");
 		/* XXX Signal user error */
 		conn->cstate = st_closed;
@@ -413,5 +421,11 @@
 	log_msg(LVL_DEBUG, "tcp_conn_sa_seq(%p, %p)", conn, seg);
 
-	/* XXX Discard old duplicates */
+	/* 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 */
@@ -463,6 +477,23 @@
 static cproc_t tcp_conn_seg_proc_syn(tcp_conn_t *conn, tcp_segment_t *seg)
 {
-	/* TODO */
-	return cp_continue;
+	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;
 }
 
@@ -790,4 +821,6 @@
 {
 	log_msg(LVL_DEBUG, "tcp_conn_seg_proc_fin(%p, %p)", 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. */
@@ -896,8 +929,11 @@
 	 * incoming segments queue.
 	 */
-	if (seg->len > 0)
+	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
+	} else {
 		tcp_segment_delete(seg);
+	}
 }
 
@@ -927,4 +963,5 @@
 		tcp_conn_sa_queue(conn, seg); break;
 	case st_closed:
+		log_msg(LVL_DEBUG, "state=%d", (int) conn->cstate);
 		assert(false);
 	}
Index: uspace/srv/net/tl/tcp/ncsim.c
===================================================================
--- uspace/srv/net/tl/tcp/ncsim.c	(revision 8218b6bbf08ac4c9a5e61142d2b5a6fa28d09f5f)
+++ uspace/srv/net/tl/tcp/ncsim.c	(revision 7cf7ded59bfcb0ce3986ba191f580a7814749ae4)
@@ -48,4 +48,5 @@
 #include "ncsim.h"
 #include "rqueue.h"
+#include "segment.h"
 #include "tcp_type.h"
 
@@ -73,5 +74,14 @@
 	link_t *link;
 
-	log_msg(LVL_DEBUG, "tcp_ncsim_insert_seg()");
+	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));
Index: uspace/srv/net/tl/tcp/seq_no.c
===================================================================
--- uspace/srv/net/tl/tcp/seq_no.c	(revision 8218b6bbf08ac4c9a5e61142d2b5a6fa28d09f5f)
+++ uspace/srv/net/tl/tcp/seq_no.c	(revision 7cf7ded59bfcb0ce3986ba191f580a7814749ae4)
@@ -88,4 +88,10 @@
 }
 
+/** 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.
  *
Index: uspace/srv/net/tl/tcp/seq_no.h
===================================================================
--- uspace/srv/net/tl/tcp/seq_no.h	(revision 8218b6bbf08ac4c9a5e61142d2b5a6fa28d09f5f)
+++ uspace/srv/net/tl/tcp/seq_no.h	(revision 7cf7ded59bfcb0ce3986ba191f580a7814749ae4)
@@ -41,4 +41,5 @@
 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);
Index: uspace/srv/net/tl/tcp/state.c
===================================================================
--- uspace/srv/net/tl/tcp/state.c	(revision 8218b6bbf08ac4c9a5e61142d2b5a6fa28d09f5f)
+++ uspace/srv/net/tl/tcp/state.c	(revision 7cf7ded59bfcb0ce3986ba191f580a7814749ae4)
@@ -112,4 +112,12 @@
 	log_msg(LVL_DEBUG, "tcp_uc_receive()");
 
+	/*
+	 * XXX Handle all states for all user calls properly, return
+	 * errors as appropriate.
+	 */
+	if (conn->cstate == st_closed)
+		return;
+
+
 	fibril_mutex_lock(&conn->rcv_buf_lock);
 
@@ -186,5 +194,5 @@
 
 	conn = tcp_conn_find(sp);
-	if (conn != NULL) {
+	if (conn != NULL && conn->cstate != st_closed) {
 		tcp_conn_segment_arrived(conn, seg);
 	} else {
Index: uspace/srv/net/tl/tcp/tcp_type.h
===================================================================
--- uspace/srv/net/tl/tcp/tcp_type.h	(revision 8218b6bbf08ac4c9a5e61142d2b5a6fa28d09f5f)
+++ uspace/srv/net/tl/tcp/tcp_type.h	(revision 7cf7ded59bfcb0ce3986ba191f580a7814749ae4)
@@ -94,4 +94,5 @@
 } tcp_sockpair_t;
 
+/** Connection incoming segments queue */
 typedef struct {
 	struct tcp_conn *conn;
@@ -99,10 +100,15 @@
 } tcp_iqueue_t;
 
+/** Retransmission queue */
 typedef struct {
 	struct tcp_conn *conn;
 	list_t list;
+
+	/** Retransmission timer */
+	fibril_timer_t *timer;
 } tcp_tqueue_t;
 
 typedef struct tcp_conn {
+	char *name;
 	link_t link;
 
@@ -221,6 +227,8 @@
 } tcp_iqueue_entry_t;
 
-typedef struct {
-	link_t link;
+/** Retransmission queue entry */
+typedef struct {
+	link_t link;
+	tcp_conn_t *conn;
 	tcp_segment_t *seg;
 } tcp_tqueue_entry_t;
Index: uspace/srv/net/tl/tcp/test.c
===================================================================
--- uspace/srv/net/tl/tcp/test.c	(revision 8218b6bbf08ac4c9a5e61142d2b5a6fa28d09f5f)
+++ uspace/srv/net/tl/tcp/test.c	(revision 7cf7ded59bfcb0ce3986ba191f580a7814749ae4)
@@ -59,4 +59,5 @@
 	sock.addr.ipv4 = 0x7f000001;
 	tcp_uc_open(80, &sock, ap_passive, &conn);
+	conn->name = (char *) "S";
 
 	while (true) {
@@ -73,5 +74,5 @@
 	}
 
-	async_usleep(1000*1000);
+	async_usleep(/*10**/1000*1000);
 
 	printf("test_srv() close connection\n");
@@ -94,9 +95,10 @@
 	async_usleep(1000*1000*3);
 	tcp_uc_open(1024, &sock, ap_active, &conn);
+	conn->name = (char *) "C";
 
 	async_usleep(1000*1000*10);
 	tcp_uc_send(conn, (void *)msg, str_size(msg), 0);
 
-	async_usleep(1000*1000*3);
+	async_usleep(1000*1000*3/**20*2*/);
 	tcp_uc_close(conn);
 }
Index: uspace/srv/net/tl/tcp/tqueue.c
===================================================================
--- uspace/srv/net/tl/tcp/tqueue.c	(revision 8218b6bbf08ac4c9a5e61142d2b5a6fa28d09f5f)
+++ uspace/srv/net/tl/tcp/tqueue.c	(revision 7cf7ded59bfcb0ce3986ba191f580a7814749ae4)
@@ -36,4 +36,6 @@
 
 #include <adt/list.h>
+#include <errno.h>
+#include <fibril_synch.h>
 #include <byteorder.h>
 #include <io/log.h>
@@ -50,8 +52,28 @@
 #include "tcp_type.h"
 
-void tcp_tqueue_init(tcp_tqueue_t *tqueue, tcp_conn_t *conn)
+#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_fini(tcp_tqueue_t *tqueue)
+{
+	if (tqueue->timer != NULL) {
+		fibril_timer_destroy(tqueue->timer);
+		tqueue->timer = NULL;
+	}
 }
 
@@ -92,8 +114,17 @@
 		}
 
+		tqe->conn = conn;
 		tqe->seg = rt_seg;
 		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.
@@ -105,14 +136,4 @@
 
 	seg->seq = conn->snd_nxt;
-	seg->wnd = conn->rcv_wnd;
-
-	log_msg(LVL_DEBUG, "SEG.SEQ=%" PRIu32 ", SEG.WND=%" PRIu32,
-	    seg->seq, seg->wnd);
-
-	if ((seg->ctrl & CTL_ACK) != 0)
-		seg->ack = conn->rcv_nxt;
-	else
-		seg->ack = 0;
-
 	conn->snd_nxt += seg->len;
 
@@ -204,8 +225,15 @@
 			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 */
@@ -213,13 +241,78 @@
 }
 
+void tcp_conn_transmit_segment(tcp_conn_t *conn, tcp_segment_t *seg)
+{
+	log_msg(LVL_DEBUG, "tcp_conn_transmit_segment(%p, %p)", 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(%p, %p)", sp, seg);
+
+	log_msg(LVL_DEBUG, "SEG.SEQ=%" PRIu32 ", SEG.WND=%" PRIu32,
+	    seg->seq, seg->wnd);
 /*
 	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_rqueue_bounce_seg(sp, seg);
+//	tcp_ncsim_bounce_seg(sp, seg);
+}
+
+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);
+	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 8218b6bbf08ac4c9a5e61142d2b5a6fa28d09f5f)
+++ uspace/srv/net/tl/tcp/tqueue.h	(revision 7cf7ded59bfcb0ce3986ba191f580a7814749ae4)
@@ -39,9 +39,12 @@
 #include "tcp_type.h"
 
-extern void tcp_tqueue_init(tcp_tqueue_t *, tcp_conn_t *);
+extern int tcp_tqueue_init(tcp_tqueue_t *, tcp_conn_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 *);
