Index: uspace/lib/c/generic/inet.c
===================================================================
--- uspace/lib/c/generic/inet.c	(revision ab6326bcc8e9a50d34db6b657d59aaddc9baca6c)
+++ uspace/lib/c/generic/inet.c	(revision 8d48c7e46fe4c12afd9489154fb06b10f9e2c913)
@@ -177,4 +177,5 @@
 	
 	dgram.tos = IPC_GET_ARG1(*icall);
+	dgram.iplink = IPC_GET_ARG2(*icall);
 	
 	ipc_callid_t callid;
Index: uspace/lib/c/generic/iplink.c
===================================================================
--- uspace/lib/c/generic/iplink.c	(revision ab6326bcc8e9a50d34db6b657d59aaddc9baca6c)
+++ uspace/lib/c/generic/iplink.c	(revision 8d48c7e46fe4c12afd9489154fb06b10f9e2c913)
@@ -47,5 +47,5 @@
 static void iplink_cb_conn(ipc_callid_t iid, ipc_call_t *icall, void *arg);
 
-int iplink_open(async_sess_t *sess, iplink_ev_ops_t *ev_ops,
+int iplink_open(async_sess_t *sess, iplink_ev_ops_t *ev_ops, void *arg,
     iplink_t **riplink)
 {
@@ -56,4 +56,5 @@
 	iplink->sess = sess;
 	iplink->ev_ops = ev_ops;
+	iplink->arg = arg;
 	
 	async_exch_t *exch = async_exchange_begin(sess);
@@ -234,4 +235,9 @@
 	
 	return (int) retval;
+}
+
+void *iplink_get_userptr(iplink_t *iplink)
+{
+	return iplink->arg;
 }
 
Index: uspace/lib/c/include/inet/iplink.h
===================================================================
--- uspace/lib/c/include/inet/iplink.h	(revision ab6326bcc8e9a50d34db6b657d59aaddc9baca6c)
+++ uspace/lib/c/include/inet/iplink.h	(revision 8d48c7e46fe4c12afd9489154fb06b10f9e2c913)
@@ -44,4 +44,5 @@
 	async_sess_t *sess;
 	struct iplink_ev_ops *ev_ops;
+	void *arg;
 } iplink_t;
 
@@ -81,5 +82,5 @@
 } iplink_ev_ops_t;
 
-extern int iplink_open(async_sess_t *, iplink_ev_ops_t *, iplink_t **);
+extern int iplink_open(async_sess_t *, iplink_ev_ops_t *, void *, iplink_t **);
 extern void iplink_close(iplink_t *);
 extern int iplink_send(iplink_t *, iplink_sdu_t *);
@@ -90,4 +91,5 @@
 extern int iplink_get_mac48(iplink_t *, addr48_t *);
 extern int iplink_set_mac48(iplink_t *, addr48_t);
+extern void *iplink_get_userptr(iplink_t *);
 
 #endif
Index: uspace/lib/nettl/include/nettl/amap.h
===================================================================
--- uspace/lib/nettl/include/nettl/amap.h	(revision ab6326bcc8e9a50d34db6b657d59aaddc9baca6c)
+++ uspace/lib/nettl/include/nettl/amap.h	(revision 8d48c7e46fe4c12afd9489154fb06b10f9e2c913)
@@ -96,5 +96,5 @@
     inet_ep2_t *);
 extern void amap_remove(amap_t *, inet_ep2_t *);
-extern int amap_find(amap_t *, inet_ep2_t *, void **);
+extern int amap_find_match(amap_t *, inet_ep2_t *, void **);
 
 #endif
Index: uspace/lib/nettl/include/nettl/portrng.h
===================================================================
--- uspace/lib/nettl/include/nettl/portrng.h	(revision ab6326bcc8e9a50d34db6b657d59aaddc9baca6c)
+++ uspace/lib/nettl/include/nettl/portrng.h	(revision 8d48c7e46fe4c12afd9489154fb06b10f9e2c913)
@@ -46,4 +46,6 @@
 	/** Port number */
 	uint16_t pn;
+	/** User argument */
+	void *arg;
 } portrng_port_t;
 
@@ -60,4 +62,5 @@
 extern int portrng_alloc(portrng_t *, uint16_t, void *,
     portrng_flags_t, uint16_t *);
