Index: uspace/app/netecho/netecho.c
===================================================================
--- uspace/app/netecho/netecho.c	(revision ee603c4748782944b7cead483ab765c2c120b05b)
+++ uspace/app/netecho/netecho.c	(revision 92b42442e2fe12870594cc9064095c5f587a6fbd)
@@ -373,4 +373,5 @@
 		address_in.sin_family = AF_INET;
 		address_in.sin_port = htons(port);
+		address_in.sin_addr.s_addr = INADDR_ANY;
 		address = (struct sockaddr *) &address_in;
 		addrlen = sizeof(address_in);
Index: uspace/lib/c/include/net/in.h
===================================================================
--- uspace/lib/c/include/net/in.h	(revision ee603c4748782944b7cead483ab765c2c120b05b)
+++ uspace/lib/c/include/net/in.h	(revision 92b42442e2fe12870594cc9064095c5f587a6fbd)
@@ -45,4 +45,6 @@
 #define INET_ADDRSTRLEN  (4 * 3 + 3 + 1)
 
+#define INADDR_ANY 0
+
 /** Type definition of the INET address.
  * @see in_addr
Index: uspace/srv/tcp/conn.c
===================================================================
--- uspace/srv/tcp/conn.c	(revision ee603c4748782944b7cead483ab765c2c120b05b)
+++ uspace/srv/tcp/conn.c	(revision 92b42442e2fe12870594cc9064095c5f587a6fbd)
@@ -309,9 +309,5 @@
 }
 
-/** Compare two sockets.
- *
- * Two sockets are equal if the address is equal and the port number
- * is equal.
- */
+/** Match socket with pattern. */
 static bool tcp_socket_match(tcp_sock_t *sock, tcp_sock_t *patt)
 {
@@ -332,5 +328,5 @@
 }
 
