Index: uspace/srv/net/nconfsrv/iplink.c
===================================================================
--- uspace/srv/net/nconfsrv/iplink.c	(revision 7af0cc5a018d0110bf2c694b3d163d45aa8b2725)
+++ uspace/srv/net/nconfsrv/iplink.c	(revision 7af0cc5a018d0110bf2c694b3d163d45aa8b2725)
@@ -0,0 +1,220 @@
+/*
+ * Copyright (c) 2013 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 nconfsrv
+ * @{
+ */
+/**
+ * @file
+ * @brief
+ */
+
+#include <stdbool.h>
+#include <errno.h>
+#include <fibril_synch.h>
+#include <inet/inetcfg.h>
+#include <io/log.h>
+#include <loc.h>
+#include <stdlib.h>
+
+#include "iplink.h"
+#include "nconfsrv.h"
+
+static int ncs_link_add(service_id_t);
+
+static LIST_INITIALIZE(ncs_links);
+static FIBRIL_MUTEX_INITIALIZE(ncs_links_lock);
+
+static int ncs_link_check_new(void)
+{
+	bool already_known;
+	category_id_t iplink_cat;
+	service_id_t *svcs;
+	size_t count, i;
+	int rc;
+
+	fibril_mutex_lock(&ncs_links_lock);
+
+	rc = loc_category_get_id("iplink", &iplink_cat, IPC_FLAG_BLOCKING);
+	if (rc != EOK) {
+		log_msg(LOG_DEFAULT, LVL_ERROR, "Failed resolving category 'iplink'.");
+		fibril_mutex_unlock(&ncs_links_lock);
+		return ENOENT;
+	}
+
+	rc = loc_category_get_svcs(iplink_cat, &svcs, &count);
+	if (rc != EOK) {
+		log_msg(LOG_DEFAULT, LVL_ERROR, "Failed getting list of IP links.");
+		fibril_mutex_unlock(&ncs_links_lock);
+		return EIO;
+	}
+
+	for (i = 0; i < count; i++) {
+		already_known = false;
+
+		list_foreach(ncs_links, link_list, ncs_link_t, ilink) {
+			if (ilink->svc_id == svcs[i]) {
+				already_known = true;
+				break;
+			}
+		}
+
+		if (!already_known) {
+			log_msg(LOG_DEFAULT, LVL_NOTE, "Found IP link '%lu'",
+			    (unsigned long) svcs[i]);
+			rc = ncs_link_add(svcs[i]);
+			if (rc != EOK)
+				log_msg(LOG_DEFAULT, LVL_ERROR, "Could not add IP link.");
+		}
+	}
+
+	fibril_mutex_unlock(&ncs_links_lock);
+	return EOK;
+}
+
+static ncs_link_t *ncs_link_new(void)
+{
+	ncs_link_t *nlink = calloc(1, sizeof(ncs_link_t));
+
+	if (nlink == NULL) {
+		log_msg(LOG_DEFAULT, LVL_ERROR, "Failed allocating link structure. "
+		    "Out of memory.");
+		return NULL;
+	}
+
+	link_initialize(&nlink->link_list);
+
+	return nlink;
+}
+
+static void ncs_link_delete(ncs_link_t *nlink)
+{
+	if (nlink->svc_name != NULL)
+		free(nlink->svc_name);
+
+	free(nlink);
+}
+
+static int ncs_link_add(service_id_t sid)
+{
+	ncs_link_t *nlink;
+	int rc;
+
+	log_msg(LOG_DEFAULT, LVL_DEBUG, "ncs_link_add()");
+	nlink = ncs_link_new();
+	if (nlink == NULL)
+		return ENOMEM;
+
+	nlink->svc_id = sid;
+
+	rc = loc_service_get_name(sid, &nlink->svc_name);
+	if (rc != EOK) {
+		log_msg(LOG_DEFAULT, LVL_ERROR, "Failed getting service name.");
+		goto error;
+	}
+
+	rc = inetcfg_link_add(sid);
+	if (rc != EOK) {
+		log_msg(LOG_DEFAULT, LVL_ERROR, "Failed configuring link "
+		    "'%s'.\n", nlink->svc_name);
+		goto error;
+	}
+
+	return EOK;
+
+error:
+	ncs_link_delete(nlink);
+	return rc;
+}
+
+static void ncs_link_cat_change_cb(void)
+{
+	(void) ncs_link_check_new();
+}
+
+int ncs_link_discovery_start(void)
+{
+	int rc;
+
+	rc = loc_register_cat_change_cb(ncs_link_cat_change_cb);
+	if (rc != EOK) {
+		log_msg(LOG_DEFAULT, LVL_ERROR, "Failed registering callback for IP link "
+		    "discovery (%d).", rc);
+		return rc;
+	}
+
+	return ncs_link_check_new();
+}
+
+ncs_link_t *ncs_link_get_by_id(sysarg_t link_id)
+{
+	fibril_mutex_lock(&ncs_links_lock);
+
+	list_foreach(ncs_links, link_list, ncs_link_t, nlink) {
+		if (nlink->svc_id == link_id) {
+			fibril_mutex_unlock(&ncs_links_lock);
+			return nlink;
+		}
+	}
+
+	fibril_mutex_unlock(&ncs_links_lock);
+	return NULL;
+}
+
+/** Get IDs of all links. */
+int ncs_link_get_id_list(sysarg_t **rid_list, size_t *rcount)
+{
+	sysarg_t *id_list;
+	size_t count, i;
+
+	fibril_mutex_lock(&ncs_links_lock);
+	count = list_count(&ncs_links);
+
+	id_list = calloc(count, sizeof(sysarg_t));
+	if (id_list == NULL) {
+		fibril_mutex_unlock(&ncs_links_lock);
+		return ENOMEM;
+	}
+
+	i = 0;
+	list_foreach(ncs_links, link_list, ncs_link_t, nlink) {
+		id_list[i++] = nlink->svc_id;
+		log_msg(LOG_DEFAULT, LVL_NOTE, "add link to list");
+	}
+
+	fibril_mutex_unlock(&ncs_links_lock);
+
+	log_msg(LOG_DEFAULT, LVL_NOTE, "return %zu links", count);
+	*rid_list = id_list;
+	*rcount = count;
+
+	return EOK;
+}
+
+/** @}
+ */