+extern int portrng_find_port(portrng_t *, uint16_t, void **);
 extern void portrng_free_port(portrng_t *, uint16_t);
 extern bool portrng_empty(portrng_t *);
Index: uspace/lib/nettl/src/amap.c
===================================================================
--- uspace/lib/nettl/src/amap.c	(revision ab6326bcc8e9a50d34db6b657d59aaddc9baca6c)
+++ uspace/lib/nettl/src/amap.c	(revision 8d48c7e46fe4c12afd9489154fb06b10f9e2c913)
@@ -510,8 +510,70 @@
 }
 
-int amap_find(amap_t *map, inet_ep2_t *epp, void **rarg)
-{
-	log_msg(LOG_DEFAULT, LVL_NOTE, "amap_find()");
-	return EOK;
+/** Find association matching an endpoint pair.
+ *
+ * Used to find which association to deliver a datagram to.
+ *
+ * @param map	Association map
+ * @param epp	Endpoint pair
+ * @param rarg	Place to store user argument for the matching association.
+ *
+ * @return	EOK on success, ENOENT if not found.
+ */
+int amap_find_match(amap_t *map, inet_ep2_t *epp, void **rarg)
+{
+	int rc;
+	amap_repla_t *repla;
+	amap_laddr_t *laddr;
+	amap_llink_t *llink;
+
+	log_msg(LOG_DEFAULT, LVL_NOTE, "amap_find_match(llink=%zu)",
+	    epp->local_link);
+
+	/* Remode endpoint, local address */
+	rc = amap_repla_find(map, &epp->remote, &epp->local.addr, &repla);
+	if (rc == EOK) {
+		rc = portrng_find_port(repla->portrng, epp->local.port,
+		    rarg);
+		if (rc == EOK) {
+			log_msg(LOG_DEFAULT, LVL_NOTE, "Matched repla / "
+			    "port %" PRIu16, epp->local.port);
+			return EOK;
+		}
+	}
+
+	/* Local address */
+	rc = amap_laddr_find(map, &epp->local.addr, &laddr);
+	if (rc == EOK) {
+		rc = portrng_find_port(laddr->portrng, epp->local.port,
+		    rarg);
+		if (rc == EOK) {
+			log_msg(LOG_DEFAULT, LVL_NOTE, "Matched laddr / "
+			    "port %" PRIu16, epp->local.port);
+			return EOK;
+		}
+	}
+
+	/* Local link */
+	rc = amap_llink_find(map, epp->local_link, &llink);
+	if (epp->local_link != 0 && rc == EOK) {
+		rc = portrng_find_port(llink->portrng, epp->local.port,
+		    rarg);
+		if (rc == EOK) {
+			log_msg(LOG_DEFAULT, LVL_NOTE, "Matched llink / "
+			    "port %" PRIu16, epp->local.port);
+			return EOK;
+		}
+	}
+
+	/* Unspecified */
+	rc = portrng_find_port(map->unspec, epp->local.port, rarg);
+	if (rc == EOK) {
+		log_msg(LOG_DEFAULT, LVL_NOTE, "Matched unspec / port %" PRIu16,
+		    epp->local.port);
+		return EOK;
+	}
+
+	log_msg(LOG_DEFAULT, LVL_NOTE, "No match.");
+	return ENOENT;
 }
 
Index: uspace/lib/nettl/src/portrng.c
===================================================================
--- uspace/lib/nettl/src/portrng.c	(revision ab6326bcc8e9a50d34db6b657d59aaddc9baca6c)
+++ uspace/lib/nettl/src/portrng.c	(revision 8d48c7e46fe4c12afd9489154fb06b10f9e2c913)
@@ -118,4 +118,5 @@
 
 	p->pn = pnum;
+	p->arg = arg;
 	list_append(&p->lprng, &pr->used);
 	*apnum = pnum;
@@ -123,4 +124,16 @@
 	    pnum);
 	return EOK;
+}
+
+int portrng_find_port(portrng_t *pr, uint16_t pnum, void **rarg)
+{
+	list_foreach(pr->used, lprng, portrng_port_t, port) {
+		if (port->pn == pnum) {
+			*rarg = port->arg;
+			return EOK;
+		}
+	}
+
+	return ENOENT;
 }
 
