Index: uspace/srv/ethip/Makefile
===================================================================
--- uspace/srv/ethip/Makefile	(revision e2e56e6766826103b6b7b98baac8f8d7ae826855)
+++ uspace/srv/ethip/Makefile	(revision 06295a99157877f0b3955dae49c83b8d24365018)
@@ -31,5 +31,6 @@
 
 SOURCES = \
-	ethip.c
+	ethip.c \
+	ethip_nic.c
 
 include $(USPACE_PREFIX)/Makefile.common
Index: uspace/srv/ethip/ethip.c
===================================================================
--- uspace/srv/ethip/ethip.c	(revision e2e56e6766826103b6b7b98baac8f8d7ae826855)
+++ uspace/srv/ethip/ethip.c	(revision 06295a99157877f0b3955dae49c83b8d24365018)
@@ -41,4 +41,8 @@
 #include <loc.h>
 #include <stdio.h>
+#include <stdlib.h>
+
+#include "ethip.h"
+#include "ethip_nic.h"
 
 #define NAME "eth"
@@ -58,14 +62,7 @@
 };
 
-static iplink_srv_t test_srv;
-
 static int ethip_init(void)
 {
 	int rc;
-	service_id_t sid;
-	category_id_t iplink_cat;
-
-	test_srv.ops = &ethip_iplink_ops;
-	test_srv.arg = NULL;
 
 	async_set_client_connection(ethip_client_conn);
@@ -77,8 +74,34 @@
 	}
 
-	rc = loc_service_register("net/eth0", &sid);
+	rc = ethip_nic_discovery_start();
+	if (rc != EOK)
+		return rc;
+
+	return EOK;
+}
+
+int ethip_iplink_init(ethip_nic_t *nic)
+{
+	int rc;
+	service_id_t sid;
+	category_id_t iplink_cat;
+	static unsigned link_num = 0;
+	char *svc_name = NULL;
+
+	log_msg(LVL_DEBUG, "ethip_iplink_init()");
+
+	nic->iplink.ops = &ethip_iplink_ops;
+	nic->iplink.arg = nic;
+
+	rc = asprintf(&svc_name, "net/eth%u", ++link_num);
+	if (rc < 0) {
+		log_msg(LVL_ERROR, "Out of memory.");
+		goto error;
+	}
+
+	rc = loc_service_register(svc_name, &sid);
 	if (rc != EOK) {
-		log_msg(LVL_ERROR, "Failed registering service net/eth0.");
-		return rc;
+		log_msg(LVL_ERROR, "Failed registering service %s.", svc_name);
+		goto error;
 	}
 
@@ -86,20 +109,25 @@
 	if (rc != EOK) {
 		log_msg(LVL_ERROR, "Failed resolving category 'iplink'.");
-		return rc;
+		goto error;
 	}
 
 	rc = loc_service_add_to_cat(sid, iplink_cat);
 	if (rc != EOK) {
-		log_msg(LVL_ERROR, "Failed adding net/eth0 to category.");
-		return rc;
+		log_msg(LVL_ERROR, "Failed adding %s to category.", svc_name);
+		goto error;
 	}
 
 	return EOK;
+
+error:
+	if (svc_name != NULL)
+		free(svc_name);
+	return rc;
 }
 
 static void ethip_client_conn(ipc_callid_t iid, ipc_call_t *icall, void *arg)
 {
-	log_msg(LVL_DEBUG, "ethip_client_conn()");
-	iplink_conn(iid, icall, &test_srv);
+	log_msg(LVL_DEBUG, "ethip_client_conn(%u)", (unsigned) IPC_GET_ARG1(*icall));
+	if (0) iplink_conn(iid, icall, NULL);
 }
 