-/** Match socket with pattern. */
+/** Match socket pair with pattern. */
 static bool tcp_sockpair_match(tcp_sockpair_t *sp, tcp_sockpair_t *pattern)
 {
@@ -357,5 +353,5 @@
 tcp_conn_t *tcp_conn_find_ref(tcp_sockpair_t *sp)
 {
-	log_msg(LVL_DEBUG, "tcp_conn_find(%p)", sp);
+	log_msg(LVL_DEBUG, "tcp_conn_find_ref(%p)", sp);
 
 	fibril_mutex_lock(&conn_list_lock);
Index: uspace/srv/udp/assoc.c
===================================================================
--- uspace/srv/udp/assoc.c	(revision ee603c4748782944b7cead483ab765c2c120b05b)
+++ uspace/srv/udp/assoc.c	(revision 92b42442e2fe12870594cc9064095c5f587a6fbd)
@@ -51,4 +51,9 @@
 FIBRIL_MUTEX_INITIALIZE(assoc_list_lock);
 
+static udp_assoc_t *udp_assoc_find_ref(udp_sockpair_t *);
+static int udp_assoc_queue_msg(udp_assoc_t *, udp_sockpair_t *, udp_msg_t *);
+static bool udp_socket_match(udp_sock_t *, udp_sock_t *);
+static bool udp_sockpair_match(udp_sockpair_t *, udp_sockpair_t *);
+
 /** Create new association structure.
  *
@@ -61,5 +66,5 @@
 	udp_assoc_t *assoc = NULL;
 
-	/* Allocate connection structure */
+	/* Allocate association structure */
 	assoc = calloc(1, sizeof(udp_assoc_t));
 	if (assoc == NULL)
@@ -95,5 +100,5 @@
  * as an extra reference.
  *
- * @param conn		Connection
+ * @param assoc		Association
  */
 static void udp_assoc_free(udp_assoc_t *assoc)
@@ -103,6 +108,10 @@
 	while (!list_empty(&assoc->rcv_queue)) {
 		link_t *link = list_first(&assoc->rcv_queue);
-//		....;
+		udp_rcv_queue_entry_t *rqe = list_get_instance(link,
+		    udp_rcv_queue_entry_t, link);
 		list_remove(link);
+
+		udp_msg_delete(rqe->msg);
+		free(rqe);
 	}
 
@@ -219,4 +228,7 @@
 	int rc;
 
+	log_msg(LVL_DEBUG, "udp_assoc_send(%p, %p, %p)",
+	    assoc, fsock, msg);
+
 	/* @a fsock can be used to override the foreign socket */
 	sp = assoc->ident;
@@ -240,4 +252,158 @@
 }
 
+/** Get a received message.
+ *
+ * Pull one message from the association's receive queue.
+ */
+int udp_assoc_recv(udp_assoc_t *assoc, udp_msg_t **msg, udp_sock_t *fsock)
+{
+	link_t *link;
+	udp_rcv_queue_entry_t *rqe;
+
+	log_msg(LVL_DEBUG, "udp_assoc_recv()");
+
+	fibril_mutex_lock(&assoc->lock);
+	while (list_empty(&assoc->rcv_queue)) {
+		log_msg(LVL_DEBUG, "udp_assoc_recv() - waiting");
+		fibril_condvar_wait(&assoc->rcv_queue_cv, &assoc->lock);
+	}
+
+	log_msg(LVL_DEBUG, "udp_assoc_recv() - got a message");
+	link = list_first(&assoc->rcv_queue);
+	rqe = list_get_instance(link, udp_rcv_queue_entry_t, link);
+	list_remove(link);
+	fibril_mutex_unlock(&assoc->lock);
+
+	*msg = rqe->msg;
+	*fsock = rqe->sp.foreign;
+
+	return EOK;
+}
+
+/** Message received.
+ *
+ * Find the association to which the message belongs and queue it.
+ */
+void udp_assoc_received(udp_sockpair_t *rsp, udp_msg_t *msg)
+{
+	udp_assoc_t *assoc;
+	int rc;
+
+	log_msg(LVL_DEBUG, "udp_assoc_received(%p, %p)", rsp, msg);
+
+	assoc = udp_assoc_find_ref(rsp);
+	if (assoc == NULL) {
+		log_msg(LVL_DEBUG, "No association found. Message dropped.");
+		/* XXX Generate ICMP error. */
+		/* XXX Might propagate error directly by error return. */
+		return;
+	}
+
+	rc = udp_assoc_queue_msg(assoc, rsp, msg);
+	if (rc != EOK) {
+		log_msg(LVL_DEBUG, "Out of memory. Message dropped.");
+		/* XXX Generate ICMP error? */
+	}
+}
+
+static int udp_assoc_queue_msg(udp_assoc_t *assoc, udp_sockpair_t *sp,
+    udp_msg_t *msg)
+{
+	udp_rcv_queue_entry_t *rqe;
+
+	log_msg(LVL_DEBUG, "udp_assoc_queue_msg(%p, %p, %p)",
+	    assoc, sp, msg);
+
+	rqe = calloc(1, sizeof(udp_rcv_queue_entry_t));
+	if (rqe == NULL)
+		return ENOMEM;
+
+	link_initialize(&rqe->link);
+	rqe->sp = *sp;
+	rqe->msg = msg;
+
+	fibril_mutex_lock(&assoc->lock);
+	list_append(&rqe->link, &assoc->rcv_queue);
+	fibril_mutex_unlock(&assoc->lock);
+
+	fibril_condvar_broadcast(&assoc->rcv_queue_cv);
+
+	return EOK;
+}
+
+/** Match socket with pattern. */
+static bool udp_socket_match(udp_sock_t *sock, udp_sock_t *patt)
+{
+	log_msg(LVL_DEBUG, "udp_socket_match(sock=(%x,%u), pat=(%x,%u))",
+	    sock->addr.ipv4, sock->port, patt->addr.ipv4, patt->port);
+
+	if (patt->addr.ipv4 != UDP_IPV4_ANY &&
+	    patt->addr.ipv4 != sock->addr.ipv4)
+		return false;
+
+	if (patt->port != UDP_PORT_ANY &&
+	    patt->port != sock->port)
+		return false;
+
+	log_msg(LVL_DEBUG, " -> match");
+
+	return true;
+}
+
+/** Match socket pair with pattern. */
+static bool udp_sockpair_match(udp_sockpair_t *sp, udp_sockpair_t *pattern)
+{
+	log_msg(LVL_DEBUG, "udp_sockpair_match(%p, %p)", sp, pattern);
+
+	if (!udp_socket_match(&sp->local, &pattern->local))
+		return false;
+
+	if (!udp_socket_match(&sp->foreign, &pattern->foreign))
+		return false;
+
+	log_msg(LVL_DEBUG, "Socket pair matched.");
+	return true;
+}
+
+
+/** Find association structure for specified socket pair.
+ *
+ * An association is uniquely identified by a socket pair. Look up our
+ * association map and return association structure based on socket pair.
+ * The association reference count is bumped by one.
+ *
+ * @param sp	Socket pair
+ * @return	Association structure or NULL if not found.
+ */
+static udp_assoc_t *udp_assoc_find_ref(udp_sockpair_t *sp)
+{
+	log_msg(LVL_DEBUG, "udp_assoc_find_ref(%p)", sp);
+
+	fibril_mutex_lock(&assoc_list_lock);
+
+	list_foreach(assoc_list, link) {
+		udp_assoc_t *assoc = list_get_instance(link, udp_assoc_t, link);
+		udp_sockpair_t *asp = &assoc->ident;
+		log_msg(LVL_DEBUG, "compare with assoc (f:(%x,%u), l:(%x,%u))",
+		    asp->foreign.addr.ipv4, asp->foreign.port,
+		    asp->local.addr.ipv4, asp->local.port);
+
+		/* Skip unbound associations */
+		if (asp->local.port == UDP_PORT_ANY)
+			continue;
+
+		if (udp_sockpair_match(sp, asp)) {
+			log_msg(LVL_DEBUG, "Returning assoc %p", assoc);
+			udp_assoc_addref(assoc);
+			fibril_mutex_unlock(&assoc_list_lock);
+			return assoc;
+		}
+	}
+
+	fibril_mutex_unlock(&assoc_list_lock);
+	return NULL;
+}
+
+
 /**
  * @}
Index: uspace/srv/udp/assoc.h
===================================================================
--- uspace/srv/udp/assoc.h	(revision ee603c4748782944b7cead483ab765c2c120b05b)
+++ uspace/srv/udp/assoc.h	(revision 92b42442e2fe12870594cc9064095c5f587a6fbd)
@@ -48,4 +48,7 @@
 extern void udp_assoc_set_local(udp_assoc_t *, udp_sock_t *);
 extern int udp_assoc_send(udp_assoc_t *, udp_sock_t *, udp_msg_t *);
+extern int udp_assoc_recv(udp_assoc_t *, udp_msg_t **, udp_sock_t *);
+extern void udp_assoc_received(udp_sockpair_t *, udp_msg_t *);
+
 
 #endif
Index: uspace/srv/udp/pdu.c
===================================================================
--- uspace/srv/udp/pdu.c	(revision ee603c4748782944b7cead483ab765c2c120b05b)
+++ uspace/srv/udp/pdu.c	(revision 92b42442e2fe12870594cc9064095c5f587a6fbd)
@@ -93,5 +93,5 @@
 }
 
-static udp_pdu_t *udp_pdu_new(void)
+udp_pdu_t *udp_pdu_new(void)
 {
 	return calloc(1, sizeof(udp_pdu_t));
Index: uspace/srv/udp/pdu.h
===================================================================
--- uspace/srv/udp/pdu.h	(revision ee603c4748782944b7cead483ab765c2c120b05b)
+++ uspace/srv/udp/pdu.h	(revision 92b42442e2fe12870594cc9064095c5f587a6fbd)
@@ -40,4 +40,5 @@
 #include "udp_type.h"
 
+extern udp_pdu_t *udp_pdu_new(void);
 extern void udp_pdu_delete(udp_pdu_t *);
 extern int udp_pdu_decode(udp_pdu_t *, udp_sockpair_t *, udp_msg_t **);
Index: uspace/srv/udp/sock.c
===================================================================
--- uspace/srv/udp/sock.c	(revision ee603c4748782944b7cead483ab765c2c120b05b)
+++ uspace/srv/udp/sock.c	(revision 92b42442e2fe12870594cc9064095c5f587a6fbd)
@@ -114,5 +114,4 @@
 	fibril_mutex_initialize(&sock->lock);
 	sock->client = client;
-	sock->laddr.ipv4 = UDP_IPV4_ANY;
 
 	rc = udp_uc_create(&sock->assoc);
@@ -189,5 +188,5 @@
 
 	fsock.addr.ipv4 = uint32_t_be2host(addr->sin_addr.s_addr);
-	fsock.port = uint16_t_be2host(sock_core->port);
+	fsock.port = sock_core->port;
 	urc = udp_uc_set_local(socket->assoc, &fsock);
 
@@ -209,4 +208,6 @@
 	}
 
+	udp_sock_notify_data(sock_core);
+
 	log_msg(LVL_DEBUG, " - success");
 	async_answer_0(callid, rc);
@@ -286,5 +287,5 @@
 
 	if (sock_core->port == 0) {
-		/* Implicitly bind socket */
+		/* Implicitly bind socket to port */
 		rc = socket_bind(&client->sockets, &gsock, SOCKET_GET_SOCKET_ID(call),
 		    addr, addr_size, UDP_FREE_PORTS_START, UDP_FREE_PORTS_END,
@@ -294,8 +295,32 @@
 			goto out;
 		}
+
+		udp_sock_notify_data(sock_core);
 	}
 
 	socket = (udp_sockdata_t *)sock_core->specific_data;
 	fibril_mutex_lock(&socket->lock);