Index: uspace/srv/net/inetsrv/inet_link.c
===================================================================
--- uspace/srv/net/inetsrv/inet_link.c	(revision ab6326bcc8e9a50d34db6b657d59aaddc9baca6c)
+++ uspace/srv/net/inetsrv/inet_link.c	(revision 8d48c7e46fe4c12afd9489154fb06b10f9e2c913)
@@ -73,5 +73,5 @@
 {
 	memcpy(ip_addr, link_local_node_ip, 16);
-	
+
 	ip_addr[8] = mac_addr[0] ^ 0x02;
 	ip_addr[9] = mac_addr[1];
@@ -85,14 +85,19 @@
 {
 	log_msg(LOG_DEFAULT, LVL_DEBUG, "inet_iplink_recv()");
-	
+
 	int rc;
 	inet_packet_t packet;
-	
+	inet_link_t *ilink;
+
+	ilink = (inet_link_t *)iplink_get_userptr(iplink);
+
 	switch (ver) {
 	case ip_v4:
-		rc = inet_pdu_decode(sdu->data, sdu->size, &packet);
+		rc = inet_pdu_decode(sdu->data, sdu->size, ilink->svc_id,
+		    &packet);
 		break;
 	case ip_v6:
-		rc = inet_pdu_decode6(sdu->data, sdu->size, &packet);
+		rc = inet_pdu_decode6(sdu->data, sdu->size, ilink->svc_id,
+		    &packet);
 		break;
 	default:
@@ -100,15 +105,16 @@
 		return EINVAL;
 	}
-	
+
 	if (rc != EOK) {
 		log_msg(LOG_DEFAULT, LVL_DEBUG, "failed decoding PDU");
 		return rc;
 	}
-	
+
+	log_msg(LOG_DEFAULT, LVL_NOTE, "inet_iplink_recv: link_id=%zu", packet.link_id);
 	log_msg(LOG_DEFAULT, LVL_DEBUG, "call inet_recv_packet()");
 	rc = inet_recv_packet(&packet);
 	log_msg(LOG_DEFAULT, LVL_DEBUG, "call inet_recv_packet -> %d", rc);
 	free(packet.data);
-	
+
 	return rc;
 }
@@ -177,5 +183,5 @@
 	}
 
