Index: uspace/srv/net/tl/tcp/conn.c
===================================================================
--- uspace/srv/net/tl/tcp/conn.c	(revision bbf159a20a99023b10c2a8bcff9b59156f1a59eb)
+++ uspace/srv/net/tl/tcp/conn.c	(revision 7a8c1c4e5b8ffa09d9d43a81d222f21253b15586)
@@ -61,9 +61,9 @@
 static void tcp_conn_tw_timer_clear(tcp_conn_t *conn);
 
-/** Create new segment structure.
+/** Create new connection structure.
  *
  * @param lsock		Local socket (will be deeply copied)
  * @param fsock		Foreign socket (will be deeply copied)
- * @return		New segment or NULL
+ * @return		New connection or NULL
  */
 tcp_conn_t *tcp_conn_new(tcp_sock_t *lsock, tcp_sock_t *fsock)
@@ -120,4 +120,5 @@
 	conn->cstate = st_listen;
 	conn->reset = false;
+	conn->deleted = false;
 	conn->ap = ap_passive;
 	conn->fin_is_acked = false;
@@ -143,4 +144,46 @@
 }
 
+/** Destroy connection structure.
+ *
+ * Connection structure should be destroyed when both of two conditions are
+ * met: (1) user has deleted the connection and (2) the connection has entered
+ * closed state.
+ *
+ * @param conn		Connection
+ */
+static void tcp_conn_free(tcp_conn_t *conn)
+{
+	log_msg(LVL_DEBUG, "%s: tcp_conn_free(%p)", conn->name, conn);
+	tcp_tqueue_fini(&conn->retransmit);
+
+	if (conn->rcv_buf != NULL)
+		free(conn->rcv_buf);
+	if (conn->snd_buf != NULL)
+		free(conn->snd_buf);
+	if (conn->tw_timer != NULL)
+		fibril_timer_destroy(conn->tw_timer);
+	free(conn);
+}
+
+/** Delete connection.
+ *
+ * The caller promises not make no further references to @a conn.
+ * TCP will free @a conn eventually.
+ *
+ * @param conn		Connection
+ */
+void tcp_conn_delete(tcp_conn_t *conn)
+{
+	fibril_mutex_lock(&conn->cstate_lock);
+	conn->deleted = true;
+
+	if (conn->cstate == st_closed) {
+		fibril_mutex_unlock(&conn->cstate_lock);
+		tcp_conn_free(conn);
+	} else {
+		fibril_mutex_unlock(&conn->cstate_lock);
+	}
+}
+
 /** Enlist connection.
  *
@@ -166,5 +209,11 @@
 	conn->cstate = nstate;
 	fibril_condvar_broadcast(&conn->cstate_cv);
-	fibril_mutex_unlock(&conn->cstate_lock);
+
+	if (nstate == st_closed && conn->deleted) {
+		fibril_mutex_unlock(&conn->cstate_lock);
+		tcp_conn_free(conn);
+	} else {
+		fibril_mutex_unlock(&conn->cstate_lock);
+	}
 }
 
Index: uspace/srv/net/tl/tcp/conn.h
===================================================================
--- uspace/srv/net/tl/tcp/conn.h	(revision bbf159a20a99023b10c2a8bcff9b59156f1a59eb)
+++ uspace/srv/net/tl/tcp/conn.h	(revision 7a8c1c4e5b8ffa09d9d43a81d222f21253b15586)
@@ -40,4 +40,5 @@
 
 extern tcp_conn_t *tcp_conn_new(tcp_sock_t *, tcp_sock_t *);
+extern void tcp_conn_delete(tcp_conn_t *);
 extern void tcp_conn_add(tcp_conn_t *);
 extern void tcp_conn_remove(tcp_conn_t *);
Index: uspace/srv/net/tl/tcp/sock.c
===================================================================
--- uspace/srv/net/tl/tcp/sock.c	(revision bbf159a20a99023b10c2a8bcff9b59156f1a59eb)
+++ uspace/srv/net/tl/tcp/sock.c	(revision 7a8c1c4e5b8ffa09d9d43a81d222f21253b15586)
@@ -615,4 +615,6 @@
 	} while (trc == TCP_EOK);
 
+	tcp_uc_delete(socket->conn);
+
 	rc = socket_destroy(net_sess, socket_id, &client->sockets, &gsock,
 	    tcp_free_sock_data);
Index: uspace/srv/net/tl/tcp/tcp_type.h
===================================================================
--- uspace/srv/net/tl/tcp/tcp_type.h	(revision bbf159a20a99023b10c2a8bcff9b59156f1a59eb)
+++ uspace/srv/net/tl/tcp/tcp_type.h	(revision 7a8c1c4e5b8ffa09d9d43a81d222f21253b15586)
@@ -166,4 +166,6 @@
 	/** True if connection was reset */
 	bool reset;
+	/** True if connection was deleted by user */
+	bool deleted;
 	/** Protects @c cstate */
 	fibril_mutex_t cstate_lock;
Index: uspace/srv/net/tl/tcp/ucall.c
===================================================================
--- uspace/srv/net/tl/tcp/ucall.c	(revision bbf159a20a99023b10c2a8bcff9b59156f1a59eb)
+++ uspace/srv/net/tl/tcp/ucall.c	(revision 7a8c1c4e5b8ffa09d9d43a81d222f21253b15586)
@@ -251,4 +251,15 @@
 }
 
+/** Delete connection user call.
+ *
+ * (Not in spec.) Inform TCP that the user is done with this connection
+ * and will not make any further calls/references to it. TCP can deallocate
+ * the connection from now on.
+ */
+void tcp_uc_delete(tcp_conn_t *conn)
+{
+	log_msg(LVL_DEBUG, "tcp_uc_delete()");
+	tcp_conn_delete(conn);
+}
 
 /*
Index: uspace/srv/net/tl/tcp/ucall.h
===================================================================
--- uspace/srv/net/tl/tcp/ucall.h	(revision bbf159a20a99023b10c2a8bcff9b59156f1a59eb)
+++ uspace/srv/net/tl/tcp/ucall.h	(revision 7a8c1c4e5b8ffa09d9d43a81d222f21253b15586)
@@ -48,4 +48,5 @@
 extern void tcp_uc_abort(tcp_conn_t *);
 extern void tcp_uc_status(tcp_conn_t *, tcp_conn_status_t *);
+extern void tcp_uc_delete(tcp_conn_t *);
 
 /*