+
+	if (socket->assoc->ident.local.addr.ipv4 == UDP_IPV4_ANY) {
+		/* Determine local IP address */
+		inet_addr_t loc_addr, rem_addr;
+
+		rem_addr.ipv4 = fsockp ? fsock.addr.ipv4 :
+		    socket->assoc->ident.foreign.addr.ipv4;
+
+		rc = inet_get_srcaddr(&rem_addr, 0, &loc_addr);
+		if (rc != EOK) {
+			fibril_mutex_unlock(&socket->lock);
+			async_answer_0(callid, rc);
+			log_msg(LVL_DEBUG, "udp_sock_sendto: Failed to "
+			    "determine local address.");
+			return;
+		}
+
+		socket->assoc->ident.local.addr.ipv4 = loc_addr.ipv4;
+		log_msg(LVL_DEBUG, "Local IP address is %x",
+		    socket->assoc->ident.local.addr.ipv4);
+	}
+
 
 	assert(socket->assoc != NULL);
@@ -394,5 +419,5 @@
 	urc = udp_uc_receive(socket->assoc, buffer, FRAGMENT_SIZE, &data_len,
 	    &xflags, &rsock);
-	log_msg(LVL_DEBUG, "**** udp_uc_receive done");
+	log_msg(LVL_DEBUG, "**** udp_uc_receive done, data_len=%zu", data_len);
 
 	switch (urc) {
@@ -459,6 +484,8 @@
 		rc = EOVERFLOW;
 
+	log_msg(LVL_DEBUG, "read_data_length <- %zu", length);
 	SOCKET_SET_READ_DATA_LENGTH(answer, length);
-	answer_call(callid, EOK, &answer, 1);
+	SOCKET_SET_ADDRESS_LENGTH(answer, sizeof(addr));
+	answer_call(callid, EOK, &answer, 3);
 
 	/* Push one fragment notification to client's queue */
@@ -526,9 +553,10 @@
 
 	while (true) {
+		log_msg(LVL_DEBUG, "udp_sock_connection: wait");
 		callid = async_get_call(&call);
 		if (!IPC_GET_IMETHOD(call))
 			break;
 
-		log_msg(LVL_DEBUG, "udp_sock_connection: METHOD=%d\n",
+		log_msg(LVL_DEBUG, "udp_sock_connection: METHOD=%d",
 		    (int)IPC_GET_IMETHOD(call));
 
Index: uspace/srv/udp/ucall.c
===================================================================
--- uspace/srv/udp/ucall.c	(revision ee603c4748782944b7cead483ab765c2c120b05b)
+++ uspace/srv/udp/ucall.c	(revision 92b42442e2fe12870594cc9064095c5f587a6fbd)
@@ -36,4 +36,5 @@
 
 #include <io/log.h>
+#include <macros.h>
 
 #include "assoc.h"
@@ -97,7 +98,17 @@
     size_t *rcvd, xflags_t *xflags, udp_sock_t *fsock)
 {
-//	size_t xfer_size;
+	size_t xfer_size;
+	udp_msg_t *msg;
+	int rc;
 
 	log_msg(LVL_DEBUG, "%s: udp_uc_receive()", assoc->name);
+	rc = udp_assoc_recv(assoc, &msg, fsock);
+	switch (rc) {
+	}
+
+	xfer_size = min(size, msg->data_size);
+	memcpy(buf, msg->data, xfer_size);
+	*rcvd = xfer_size;
+
 	return UDP_EOK;
 }
Index: uspace/srv/udp/udp_inet.c
===================================================================
--- uspace/srv/udp/udp_inet.c	(revision ee603c4748782944b7cead483ab765c2c120b05b)
+++ uspace/srv/udp/udp_inet.c	(revision 92b42442e2fe12870594cc9064095c5f587a6fbd)
@@ -43,4 +43,6 @@
 #include <task.h>
 
+#include "assoc.h"
+#include "pdu.h"
 #include "std.h"
 #include "udp_inet.h"
@@ -48,5 +50,5 @@
 
 static int udp_inet_ev_recv(inet_dgram_t *dgram);
-//static void tcp_received_pdu(tcp_pdu_t *pdu);
+static void udp_received_pdu(udp_pdu_t *pdu);
 
 static inet_ev_ops_t udp_inet_ev_ops = {
@@ -57,63 +59,20 @@
 static int udp_inet_ev_recv(inet_dgram_t *dgram)
 {
-	uint8_t *pdu_raw;
-	size_t pdu_raw_size;
+	udp_pdu_t *pdu;
 
 	log_msg(LVL_DEBUG, "udp_inet_ev_recv()");
 
-	pdu_raw = dgram->data;
-	pdu_raw_size = dgram->size;
+	pdu = udp_pdu_new();
+	pdu->data = dgram->data;
+	pdu->data_size = dgram->size;
 
-	(void)pdu_raw;
-	(void)pdu_raw_size;
-	/* Split into header and payload. */
-/*
-	log_msg(LVL_DEBUG, "tcp_inet_ev_recv() - split header/payload");
+	pdu->src.ipv4 = dgram->src.ipv4;
+	pdu->dest.ipv4 = dgram->dest.ipv4;
+	log_msg(LVL_DEBUG, "src: 0x%08x, dest: 0x%08x",
+	    pdu->src.ipv4, pdu->dest.ipv4);
 
-	tcp_pdu_t *pdu;
-	size_t hdr_size;
-	tcp_header_t *hdr;
-	uint32_t data_offset;
+	udp_received_pdu(pdu);
+	udp_pdu_delete(pdu);
 
-	if (pdu_raw_size < sizeof(tcp_header_t)) {
-		log_msg(LVL_WARN, "pdu_raw_size = %zu < sizeof(tcp_header_t) = %zu",
-		    pdu_raw_size, sizeof(tcp_header_t));
-		return EINVAL;
-	}
-
-	hdr = (tcp_header_t *)pdu_raw;
-	data_offset = BIT_RANGE_EXTRACT(uint32_t, DF_DATA_OFFSET_h, DF_DATA_OFFSET_l,
-	    uint16_t_be2host(hdr->doff_flags));
-
-	hdr_size = sizeof(uint32_t) * data_offset;
-
-	if (pdu_raw_size < hdr_size) {
-		log_msg(LVL_WARN, "pdu_raw_size = %zu < hdr_size = %zu",
-		    pdu_raw_size, hdr_size);
-		return EINVAL;
-	}
-
-	if (hdr_size < sizeof(tcp_header_t)) {
-		log_msg(LVL_WARN, "hdr_size = %zu < sizeof(tcp_header_t) = %zu",
-		    hdr_size, sizeof(tcp_header_t));		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;
-	}
-
-	pdu->src_addr.ipv4 = dgram->src.ipv4;
-	pdu->dest_addr.ipv4 = dgram->dest.ipv4;
-	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;
 }
@@ -124,4 +83,6 @@
 	int rc;
 	inet_dgram_t dgram;
+
+	log_msg(LVL_DEBUG, "udp_transmit_pdu()");
 
 	dgram.src.ipv4 = pdu->src.ipv4;
@@ -139,21 +100,24 @@
 
 /** Process received PDU. */
-/*
-static void tcp_received_pdu(tcp_pdu_t *pdu)
+static void udp_received_pdu(udp_pdu_t *pdu)
 {
-	tcp_segment_t *dseg;
-	tcp_sockpair_t rident;
+	udp_msg_t *dmsg;
+	udp_sockpair_t rident;
 
-	log_msg(LVL_DEBUG, "tcp_received_pdu()");
+	log_msg(LVL_DEBUG, "udp_received_pdu()");
 
-	if (tcp_pdu_decode(pdu, &rident, &dseg) != EOK) {
+	if (udp_pdu_decode(pdu, &rident, &dmsg) != EOK) {
 		log_msg(LVL_WARN, "Not enough memory. PDU dropped.");
 		return;
 	}
-*/
-	/* Insert decoded segment into rqueue */
-/*	tcp_rqueue_insert_seg(&rident, dseg);
+
+	/*
+	 * Insert decoded message into appropriate receive queue.
+	 * This transfers ownership of dmsg to the callee, we do not
+	 * free it.
+	 */
+	udp_assoc_received(&rident, dmsg);
 }
-*/
+
 int udp_inet_init(void)
 {
Index: uspace/srv/udp/udp_type.h
===================================================================
--- uspace/srv/udp/udp_type.h	(revision ee603c4748782944b7cead483ab765c2c120b05b)
+++ uspace/srv/udp/udp_type.h	(revision 92b42442e2fe12870594cc9064095c5f587a6fbd)
@@ -60,4 +60,8 @@
 enum netaddr {
 	UDP_IPV4_ANY = 0
+};
+
+enum tcp_port {
+	UDP_PORT_ANY = 0
 };
 
@@ -137,7 +141,14 @@
 	/** Connection */
 	udp_assoc_t *assoc;
-	/** Local address */
-	netaddr_t laddr;
 } udp_sockdata_t;
+
+typedef struct {
+	/** Link to receive queue */
+	link_t link;
+	/** Socket pair */
+	udp_sockpair_t sp;
+	/** Message */
+	udp_msg_t *msg;
+} udp_rcv_queue_entry_t;
 
 #endif