-	rc = iplink_open(ilink->sess, &inet_iplink_ev_ops, &ilink->iplink);
+	rc = iplink_open(ilink->sess, &inet_iplink_ev_ops, ilink, &ilink->iplink);
 	if (rc != EOK) {
 		log_msg(LOG_DEFAULT, LVL_ERROR, "Failed opening IP link '%s'",
Index: uspace/srv/net/inetsrv/inetsrv.c
===================================================================
--- uspace/srv/net/inetsrv/inetsrv.c	(revision ab6326bcc8e9a50d34db6b657d59aaddc9baca6c)
+++ uspace/srv/net/inetsrv/inetsrv.c	(revision 8d48c7e46fe4c12afd9489154fb06b10f9e2c913)
@@ -469,8 +469,13 @@
 {
 	async_exch_t *exch = async_exchange_begin(client->sess);
-	
+
 	ipc_call_t answer;
-	aid_t req = async_send_1(exch, INET_EV_RECV, dgram->tos, &answer);
-	
+
+	log_msg(LOG_DEFAULT, LVL_NOTE, "inet_ev_recv: iplink=%zu",
+	    dgram->iplink);
+
+	aid_t req = async_send_2(exch, INET_EV_RECV, dgram->tos,
+	    dgram->iplink, &answer);
+
 	int rc = async_data_write_start(exch, &dgram->src, sizeof(inet_addr_t));
 	if (rc != EOK) {
@@ -479,5 +484,5 @@
 		return rc;
 	}
-	
+
 	rc = async_data_write_start(exch, &dgram->dest, sizeof(inet_addr_t));
 	if (rc != EOK) {
@@ -486,17 +491,17 @@
 		return rc;
 	}
-	
+
 	rc = async_data_write_start(exch, dgram->data, dgram->size);
-	
+
 	async_exchange_end(exch);
-	
+
 	if (rc != EOK) {
 		async_forget(req);
 		return rc;
 	}
-	
+
 	sysarg_t retval;
 	async_wait_for(req, &retval);
-	
+
 	return (int) retval;
 }
@@ -511,5 +516,5 @@
 	if (proto == IP_PROTO_ICMP)
 		return icmp_recv(dgram);
-	
+
 	if (proto == IP_PROTO_ICMPV6)
 		return icmpv6_recv(dgram);
@@ -540,4 +545,5 @@
 		if (packet->offs == 0 && !packet->mf) {
 			/* It is complete deliver it immediately */
+			dgram.iplink = packet->link_id;
 			dgram.src = packet->src;
 			dgram.dest = packet->dest;
Index: uspace/srv/net/inetsrv/inetsrv.h
===================================================================
--- uspace/srv/net/inetsrv/inetsrv.h	(revision ab6326bcc8e9a50d34db6b657d59aaddc9baca6c)
+++ uspace/srv/net/inetsrv/inetsrv.h	(revision 8d48c7e46fe4c12afd9489154fb06b10f9e2c913)
@@ -75,4 +75,6 @@
 
 typedef struct {
+	/** Local link ID */
+	service_id_t link_id;
 	/** Source address */
 	inet_addr_t src;
Index: uspace/srv/net/inetsrv/pdu.c
===================================================================
--- uspace/srv/net/inetsrv/pdu.c	(revision ab6326bcc8e9a50d34db6b657d59aaddc9baca6c)
+++ uspace/srv/net/inetsrv/pdu.c	(revision 8d48c7e46fe4c12afd9489154fb06b10f9e2c913)
@@ -298,7 +298,8 @@
 /** Decode IPv4 datagram
  *
- * @param data   Serialized IPv4 datagram
- * @param size   Length of serialized IPv4 datagram
- * @param packet IP datagram structure to be filled
+ * @param data    Serialized IPv4 datagram
+ * @param size    Length of serialized IPv4 datagram
+ * @param link_id Link on which PDU was received
+ * @param packet  IP datagram structure to be filled
  *
  * @return EOK on success
@@ -307,5 +308,6 @@
  *
  */
-int inet_pdu_decode(void *data, size_t size, inet_packet_t *packet)
+int inet_pdu_decode(void *data, size_t size, service_id_t link_id,
+    inet_packet_t *packet)
 {
 	log_msg(LOG_DEFAULT, LVL_DEBUG, "inet_pdu_decode()");
@@ -366,4 +368,5 @@
 	
 	memcpy(packet->data, (uint8_t *) data + data_offs, packet->size);
+	packet->link_id = link_id;
 	
 	return EOK;
@@ -372,7 +375,8 @@
 /** Decode IPv6 datagram
  *
- * @param data   Serialized IPv6 datagram
- * @param size   Length of serialized IPv6 datagram
- * @param packet IP datagram structure to be filled
+ * @param data    Serialized IPv6 datagram
+ * @param size    Length of serialized IPv6 datagram
+ * @param link_id Link on which PDU was received
+ * @param packet  IP datagram structure to be filled
  *
  * @return EOK on success
@@ -381,5 +385,6 @@
  *
  */
-int inet_pdu_decode6(void *data, size_t size, inet_packet_t *packet)
+int inet_pdu_decode6(void *data, size_t size, service_id_t link_id,
+    inet_packet_t *packet)
 {
 	log_msg(LOG_DEFAULT, LVL_DEBUG, "inet_pdu_decode6()");
@@ -457,5 +462,5 @@
 	
 	memcpy(packet->data, (uint8_t *) data + data_offs, packet->size);
-	
+	packet->link_id = link_id;
 	return EOK;
 }
Index: uspace/srv/net/inetsrv/pdu.h
===================================================================
--- uspace/srv/net/inetsrv/pdu.h	(revision ab6326bcc8e9a50d34db6b657d59aaddc9baca6c)
+++ uspace/srv/net/inetsrv/pdu.h	(revision 8d48c7e46fe4c12afd9489154fb06b10f9e2c913)
@@ -38,4 +38,5 @@
 #define INET_PDU_H_
 
+#include <loc.h>
 #include <sys/types.h>
 #include "inetsrv.h"
@@ -50,6 +51,6 @@
 extern int inet_pdu_encode6(inet_packet_t *, addr128_t, addr128_t, size_t,
     size_t, void **, size_t *, size_t *);
-extern int inet_pdu_decode(void *, size_t, inet_packet_t *);
-extern int inet_pdu_decode6(void *, size_t, inet_packet_t *);
+extern int inet_pdu_decode(void *, size_t, service_id_t, inet_packet_t *);
+extern int inet_pdu_decode6(void *, size_t, service_id_t, inet_packet_t *);
 
 extern int ndp_pdu_decode(inet_dgram_t *, ndp_packet_t *);
Index: uspace/srv/net/inetsrv/reass.c
===================================================================
--- uspace/srv/net/inetsrv/reass.c	(revision ab6326bcc8e9a50d34db6b657d59aaddc9baca6c)
+++ uspace/srv/net/inetsrv/reass.c	(revision 8d48c7e46fe4c12afd9489154fb06b10f9e2c913)
@@ -325,4 +325,6 @@
 		return ENOMEM;
 
+	/* XXX What if different fragments came from different link? */
+	dgram.iplink = frag->packet.link_id;
 	dgram.size = dgram_size;
 	dgram.src = frag->packet.src;
Index: uspace/srv/net/tcp/conn.c
===================================================================
--- uspace/srv/net/tcp/conn.c	(revision ab6326bcc8e9a50d34db6b657d59aaddc9baca6c)
+++ uspace/srv/net/tcp/conn.c	(revision 8d48c7e46fe4c12afd9489154fb06b10f9e2c913)
@@ -199,5 +199,6 @@
 void tcp_conn_addref(tcp_conn_t *conn)
 {
-	log_msg(LOG_DEFAULT, LVL_DEBUG2, "%s: tcp_conn_addref(%p)", conn->name, conn);
+	log_msg(LOG_DEFAULT, LVL_DEBUG2, "%s: tcp_conn_addref(%p) before=%zu",
+	    conn->name, conn, atomic_get(&conn->refcnt));
 	atomic_inc(&conn->refcnt);
 }
@@ -211,5 +212,6 @@
 void tcp_conn_delref(tcp_conn_t *conn)
 {
-	log_msg(LOG_DEFAULT, LVL_DEBUG2, "%s: tcp_conn_delref(%p)", conn->name, conn);
+	log_msg(LOG_DEFAULT, LVL_DEBUG2, "%s: tcp_conn_delref(%p) before=%zu",
+	    conn->name, conn, atomic_get(&conn->refcnt));
 
 	if (atomic_predec(&conn->refcnt) == 0)
@@ -269,4 +271,6 @@
 	tcp_conn_addref(conn);
 	fibril_mutex_lock(&conn_list_lock);
+
+	log_msg(LOG_DEFAULT, LVL_NOTE, "tcp_conn_add: conn=%p", conn);
 
 	rc = amap_insert(amap, &conn->ident, conn, af_allow_system, &aepp);
@@ -365,37 +369,4 @@
 }
 
-/** Match endpoint with pattern. */
-static bool tcp_ep_match(inet_ep_t *ep, inet_ep_t *patt)
-{
-	log_msg(LOG_DEFAULT, LVL_DEBUG2,
-	    "tcp_ep_match(ep=(%u), pat=(%u))", ep->port, patt->port);
-
-	if ((!inet_addr_is_any(&patt->addr)) &&
-	    (!inet_addr_compare(&patt->addr, &ep->addr)))
-		return false;
-
-	if ((patt->port != inet_port_any) &&
-	    (patt->port != ep->port))
-		return false;
-
-	log_msg(LOG_DEFAULT, LVL_DEBUG2, " -> match");
-
-	return true;
-}
-
-/** Match endpoint pair with pattern. */
-static bool tcp_ep2_match(inet_ep2_t *epp, inet_ep2_t *pattern)
-{
-	log_msg(LOG_DEFAULT, LVL_DEBUG2, "tcp_ep2_match(%p, %p)", epp, pattern);
-
-	if (!tcp_ep_match(&epp->local, &pattern->local))
-		return false;
-
-	if (!tcp_ep_match(&epp->remote, &pattern->remote))
-		return false;
-
-	return true;
-}
-
 /** Find connection structure for specified endpoint pair.
  *
@@ -409,26 +380,26 @@
 tcp_conn_t *tcp_conn_find_ref(inet_ep2_t *epp)
 {
+	int rc;
+	void *arg;
+	tcp_conn_t *conn;
+
 	log_msg(LOG_DEFAULT, LVL_DEBUG, "tcp_conn_find_ref(%p)", epp);
 
-	log_msg(LOG_DEFAULT, LVL_DEBUG2, "compare conn (f:(%u), l:(%u))",
-	    epp->remote.port, epp->local.port);
-
 	fibril_mutex_lock(&conn_list_lock);
 
-	list_foreach(conn_list, link, tcp_conn_t, conn) {
-		inet_ep2_t *cepp = &conn->ident;
-
-		log_msg(LOG_DEFAULT, LVL_DEBUG2, " - with (f:(%u), l:(%u))",
-		    cepp->remote.port, cepp->local.port);
-
-		if (tcp_ep2_match(epp, cepp)) {
-			tcp_conn_addref(conn);
-			fibril_mutex_unlock(&conn_list_lock);
-			return conn;
-		}
-	}
+	rc = amap_find_match(amap, epp, &arg);
+	if (rc != EOK) {
+		assert(rc == ENOENT);
+		fibril_mutex_unlock(&conn_list_lock);
+		return NULL;
+	}
+
+	conn = (tcp_conn_t *)arg;
+	tcp_conn_addref(conn);
 
 	fibril_mutex_unlock(&conn_list_lock);
-	return NULL;
+	log_msg(LOG_DEFAULT, LVL_NOTE, "tcp_conn_find_ref: got conn=%p",
+	    conn);
+	return conn;
 }
 
@@ -1209,5 +1180,7 @@
 void tcp_conn_segment_arrived(tcp_conn_t *conn, tcp_segment_t *seg)
 {
-	log_msg(LOG_DEFAULT, LVL_DEBUG, "%s: tcp_conn_segment_arrived(%p)",
+	log_msg(LOG_DEFAULT, LVL_NOTE, "conn=%p", conn);
+	log_msg(LOG_DEFAULT, LVL_NOTE, "conn->name=%p", conn->name);
+	log_msg(LOG_DEFAULT, LVL_NOTE, "%s: tcp_conn_segment_arrived(%p)",
 	    conn->name, seg);
 
Index: uspace/srv/net/tcp/service.c
===================================================================
--- uspace/srv/net/tcp/service.c	(revision ab6326bcc8e9a50d34db6b657d59aaddc9baca6c)
+++ uspace/srv/net/tcp/service.c	(revision 8d48c7e46fe4c12afd9489154fb06b10f9e2c913)
@@ -156,4 +156,5 @@
 	}
 
+	conn->name = (char *) "s";
 	clst->conn = conn;
 
@@ -353,4 +354,6 @@
 		return EIO;
 
+	conn->name = (char *) "c";
+
 	rc = tcp_cconn_create(client, conn, &cconn);
 	if (rc != EOK) {
@@ -402,4 +405,6 @@
 	if (trc != TCP_EOK)
 		return EIO;
+
+	conn->name = (char *) "s";
 
 	rc = tcp_clistener_create(client, conn, &clst);
Index: uspace/srv/net/udp/assoc.c
===================================================================
--- uspace/srv/net/udp/assoc.c	(revision ab6326bcc8e9a50d34db6b657d59aaddc9baca6c)
+++ uspace/srv/net/udp/assoc.c	(revision 8d48c7e46fe4c12afd9489154fb06b10f9e2c913)
@@ -56,6 +56,4 @@
 static udp_assoc_t *udp_assoc_find_ref(inet_ep2_t *);
 static int udp_assoc_queue_msg(udp_assoc_t *, inet_ep2_t *, udp_msg_t *);
-static bool udp_ep_match(inet_ep_t *, inet_ep_t *);
-static bool udp_ep2_match(inet_ep2_t *, inet_ep2_t *);
 
 /** Initialize associations. */
@@ -393,48 +391,4 @@
 }
 
-/** Match endpoint with pattern. */
-static bool udp_ep_match(inet_ep_t *ep, inet_ep_t *patt)
-{
-	char *sa, *pa;
-
-	sa = pa = (char *)"?";
-	(void) inet_addr_format(&ep->addr, &sa);
-	(void) inet_addr_format(&patt->addr, &pa);
-
-	log_msg(LOG_DEFAULT, LVL_NOTE,
-	    "udp_ep_match(ep=(%s,%u), pat=(%s,%u))",
-	    sa, ep->port, pa, patt->port);
-
-	if ((!inet_addr_is_any(&patt->addr)) &&
-	    (!inet_addr_compare(&patt->addr, &ep->addr)))
-		return false;
-
-	log_msg(LOG_DEFAULT, LVL_NOTE, "addr OK");
-
-	if ((patt->port != inet_port_any) &&
-	    (patt->port != ep->port))
-		return false;
-
-	log_msg(LOG_DEFAULT, LVL_NOTE, " -> match");
-
-	return true;
-}
-
-/** Match endpoint pair with pattern. */
-static bool udp_ep2_match(inet_ep2_t *epp, inet_ep2_t *pattern)
-{
-	log_msg(LOG_DEFAULT, LVL_DEBUG, "udp_ep2_match(%p, %p)", epp, pattern);
-
-	if (!udp_ep_match(&epp->local, &pattern->local))
-		return false;
-
-	if (!udp_ep_match(&epp->remote, &pattern->remote))
-		return false;
-
-	log_msg(LOG_DEFAULT, LVL_DEBUG, "Endpoint pair matched.");
-	return true;
-}
-
-
 /** Find association structure for specified endpoint pair.
  *
@@ -448,40 +402,23 @@
 static udp_assoc_t *udp_assoc_find_ref(inet_ep2_t *epp)
 {
-	char *la, *ra;
+	int rc;
+	void *arg;
+	udp_assoc_t *assoc;
 
 	log_msg(LOG_DEFAULT, LVL_NOTE, "udp_assoc_find_ref(%p)", epp);
-
 	fibril_mutex_lock(&assoc_list_lock);
 
-	log_msg(LOG_DEFAULT, LVL_NOTE, "associations:");
-	list_foreach(assoc_list, link, udp_assoc_t, assoc) {
-		inet_ep2_t *aepp = &assoc->ident;
-
-		la = ra = NULL;
-
-		(void) inet_addr_format(&aepp->local.addr, &la);
-		(void) inet_addr_format(&aepp->remote.addr, &ra);
-
-		log_msg(LOG_DEFAULT, LVL_NOTE, "find_ref:aepp=%p la=%s ra=%s",
-		    aepp, la, ra);
-		/* Skip unbound associations */
-		if (aepp->local.port == inet_port_any) {
-			log_msg(LOG_DEFAULT, LVL_NOTE, "skip unbound");
-			continue;
-		}
-
-		if (udp_ep2_match(epp, aepp)) {
-			log_msg(LOG_DEFAULT, LVL_DEBUG, "Returning assoc %p", assoc);
-			udp_assoc_addref(assoc);
-			fibril_mutex_unlock(&assoc_list_lock);
-			return assoc;
-		} else {
-			log_msg(LOG_DEFAULT, LVL_NOTE, "not matched");
-		}
-	}
-
-	log_msg(LOG_DEFAULT, LVL_NOTE, "associations END");
+	rc = amap_find_match(amap, epp, &arg);
+	if (rc != EOK) {
+		assert(rc == ENOMEM);
+		fibril_mutex_unlock(&assoc_list_lock);
+		return NULL;
+	}
+
+	assoc = (udp_assoc_t *)arg;
+	udp_assoc_addref(assoc);
+
 	fibril_mutex_unlock(&assoc_list_lock);
-	return NULL;
+	return assoc;
 }
 
Index: uspace/srv/net/udp/pdu.c
===================================================================
--- uspace/srv/net/udp/pdu.c	(revision ab6326bcc8e9a50d34db6b657d59aaddc9baca6c)
+++ uspace/srv/net/udp/pdu.c	(revision 8d48c7e46fe4c12afd9489154fb06b10f9e2c913)
@@ -179,4 +179,5 @@
 	hdr = (udp_header_t *)pdu->data;
 
+	epp->local_link = pdu->iplink;
 	epp->remote.port = uint16_t_be2host(hdr->src_port);
 	epp->remote.addr = pdu->src;
Index: uspace/srv/net/udp/udp_inet.c
===================================================================
--- uspace/srv/net/udp/udp_inet.c	(revision ab6326bcc8e9a50d34db6b657d59aaddc9baca6c)
+++ uspace/srv/net/udp/udp_inet.c	(revision 8d48c7e46fe4c12afd9489154fb06b10f9e2c913)
@@ -63,5 +63,9 @@
 	log_msg(LOG_DEFAULT, LVL_DEBUG, "udp_inet_ev_recv()");
 
+	log_msg(LOG_DEFAULT, LVL_NOTE, "udp_inet_ev_recv: link=%zu",
+	    dgram->iplink);
+
 	pdu = udp_pdu_new();
+	pdu->iplink = dgram->iplink;
 	pdu->data = dgram->data;
 	pdu->data_size = dgram->size;