Index: uspace/srv/ethip/ethip.h
===================================================================
--- uspace/srv/ethip/ethip.h	(revision 06295a99157877f0b3955dae49c83b8d24365018)
+++ uspace/srv/ethip/ethip.h	(revision 06295a99157877f0b3955dae49c83b8d24365018)
@@ -0,0 +1,54 @@
+/*
+ * Copyright (c) 2012 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 inet
+ * @{
+ */
+/**
+ * @file
+ * @brief
+ */
+
+#ifndef ETHIP_H_
+#define ETHIP_H_
+
+typedef struct ethip_nic {
+	link_t nic_list;
+	service_id_t svc_id;
+	char *svc_name;
+	async_sess_t *sess;
+
+	iplink_srv_t iplink;
+} ethip_nic_t;
+
+extern int ethip_iplink_init(ethip_nic_t *);
+
+#endif
+
+/** @}
+ */
Index: uspace/srv/ethip/ethip_nic.c
===================================================================
--- uspace/srv/ethip/ethip_nic.c	(revision 06295a99157877f0b3955dae49c83b8d24365018)
+++ uspace/srv/ethip/ethip_nic.c	(revision 06295a99157877f0b3955dae49c83b8d24365018)
@@ -0,0 +1,259 @@
+/*
+ * Copyright (c) 2012 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 inet
+ * @{
+ */
+/**
+ * @file
+ * @brief
+ */
+
+#include <async.h>
+#include <bool.h>
+#include <errno.h>
+#include <fibril_synch.h>
+#include <inet/iplink_srv.h>
+#include <io/log.h>
+#include <loc.h>
+#include <device/nic.h>
+#include <stdlib.h>
+
+#include "ethip.h"
+#include "ethip_nic.h"
+
+static int ethip_nic_open(service_id_t sid);
+static void ethip_nic_cb_conn(ipc_callid_t iid, ipc_call_t *icall, void *arg);
+
+static LIST_INITIALIZE(ethip_nic_list);
+static FIBRIL_MUTEX_INITIALIZE(ethip_discovery_lock);
+
+static int ethip_nic_check_new(void)
+{
+	bool already_known;
+	category_id_t iplink_cat;
+	service_id_t *svcs;
+	size_t count, i;
+	int rc;
+
+	fibril_mutex_lock(&ethip_discovery_lock);
+
+	rc = loc_category_get_id("nic", &iplink_cat, IPC_FLAG_BLOCKING);
+	if (rc != EOK) {
+		log_msg(LVL_ERROR, "Failed resolving category 'nic'.");
+		fibril_mutex_unlock(&ethip_discovery_lock);
+		return ENOENT;
+	}
+
+	rc = loc_category_get_svcs(iplink_cat, &svcs, &count);
+	if (rc != EOK) {
+		log_msg(LVL_ERROR, "Failed getting list of IP links.");
+		fibril_mutex_unlock(&ethip_discovery_lock);
+		return EIO;
+	}
+
+	for (i = 0; i < count; i++) {
+		already_known = false;
+
+		list_foreach(ethip_nic_list, nic_link) {
+			ethip_nic_t *nic = list_get_instance(nic_link,
+			    ethip_nic_t, nic_list);
+			if (nic->svc_id == svcs[i]) {
+				already_known = true;
+				break;
+			}
+		}
+
+		if (!already_known) {
+			log_msg(LVL_DEBUG, "Found NIC '%lu'",
+			    (unsigned long) svcs[i]);
+			rc = ethip_nic_open(svcs[i]);
+			if (rc != EOK)
+				log_msg(LVL_ERROR, "Could not open NIC.");
+		}
+	}
+
+	fibril_mutex_unlock(&ethip_discovery_lock);
+	return EOK;
+}
+
+static ethip_nic_t *ethip_nic_new(void)
+{
+	ethip_nic_t *nic = calloc(1, sizeof(ethip_nic_t));
+
+	if (nic == NULL) {
+		log_msg(LVL_ERROR, "Failed allocating NIC structure. "
+		    "Out of memory.");
+		return NULL;
+	}
+
+	link_initialize(&nic->nic_list);
+
+	return nic;
+}
+
+static void ethip_nic_delete(ethip_nic_t *nic)
+{
+	if (nic->svc_name != NULL)
+		free(nic->svc_name);
+	free(nic);
+}
+
+static int ethip_nic_open(service_id_t sid)
+{
+	ethip_nic_t *nic;
+	int rc;
+	bool in_list = false;
+
+	log_msg(LVL_DEBUG, "ethip_nic_open()");
+	nic = ethip_nic_new();
+	if (nic == NULL)
+		return ENOMEM;
+
+	rc = loc_service_get_name(sid, &nic->svc_name);
+	if (rc != EOK) {
+		log_msg(LVL_ERROR, "Failed getting service name.");
+		goto error;
+	}
+
+	nic->sess = loc_service_connect(EXCHANGE_SERIALIZE, sid, 0);
+	if (nic->sess == NULL) {
+		log_msg(LVL_ERROR, "Failed connecting '%s'", nic->svc_name);
+		goto error;
+	}
+
+	nic->svc_id = sid;
+
+	rc = nic_callback_create(nic->sess, ethip_nic_cb_conn, nic);
+	if (rc != EOK) {
+		log_msg(LVL_ERROR, "Failed creating callback connection "
+		    "from '%s'", nic->svc_name);
+		goto error;
+	}
+
+	rc = nic_set_state(nic->sess, NIC_STATE_ACTIVE);
+	if (rc != EOK) {
+		log_msg(LVL_ERROR, "Failed activating NIC '%s'.",
+		    nic->svc_name);
+		goto error;
+	}
+
+	log_msg(LVL_DEBUG, "Opened NIC '%s'", nic->svc_name);
+	list_append(&nic->nic_list, &ethip_nic_list);
+	in_list = true;
+
+	rc = ethip_iplink_init(nic);
+	if (rc != EOK)
+		goto error;
+
+	log_msg(LVL_DEBUG, "Initialized IP link service.");
+
+	return EOK;
+
+error:
+	if (in_list)
+		list_remove(&nic->nic_list);
+	if (nic->sess != NULL)
+		async_hangup(nic->sess);
+	ethip_nic_delete(nic);
+	return rc;
+}
+
+static void ethip_nic_cat_change_cb(void)
+{
+	(void) ethip_nic_check_new();
+}
+
+static void ethip_nic_addr_changed(ethip_nic_t *nic, ipc_callid_t callid,
+    ipc_call_t *call)
+{
+	log_msg(LVL_DEBUG, "ethip_nic_addr_changed()");
+	async_answer_0(callid, ENOTSUP);
+}
+
+static void ethip_nic_received(ethip_nic_t *nic, ipc_callid_t callid,
+    ipc_call_t *call)
+{
+	log_msg(LVL_DEBUG, "ethip_nic_received()");
+	async_answer_0(callid, ENOTSUP);
+}
+
+static void ethip_nic_device_state(ethip_nic_t *nic, ipc_callid_t callid,
+    ipc_call_t *call)
+{
+	log_msg(LVL_DEBUG, "ethip_nic_device_state()");
+	async_answer_0(callid, ENOTSUP);
+}
+
+static void ethip_nic_cb_conn(ipc_callid_t iid, ipc_call_t *icall, void *arg)
+{
+	ethip_nic_t *nic;
+
+	log_msg(LVL_DEBUG, "ethnip_nic_cb_conn()");
+
+	while (true) {
+		ipc_call_t call;
+		ipc_callid_t callid = async_get_call(&call);
+
+		if (!IPC_GET_IMETHOD(call)) {
+			/* TODO: Handle hangup */
+			return;
+		}
+
+		switch (IPC_GET_IMETHOD(call)) {
+		case NIC_EV_ADDR_CHANGED:
+			ethip_nic_addr_changed(nic, callid, &call);
+			break;
+		case NIC_EV_RECEIVED:
+			ethip_nic_received(nic, callid, &call);
+			break;
+		case NIC_EV_DEVICE_STATE:
+			ethip_nic_device_state(nic, callid, &call);
+			break;
+		default:
+			async_answer_0(callid, ENOTSUP);
+		}
+	}
+}
+
+int ethip_nic_discovery_start(void)
+{
+	int rc;
+
+	rc = loc_register_cat_change_cb(ethip_nic_cat_change_cb);
+	if (rc != EOK) {
+		log_msg(LVL_ERROR, "Failed registering callback for NIC "
+		    "discovery (%d).", rc);
+		return rc;
+	}
+
+	return ethip_nic_check_new();
+}
+
+/** @}
+ */
Index: uspace/srv/ethip/ethip_nic.h
===================================================================
--- uspace/srv/ethip/ethip_nic.h	(revision 06295a99157877f0b3955dae49c83b8d24365018)
+++ uspace/srv/ethip/ethip_nic.h	(revision 06295a99157877f0b3955dae49c83b8d24365018)
@@ -0,0 +1,45 @@
+/*
+ * Copyright (c) 2012 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 inet
+ * @{
+ */
+/**
+ * @file
+ * @brief
+ */
+
+#ifndef ETHIP_NIC_H_
+#define ETHIP_NIC_H_
+
+extern int ethip_nic_discovery_start(void);
+
+#endif
+
+/** @}
+ */
