Index: uspace/lib/nettl/include/nettl/amap.h
===================================================================
--- uspace/lib/nettl/include/nettl/amap.h	(revision 2989c7e1f80ad5fce60e2df203de878926d6c147)
+++ uspace/lib/nettl/include/nettl/amap.h	(revision ab6326bcc8e9a50d34db6b657d59aaddc9baca6c)
@@ -37,4 +37,5 @@
 #define LIBNETTL_AMAP_H_
 
+#include <adt/list.h>
 #include <inet/endpoint.h>
 #include <nettl/portrng.h>
@@ -43,4 +44,6 @@
 /** Port range for (remote endpoint, local address) */
 typedef struct {
+	/** Link to amap_t.repla */
+	link_t lamap;
 	/** Remote endpoint */
 	inet_ep_t rep;
@@ -53,4 +56,6 @@
 /** Port range for local address */
 typedef struct {
+	/** Link to amap_t.laddr */
+	link_t lamap;
 	/** Local address */
 	inet_addr_t laddr;
@@ -61,4 +66,6 @@
 /** Port range for local link */
 typedef struct {
+	/** Link to amap_t.llink */
+	link_t lamap;
 	/** Local link ID */
 	service_id_t llink;
@@ -75,4 +82,6 @@
 	/** Local links */
 	list_t llink; /* of amap_llink_t */
+	/** Nothing specified (listen on all local adresses) */
+	portrng_t *unspec;
 } amap_t;
 
Index: uspace/lib/nettl/include/nettl/portrng.h
===================================================================
--- uspace/lib/nettl/include/nettl/portrng.h	(revision 2989c7e1f80ad5fce60e2df203de878926d6c147)
+++ uspace/lib/nettl/include/nettl/portrng.h	(revision ab6326bcc8e9a50d34db6b657d59aaddc9baca6c)
@@ -37,7 +37,17 @@
 #define LIBNETTL_PORTRNG_H_
 
+#include <stdbool.h>
 #include <stdint.h>
 
+/** Allocated port */
 typedef struct {
+	/** Link to portrng_t.used */
+	link_t lprng;
+	/** Port number */
+	uint16_t pn;
+} portrng_port_t;
+
+typedef struct {
+	list_t used; /* of portrng_port_t */
 } portrng_t;
 
@@ -48,8 +58,8 @@
 extern int portrng_create(portrng_t **);
 extern void portrng_destroy(portrng_t *);
-extern int portrng_alloc_specific(portrng_t *, uint16_t, void *,
-    portrng_flags_t);
-extern int portrng_alloc_dynamic(portrng_t *, void *, uint16_t *);
+extern int portrng_alloc(portrng_t *, uint16_t, void *,
+    portrng_flags_t, uint16_t *);
 extern void portrng_free_port(portrng_t *, uint16_t);
+extern bool portrng_empty(portrng_t *);
 
 #endif
Index: uspace/lib/nettl/src/amap.c
===================================================================
--- uspace/lib/nettl/src/amap.c	(revision 2989c7e1f80ad5fce60e2df203de878926d6c147)
+++ uspace/lib/nettl/src/amap.c	(revision ab6326bcc8e9a50d34db6b657d59aaddc9baca6c)
@@ -38,5 +38,7 @@
  */
 
+#include <adt/list.h>
 #include <errno.h>
+#include <inet/addr.h>
 #include <inet/inet.h>
 #include <io/log.h>
@@ -48,5 +50,7 @@
 {
 	amap_t *map;
-	log_msg(LOG_DEFAULT, LVL_DEBUG, "amap_create()");
+	int rc;
+
+	log_msg(LOG_DEFAULT, LVL_NOTE, "amap_create()");
 
 	map = calloc(1, sizeof(amap_t));
@@ -54,4 +58,16 @@
 		return ENOMEM;
 
+	rc = portrng_create(&map->unspec);
+	if (rc != EOK) {
+		assert(rc == ENOMEM);
+		free(map);
+		return ENOMEM;
+	}
+
+	list_initialize(&map->repla);
+	list_initialize(&map->laddr);
+	list_initialize(&map->llink);
+
+	*rmap = map;
 	return EOK;
 }
@@ -59,6 +75,280 @@
 void amap_destroy(amap_t *map)
 {
-	log_msg(LOG_DEFAULT, LVL_DEBUG, "amap_destroy()");
+	log_msg(LOG_DEFAULT, LVL_NOTE, "amap_destroy()");
 	free(map);
+}
+
+/** Find exact repla */
+static int amap_repla_find(amap_t *map, inet_ep_t *rep, inet_addr_t *la,
+    amap_repla_t **rrepla)
+{
+	char *sraddr, *sladdr;
+
+	(void) inet_addr_format(&rep->addr, &sraddr);
+	(void) inet_addr_format(la, &sladdr);
+
+	log_msg(LOG_DEFAULT, LVL_NOTE, "amap_repla_find(): rep=(%s,%" PRIu16
+	    ") la=%s", sraddr, rep->port, sladdr);
+	free(sraddr);
+	free(sladdr);
+
+	list_foreach(map->repla, lamap, amap_repla_t, repla) {
+		(void) inet_addr_format(&repla->rep.addr, &sraddr);
+		(void) inet_addr_format(&repla->laddr, &sladdr);
+		log_msg(LOG_DEFAULT, LVL_NOTE, "amap_repla_find(): "
+		    "compare to rep=(%s, %" PRIu16 ") la=%s",
+		    sraddr, repla->rep.port, sladdr);
+		free(sraddr);
+		free(sladdr);
+		if (inet_addr_compare(&repla->rep.addr, &rep->addr) &&
+		    repla->rep.port == rep->port &&
+		    inet_addr_compare(&repla->laddr, la)) {
+			*rrepla = repla;
+			return EOK;
+		}
+	}
+
+	*rrepla = NULL;
+	return ENOENT;
+}
+
+static int amap_repla_insert(amap_t *map, inet_ep_t *rep, inet_addr_t *la,
+    amap_repla_t **rrepla)
+{
+	amap_repla_t *repla;
+	int rc;
+
+	repla = calloc(1, sizeof(amap_repla_t));
+	if (repla == NULL) {
+		*rrepla = NULL;
+		return ENOMEM;
+	}
+
+	rc = portrng_create(&repla->portrng);
+	if (rc != EOK) {
+		free(repla);
+		return ENOMEM;
+	}
+
+	repla->rep = *rep;
+	repla->laddr = *la;
+	list_append(&repla->lamap, &map->repla);
+
+	*rrepla = repla;
+	return EOK;
+}
+
+static void amap_repla_remove(amap_t *map, amap_repla_t *repla)
+{
+	list_remove(&repla->lamap);
+	portrng_destroy(repla->portrng);
+	free(repla);
+}
+
+/** Find exact laddr */
+static int amap_laddr_find(amap_t *map, inet_addr_t *addr,
+    amap_laddr_t **rladdr)
+{
+	list_foreach(map->laddr, lamap, amap_laddr_t, laddr) {
+		if (inet_addr_compare(&laddr->laddr, addr)) {
+			*rladdr = laddr;
+			return EOK;
+		}
+	}
+
+	*rladdr = NULL;
+	return ENOENT;
+}
+
+static int amap_laddr_insert(amap_t *map, inet_addr_t *addr,
+    amap_laddr_t **rladdr)
+{
+	amap_laddr_t *laddr;
+	int rc;
+
+	laddr = calloc(1, sizeof(amap_laddr_t));
+	if (laddr == NULL) {
+		*rladdr = NULL;
+		return ENOMEM;
+	}
+
+	rc = portrng_create(&laddr->portrng);
+	if (rc != EOK) {
+		free(laddr);
+		return ENOMEM;
+	}
+
+	laddr->laddr = *addr;
+	list_append(&laddr->lamap, &map->laddr);
+
+	*rladdr = laddr;
+	return EOK;
+}
+
+static void amap_laddr_remove(amap_t *map, amap_laddr_t *laddr)
+{
+	list_remove(&laddr->lamap);
+	portrng_destroy(laddr->portrng);
+	free(laddr);
+}
+
+/** Find exact llink */
+static int amap_llink_find(amap_t *map, sysarg_t link_id,
+    amap_llink_t **rllink)
+{
+	list_foreach(map->llink, lamap, amap_llink_t, llink) {
+		if (llink->llink == link_id) {
+			*rllink = llink;
+			return EOK;
+		}
+	}
+
+	*rllink = NULL;
+	return ENOENT;
+}
+
+static int amap_llink_insert(amap_t *map, sysarg_t link_id,
+    amap_llink_t **rllink)
+{
+	amap_llink_t *llink;
+	int rc;
+
+	llink = calloc(1, sizeof(amap_llink_t));
+	if (llink == NULL) {
+		*rllink = NULL;
+		return ENOMEM;
+	}
+
+	rc = portrng_create(&llink->portrng);
+	if (rc != EOK) {
+		free(llink);
+		return ENOMEM;
+	}
+
+	llink->llink = link_id;
+	list_append(&llink->lamap, &map->llink);
+
+	*rllink = llink;
+	return EOK;
+}
+
+static void amap_llink_remove(amap_t *map, amap_llink_t *llink)
+{
+	list_remove(&llink->lamap);
+	portrng_destroy(llink->portrng);
+	free(llink);
+}
+
+static int amap_insert_repla(amap_t *map, inet_ep2_t *epp, void *arg,
+    amap_flags_t flags, inet_ep2_t *aepp)
+{
+	amap_repla_t *repla;
+	inet_ep2_t mepp;
+	int rc;
+
+	log_msg(LOG_DEFAULT, LVL_NOTE, "amap_insert_repla()");
+
+	rc = amap_repla_find(map, &epp->remote, &epp->local.addr, &repla);
+	if (rc != EOK) {
+		/* New repla */
+		rc = amap_repla_insert(map, &epp->remote, &epp->local.addr,
+		    &repla);
+		if (rc != EOK) {
+			assert(rc == ENOMEM);
+			return rc;
+		}
+	}
+
+	mepp = *epp;
+
+	rc = portrng_alloc(repla->portrng, epp->local.port, arg, flags,
+	    &mepp.local.port);
+	if (rc != EOK) {
+		return rc;
+	}
+
+	*aepp = mepp;
+	return EOK;
+}
+
+static int amap_insert_laddr(amap_t *map, inet_ep2_t *epp, void *arg,
+    amap_flags_t flags, inet_ep2_t *aepp)
+{
+	amap_laddr_t *laddr;
+	inet_ep2_t mepp;
+	int rc;
+
+	log_msg(LOG_DEFAULT, LVL_NOTE, "amap_insert_laddr()");
+
+	rc = amap_laddr_find(map, &epp->local.addr, &laddr);
+	if (rc != EOK) {
+		/* New laddr */
+		rc = amap_laddr_insert(map, &epp->local.addr, &laddr);
+		if (rc != EOK) {
+			assert(rc == ENOMEM);
+			return rc;
+		}
+	}
+
+	mepp = *epp;
+
+	rc = portrng_alloc(laddr->portrng, epp->local.port, arg, flags,
+	    &mepp.local.port);
+	if (rc != EOK) {
+		return rc;
+	}
+
+	*aepp = mepp;
+	return EOK;
+}
+
+static int amap_insert_llink(amap_t *map, inet_ep2_t *epp, void *arg,
+    amap_flags_t flags, inet_ep2_t *aepp)
+{
+	amap_llink_t *llink;
+	inet_ep2_t mepp;
+	int rc;
+
+	log_msg(LOG_DEFAULT, LVL_NOTE, "amap_insert_llink()");
+
+	rc = amap_llink_find(map, epp->local_link, &llink);
+	if (rc != EOK) {
+		/* New llink */
+		rc = amap_llink_insert(map, epp->local_link, &llink);
+		if (rc != EOK) {
+			assert(rc == ENOMEM);
+			return rc;
+		}
+	}
+
+	mepp = *epp;
+
+	rc = portrng_alloc(llink->portrng, epp->local.port, arg, flags,
+	    &mepp.local.port);
+	if (rc != EOK) {
+		return rc;
+	}
+
+	*aepp = mepp;
+	return EOK;
+}
+
+static int amap_insert_unspec(amap_t *map, inet_ep2_t *epp, void *arg,
+    amap_flags_t flags, inet_ep2_t *aepp)
+{
+	inet_ep2_t mepp;
+	int rc;
+
+	log_msg(LOG_DEFAULT, LVL_NOTE, "amap_insert_unspec()");
+	mepp = *epp;
+
+	rc = portrng_alloc(map->unspec, epp->local.port, arg, flags,
+	    &mepp.local.port);
+	if (rc != EOK) {
+		return rc;
+	}
+
+	*aepp = mepp;
+	return EOK;
 }
 
@@ -80,47 +370,147 @@
     inet_ep2_t *aepp)
 {
-	int rc;
-
-	log_msg(LOG_DEFAULT, LVL_DEBUG, "amap_insert()");
-
-	*aepp = *epp;
+	bool raddr, rport, laddr, llink;
+	inet_ep2_t mepp;
+	int rc;
+
+	log_msg(LOG_DEFAULT, LVL_NOTE, "amap_insert()");
+
+	mepp = *epp;
 
 	/* Fill in local address? */
 	if (!inet_addr_is_any(&epp->remote.addr) &&
 	    inet_addr_is_any(&epp->local.addr)) {
-		log_msg(LOG_DEFAULT, LVL_DEBUG, "amap_insert: "
+		log_msg(LOG_DEFAULT, LVL_NOTE, "amap_insert: "
 		    "determine local address");
-		rc = inet_get_srcaddr(&epp->remote.addr, 0, &aepp->local.addr);
+		rc = inet_get_srcaddr(&epp->remote.addr, 0, &mepp.local.addr);
 		if (rc != EOK) {
-			log_msg(LOG_DEFAULT, LVL_DEBUG, "amap_insert: "
+			log_msg(LOG_DEFAULT, LVL_NOTE, "amap_insert: "
 			    "cannot determine local address");
 			return rc;
 		}
 	} else {
-		log_msg(LOG_DEFAULT, LVL_DEBUG, "amap_insert: "
+		log_msg(LOG_DEFAULT, LVL_NOTE, "amap_insert: "
 		    "local address specified or remote address not specified");
 	}
 
 	/** Allocate local port? */
-	if (aepp->local.port == inet_port_any) {
-		aepp->local.port = inet_port_dyn_lo; /* XXX */
-		log_msg(LOG_DEFAULT, LVL_DEBUG, "amap_insert: allocated local "
-		    "port %" PRIu16, aepp->local.port);
+	if (mepp.local.port == inet_port_any) {
+		mepp.local.port = inet_port_dyn_lo; /* XXX */
+		log_msg(LOG_DEFAULT, LVL_NOTE, "amap_insert: allocated local "
+		    "port %" PRIu16, mepp.local.port);
 	} else {
-		log_msg(LOG_DEFAULT, LVL_DEBUG, "amap_insert: local "
-		    "port %" PRIu16 " specified", aepp->local.port);
-	}
-
-	return EOK;
+		log_msg(LOG_DEFAULT, LVL_NOTE, "amap_insert: local "
+		    "port %" PRIu16 " specified", mepp.local.port);
+	}
+
+	raddr = !inet_addr_is_any(&mepp.remote.addr);
+	rport = mepp.remote.port != inet_port_any;
+	laddr = !inet_addr_is_any(&mepp.local.addr);
+	llink = mepp.local_link != 0;
+
+	if (raddr && rport && laddr && !llink) {
+		return amap_insert_repla(map, &mepp, arg, flags, aepp);
+	} else if (!raddr && !rport && laddr && !llink) {
+		return amap_insert_laddr(map, &mepp, arg, flags, aepp);
+	} else if (!raddr && !rport && !laddr && llink) {
+		return amap_insert_llink(map, &mepp, arg, flags, aepp);
+	} else if (!raddr && !rport && !laddr && !llink) {
+		return amap_insert_unspec(map, &mepp, arg, flags, aepp);
+	} else {
+		log_msg(LOG_DEFAULT, LVL_NOTE, "amap_insert: invalid "
+		    "combination of raddr=%d rport=%d laddr=%d llink=%d",
+		    raddr, rport, laddr, llink);
+		return EINVAL;
+	}
+
+	return EOK;
+}
+
+static void amap_remove_repla(amap_t *map, inet_ep2_t *epp)
+{
+	amap_repla_t *repla;
+	int rc;
+
+	rc = amap_repla_find(map, &epp->remote, &epp->local.addr, &repla);
+	if (rc != EOK) {
+		log_msg(LOG_DEFAULT, LVL_NOTE, "amap_remove_repla: not found");
+		return;
+	}
+
+	portrng_free_port(repla->portrng, epp->local.port);
+
+	if (portrng_empty(repla->portrng))
+		amap_repla_remove(map, repla);
+}
+
+static void amap_remove_laddr(amap_t *map, inet_ep2_t *epp)
+{
+	amap_laddr_t *laddr;
+	int rc;
+
+	rc = amap_laddr_find(map, &epp->local.addr, &laddr);
+	if (rc != EOK) {
+		log_msg(LOG_DEFAULT, LVL_NOTE, "amap_remove_laddr: not found");
+		return;
+	}
+
+	portrng_free_port(laddr->portrng, epp->local.port);
+
+	if (portrng_empty(laddr->portrng))
+		amap_laddr_remove(map, laddr);
+}
+
+static void amap_remove_llink(amap_t *map, inet_ep2_t *epp)
+{
+	amap_llink_t *llink;
+	int rc;
+
+	rc = amap_llink_find(map, epp->local_link, &llink);
+	if (rc != EOK) {
+		log_msg(LOG_DEFAULT, LVL_NOTE, "amap_remove_llink: not found");
+		return;
+	}
+
+	portrng_free_port(llink->portrng, epp->local.port);
+
+	if (portrng_empty(llink->portrng))
+		amap_llink_remove(map, llink);
+}
+
+static void amap_remove_unspec(amap_t *map, inet_ep2_t *epp)
+{
+	portrng_free_port(map->unspec, epp->local.port);
 }
 
 void amap_remove(amap_t *map, inet_ep2_t *epp)
 {
-	log_msg(LOG_DEFAULT, LVL_DEBUG, "amap_remove()");
+	bool raddr, rport, laddr, llink;
+
+	log_msg(LOG_DEFAULT, LVL_NOTE, "amap_remove()");
+
+	raddr = !inet_addr_is_any(&epp->remote.addr);
+	rport = epp->remote.port != inet_port_any;
+	laddr = !inet_addr_is_any(&epp->local.addr);
+	llink = epp->local_link != 0;
+
+	if (raddr && rport && laddr && !llink) {
+		amap_remove_repla(map, epp);
+	} else if (!raddr && !rport && laddr && !llink) {
+		amap_remove_laddr(map, epp);
+	} else if (!raddr && !rport && !laddr && llink) {
+		amap_remove_llink(map, epp);
+	} else if (!raddr && !rport && !laddr && !llink) {
+		amap_remove_unspec(map, epp);
+	} else {
+		log_msg(LOG_DEFAULT, LVL_NOTE, "amap_remove: invalid "
+		    "combination of raddr=%d rport=%d laddr=%d llink=%d",
+		    raddr, rport, laddr, llink);
+		return;
+	}
 }
 
 int amap_find(amap_t *map, inet_ep2_t *epp, void **rarg)
 {
-	log_msg(LOG_DEFAULT, LVL_DEBUG, "amap_find()");
+	log_msg(LOG_DEFAULT, LVL_NOTE, "amap_find()");
 	return EOK;
 }
Index: uspace/lib/nettl/src/portrng.c
===================================================================
--- uspace/lib/nettl/src/portrng.c	(revision 2989c7e1f80ad5fce60e2df203de878926d6c147)
+++ uspace/lib/nettl/src/portrng.c	(revision ab6326bcc8e9a50d34db6b657d59aaddc9baca6c)
@@ -37,10 +37,26 @@
  */
 
+#include <adt/list.h>
 #include <errno.h>
+#include <inet/endpoint.h>
 #include <nettl/portrng.h>
 #include <stdint.h>
+#include <stdlib.h>
+
+#include <io/log.h>
 
 int portrng_create(portrng_t **rpr)
 {
+	portrng_t *pr;
+
+	log_msg(LOG_DEFAULT, LVL_NOTE, "portrng_create() - begin");
+
+	pr = calloc(1, sizeof(portrng_t));
+	if (pr == NULL)
+		return ENOMEM;
+
+	list_initialize(&pr->used);
+	*rpr = pr;
+	log_msg(LOG_DEFAULT, LVL_NOTE, "portrng_create() - end");
 	return EOK;
 }
@@ -48,14 +64,62 @@
 void portrng_destroy(portrng_t *pr)
 {
+	log_msg(LOG_DEFAULT, LVL_NOTE, "portrng_destroy()");
+	assert(list_empty(&pr->used));
+	free(pr);
 }
 
-int portrng_alloc_specific(portrng_t *pr, uint16_t pnum, void *arg,
-    portrng_flags_t flags)
+int portrng_alloc(portrng_t *pr, uint16_t pnum, void *arg,
+    portrng_flags_t flags, uint16_t *apnum)
 {
-	return EOK;
-}
+	portrng_port_t *p;
+	uint32_t i;
+	bool found;
 
-int portrng_alloc_dynamic(portrng_t *pr, void *arg, uint16_t *rpnum)
-{
+	log_msg(LOG_DEFAULT, LVL_NOTE, "portrng_alloc() - begin");
+
+	if (pnum == inet_port_any) {
+
+		for (i = inet_port_dyn_lo; i <= inet_port_dyn_hi; i++) {
+			log_msg(LOG_DEFAULT, LVL_NOTE, "trying %" PRIu32, i);
+			found = false;
+			list_foreach(pr->used, lprng, portrng_port_t, port) {
+				if (port->pn == pnum) {
+					found = true;
+					break;
+				}
+			}
+
+			if (!found) {
+				pnum = i;
+				break;
+			}
+		}
+
+		if (pnum == inet_port_any) {
+			/* No free port found */
+			return ENOENT;
+		}
+		log_msg(LOG_DEFAULT, LVL_NOTE, "selected %" PRIu16, pnum);
+	} else {
+		if ((flags & pf_allow_system) == 0 &&
+		    pnum < inet_port_user_lo) {
+			return EINVAL;
+		}
+
+		list_foreach(pr->used, lprng, portrng_port_t, port) {
+			if (port->pn == pnum)
+				return EEXISTS;
+		}
+	}
+
+	p = calloc(1, sizeof(portrng_port_t));
+	if (p == NULL)
+		return ENOMEM;
+
+	p->pn = pnum;
+	list_append(&p->lprng, &pr->used);
+	*apnum = pnum;
+	log_msg(LOG_DEFAULT, LVL_NOTE, "portrng_alloc() - end OK pn=%" PRIu16,
+	    pnum);
 	return EOK;
 }
@@ -63,4 +127,21 @@
 void portrng_free_port(portrng_t *pr, uint16_t pnum)
 {
+	log_msg(LOG_DEFAULT, LVL_NOTE, "portrng_free_port() - begin");
+	list_foreach(pr->used, lprng, portrng_port_t, port) {
+		if (port->pn == pnum) {
+			list_remove(&port->lprng);
+			free(port);
+			return;
+		}
+	}
+
+	assert(false);
+	log_msg(LOG_DEFAULT, LVL_NOTE, "portrng_free_port() - end");
+}
+
+bool portrng_empty(portrng_t *pr)
+{
+	log_msg(LOG_DEFAULT, LVL_NOTE, "portrng_empty()");
+	return list_empty(&pr->used);
 }
 
