Index: uspace/srv/net/tl/tcp/header.c
===================================================================
--- uspace/srv/net/tl/tcp/header.c	(revision 6896409cd9474f55ef576a06416616a7b3889494)
+++ uspace/srv/net/tl/tcp/header.c	(revision 1812a0d8ca8948aa748d1e907a5241e37971f92c)
@@ -164,6 +164,48 @@
 }
 
+/** 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);
Index: uspace/srv/net/tl/tcp/header.h
===================================================================
--- uspace/srv/net/tl/tcp/header.h	(revision 6896409cd9474f55ef576a06416616a7b3889494)
+++ uspace/srv/net/tl/tcp/header.h	(revision 1812a0d8ca8948aa748d1e907a5241e37971f92c)
@@ -36,11 +36,12 @@
 #define HEADER_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/tcp.c
===================================================================
--- uspace/srv/net/tl/tcp/tcp.c	(revision 6896409cd9474f55ef576a06416616a7b3889494)
+++ uspace/srv/net/tl/tcp/tcp.c	(revision 1812a0d8ca8948aa748d1e907a5241e37971f92c)
@@ -36,4 +36,5 @@
 
 #include <async.h>
+#include <byteorder.h>
 #include <errno.h>
 #include <io/log.h>
@@ -41,9 +42,341 @@
 #include <task.h>
 
+#include <icmp_remote.h>
+#include <ip_client.h>
+#include <ip_interface.h>
+#include <ipc/services.h>
+#include <ipc/tl.h>
+#include <tl_common.h>
+#include <tl_skel.h>
+#include <packet_client.h>
+#include <packet_remote.h>
+
+#include "header.h"
 #include "ncsim.h"
 #include "rqueue.h"
+#include "std.h"
+#include "tcp.h"
 #include "test.h"
 
 #define NAME       "tcp"
+
+static async_sess_t *net_sess;
+static async_sess_t *icmp_sess;
+static async_sess_t *ip_sess;
+static 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(device_id_t device_id, packet_t *packet,
+    services_t error)
+{
+	int rc;
+	size_t offset;
+	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) {
+	case SERVICE_NONE:
+		break;
+	case SERVICE_ICMP:
+	default:
+		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 || (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) {
+		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;
+	}
+
+	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;
+}
+
+/** Receive packets from network layer. */
+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:
+			log_msg(LVL_DEBUG, "method = NET_TL_RECEIVED");
+			rc = packet_translate_remote(net_sess, &packet,
+			    IPC_GET_PACKET(*icall));
+			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:
+			log_msg(LVL_DEBUG, "method = %u",
+			    (unsigned)IPC_GET_IMETHOD(*icall));
+			async_answer_0(iid, ENOTSUP);
+			break;
+		}
+
+		iid = async_get_call(icall);
+	}
+}
+
+/** Transmit PDU over network layer. */
+void tcp_transmit_pdu(tcp_pdu_t *pdu)
+{
+	struct sockaddr_in dest;
+	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)
+{
+	log_msg(LVL_DEBUG, "tl_message()");
+	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()");
+
+	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)
@@ -59,5 +392,5 @@
 	}
 
-	printf(NAME ": Accepting connections\n");
+//	printf(NAME ": Accepting connections\n");
 //	task_retval(0);
 
@@ -69,6 +402,8 @@
 
 	tcp_test();
-
+/*
 	async_manager();
+*/
+	tl_module_start(SERVICE_TCP);
 
 	/* Not reached */
Index: uspace/srv/net/tl/tcp/tcp.h
===================================================================
--- uspace/srv/net/tl/tcp/tcp.h	(revision 1812a0d8ca8948aa748d1e907a5241e37971f92c)
+++ uspace/srv/net/tl/tcp/tcp.h	(revision 1812a0d8ca8948aa748d1e907a5241e37971f92c)
@@ -0,0 +1,45 @@
+/*
+ * 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 Control Protocol) network module
+ */
+
+#ifndef TCP_H
+#define TCP_H
+
+#include "tcp_type.h"
+
+extern void tcp_transmit_pdu(tcp_pdu_t *);
+
+#endif
+
+/** @}
+ */
Index: uspace/srv/net/tl/tcp/test.c
===================================================================
--- uspace/srv/net/tl/tcp/test.c	(revision 6896409cd9474f55ef576a06416616a7b3889494)
+++ uspace/srv/net/tl/tcp/test.c	(revision 1812a0d8ca8948aa748d1e907a5241e37971f92c)
@@ -103,5 +103,5 @@
 	tcp_uc_send(conn, (void *)msg, str_size(msg), 0);
 
-	async_usleep(1000*1000*3/**20*2*/);
+	async_usleep(1000*1000*20/**20*2*/);
 	printf("C: User close...\n");
 	tcp_uc_close(conn);
@@ -115,4 +115,6 @@
 
 	printf("tcp_test()\n");
+
+	async_usleep(1000*1000);
 
 	rc = thread_create(test_srv, NULL, "test_srv", &srv_tid);
Index: uspace/srv/net/tl/tcp/tqueue.c
===================================================================
--- uspace/srv/net/tl/tcp/tqueue.c	(revision 6896409cd9474f55ef576a06416616a7b3889494)
+++ uspace/srv/net/tl/tcp/tqueue.c	(revision 1812a0d8ca8948aa748d1e907a5241e37971f92c)
@@ -50,4 +50,5 @@
 #include "seq_no.h"
 #include "tqueue.h"
+#include "tcp.h"
 #include "tcp_type.h"
 
@@ -273,6 +274,16 @@
 	tcp_pdu_transmit(data, len);
 */
-	tcp_rqueue_bounce_seg(sp, seg);
+//	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);
 }
 
