Index: boot/Makefile.common
===================================================================
--- boot/Makefile.common	(revision 6df220289417a3a5f0a319426564833d6f527370)
+++ boot/Makefile.common	(revision ce1836ed04784ea0bacc387ac80bfe852f36119b)
@@ -72,5 +72,5 @@
 	$(USPACE_PATH)/srv/loader/loader \
 	$(USPACE_PATH)/app/init/init \
-	$(USPACE_PATH)/srv/loc/loc \
+	$(USPACE_PATH)/srv/locsrv/locsrv \
 	$(USPACE_PATH)/srv/bd/rd/rd \
 	$(USPACE_PATH)/srv/vfs/vfs
@@ -107,10 +107,11 @@
 	$(USPACE_PATH)/srv/hid/remcons/remcons \
 	$(USPACE_PATH)/srv/net/ethip/ethip \
-	$(USPACE_PATH)/srv/net/inet/inet \
+	$(USPACE_PATH)/srv/net/inetsrv/inetsrv \
 	$(USPACE_PATH)/srv/net/loopip/loopip \
 	$(USPACE_PATH)/srv/net/tcp/tcp \
 	$(USPACE_PATH)/srv/net/udp/udp \
 	$(USPACE_PATH)/srv/taskmon/taskmon \
-	$(USPACE_PATH)/srv/devman/devman
+	$(USPACE_PATH)/srv/devman/devman \
+	$(USPACE_PATH)/srv/websrv/websrv
 
 RD_DRVS = \
@@ -157,8 +158,8 @@
 	$(USPACE_PATH)/app/edit/edit \
 	$(USPACE_PATH)/app/ext2info/ext2info \
-	$(USPACE_PATH)/app/inetcfg/inetcfg \
+	$(USPACE_PATH)/app/inet/inet \
 	$(USPACE_PATH)/app/kill/kill \
 	$(USPACE_PATH)/app/killall/killall \
-	$(USPACE_PATH)/app/locinfo/locinfo \
+	$(USPACE_PATH)/app/loc/loc \
 	$(USPACE_PATH)/app/mkfat/mkfat \
 	$(USPACE_PATH)/app/mkexfat/mkexfat \
@@ -184,6 +185,5 @@
 	$(USPACE_PATH)/app/usbinfo/usbinfo \
 	$(USPACE_PATH)/app/vuhid/vuh \
-	$(USPACE_PATH)/app/mkbd/mkbd \
-	$(USPACE_PATH)/app/websrv/websrv
+	$(USPACE_PATH)/app/mkbd/mkbd
 
 ifeq ($(CONFIG_PCC),y)
Index: uspace/Makefile
===================================================================
--- uspace/Makefile	(revision 6df220289417a3a5f0a319426564833d6f527370)
+++ uspace/Makefile	(revision ce1836ed04784ea0bacc387ac80bfe852f36119b)
@@ -42,9 +42,9 @@
 	app/getterm \
 	app/init \
-	app/inetcfg \
+	app/inet \
 	app/kill \
 	app/killall \
 	app/klog \
-	app/locinfo \
+	app/loc \
 	app/lsusb \
 	app/mkfat \
@@ -69,13 +69,12 @@
 	app/nettest3 \
 	app/ping \
-	app/websrv \
 	app/sysinfo \
 	app/mkbd \
 	srv/clipboard \
-	srv/loc \
+	srv/locsrv \
 	srv/devman \
 	srv/loader \
 	srv/net/ethip \
-	srv/net/inet \
+	srv/net/inetsrv \
 	srv/net/loopip \
 	srv/net/tcp \
@@ -103,4 +102,5 @@
 	srv/hid/remcons \
 	srv/hw/char/s3c24xx_uart \
+	srv/websrv \
 	drv/infrastructure/root \
 	drv/infrastructure/rootvirt \
Index: uspace/app/inet/Makefile
===================================================================
--- uspace/app/inet/Makefile	(revision ce1836ed04784ea0bacc387ac80bfe852f36119b)
+++ uspace/app/inet/Makefile	(revision ce1836ed04784ea0bacc387ac80bfe852f36119b)
@@ -0,0 +1,35 @@
+#
+# 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.
+#
+
+USPACE_PREFIX = ../..
+BINARY = inet
+
+SOURCES = \
+	inet.c
+
+include $(USPACE_PREFIX)/Makefile.common
Index: uspace/app/inet/inet.c
===================================================================
--- uspace/app/inet/inet.c	(revision ce1836ed04784ea0bacc387ac80bfe852f36119b)
+++ uspace/app/inet/inet.c	(revision ce1836ed04784ea0bacc387ac80bfe852f36119b)
@@ -0,0 +1,517 @@
+/*
+ * 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 Internet configuration utility.
+ *
+ * Controls the internet service (@c inet).
+ */
+
+#include <errno.h>
+#include <inet/inetcfg.h>
+#include <loc.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <str_error.h>
+#include <sys/types.h>
+
+#define NAME "inet"
+
+static void print_syntax(void)
+{
+	printf("syntax:\n");
+	printf("\t" NAME " create <addr>/<width> <link-name> <addr-name>\n");
+	printf("\t" NAME " delete <link-name> <addr-name>\n");
+	printf("\t" NAME " add-sr <dest-addr>/<width> <router-addr> <route-name>\n");
+	printf("\t" NAME " del-sr <route-name>\n");
+}
+
+static int naddr_parse(const char *text, inet_naddr_t *naddr)
+{
+	unsigned long a[4], bits;
+	char *cp = (char *)text;
+	int i;
+
+	for (i = 0; i < 3; i++) {
+		a[i] = strtoul(cp, &cp, 10);
+		if (*cp != '.')
+			return EINVAL;
+		++cp;
+	}
+
+	a[3] = strtoul(cp, &cp, 10);
+	if (*cp != '/')
+		return EINVAL;
+	++cp;
+
+	bits = strtoul(cp, &cp, 10);
+	if (*cp != '\0')
+		return EINVAL;
+
+	naddr->ipv4 = 0;
+	for (i = 0; i < 4; i++) {
+		if (a[i] > 255)
+			return EINVAL;
+		naddr->ipv4 = (naddr->ipv4 << 8) | a[i];
+	}
+
+	if (bits > 31)
+		return EINVAL;
+
+	naddr->bits = bits;
+	return EOK;
+}
+
+static int addr_parse(const char *text, inet_addr_t *addr)
+{
+	unsigned long a[4];
+	char *cp = (char *)text;
+	int i;
+
+	for (i = 0; i < 3; i++) {
+		a[i] = strtoul(cp, &cp, 10);
+		if (*cp != '.')
+			return EINVAL;
+		++cp;
+	}
+
+	a[3] = strtoul(cp, &cp, 10);
+	if (*cp != '\0')
+		return EINVAL;
+
+	addr->ipv4 = 0;
+	for (i = 0; i < 4; i++) {
+		if (a[i] > 255)
+			return EINVAL;
+		addr->ipv4 = (addr->ipv4 << 8) | a[i];
+	}
+
+	return EOK;
+}
+
+static int naddr_format(inet_naddr_t *naddr, char **bufp)
+{
+	int rc;
+
+	rc = asprintf(bufp, "%d.%d.%d.%d/%d", naddr->ipv4 >> 24,
+	    (naddr->ipv4 >> 16) & 0xff, (naddr->ipv4 >> 8) & 0xff,
+	    naddr->ipv4 & 0xff, naddr->bits);
+
+	if (rc < 0)
+		return ENOMEM;
+
+	return EOK;
+}
+
+static int addr_format(inet_addr_t *addr, char **bufp)
+{
+	int rc;
+
+	rc = asprintf(bufp, "%d.%d.%d.%d", addr->ipv4 >> 24,
+	    (addr->ipv4 >> 16) & 0xff, (addr->ipv4 >> 8) & 0xff,
+	    addr->ipv4 & 0xff);
+
+	if (rc < 0)
+		return ENOMEM;
+
+	return EOK;
+}
+
+static int addr_create_static(int argc, char *argv[])
+{
+	char *aobj_name;
+	char *addr_spec;
+	char *link_name;
+
+	inet_naddr_t naddr;
+	sysarg_t link_id;
+	sysarg_t addr_id;
+	int rc;
+
+	if (argc < 3) {
+		printf(NAME ": Missing arguments.\n");
+		print_syntax();
+		return EINVAL;
+	}
+
+	if (argc > 3) {
+		printf(NAME ": Too many arguments.\n");
+		print_syntax();
+		return EINVAL;
+	}
+
+	addr_spec = argv[0];
+	link_name = argv[1];
+	aobj_name = argv[2];
+
+	rc = loc_service_get_id(link_name, &link_id, 0);
+	if (rc != EOK) {
+		printf(NAME ": Service '%s' not found (%d).\n", link_name, rc);
+		return ENOENT;
+	}
+
+	rc = naddr_parse(addr_spec, &naddr);
+	if (rc != EOK) {
+		printf(NAME ": Invalid network address format '%s'.\n",
+		    addr_spec);
+		return EINVAL;
+	}
+
+	rc = inetcfg_addr_create_static(aobj_name, &naddr, link_id, &addr_id);
+	if (rc != EOK) {
+		printf(NAME ": Failed creating static address '%s' (%s)\n",
+		    aobj_name, str_error(rc));
+		return EIO;
+	}
+
+	return EOK;
+}
+
+static int addr_delete(int argc, char *argv[])
+{
+	char *aobj_name;
+	char *link_name;
+	sysarg_t link_id;
+	sysarg_t addr_id;
+	int rc;
+
+	if (argc < 2) {
+		printf(NAME ": Missing arguments.\n");
+		print_syntax();
+		return EINVAL;
+	}
+
+	if (argc > 2) {
+		printf(NAME ": Too many arguments.\n");
+		print_syntax();
+		return EINVAL;
+	}
+
+	link_name = argv[0];
+	aobj_name = argv[1];
+
+	rc = loc_service_get_id(link_name, &link_id, 0);
+	if (rc != EOK) {
+		printf(NAME ": Service '%s' not found (%d).\n", link_name, rc);
+		return ENOENT;
+	}
+
+	rc = inetcfg_addr_get_id(aobj_name, link_id, &addr_id);
+	if (rc != EOK) {
+		printf(NAME ": Address '%s' not found (%d).\n", aobj_name, rc);
+		return ENOENT;
+	}
+
+	rc = inetcfg_addr_delete(addr_id);
+	if (rc != EOK) {
+		printf(NAME ": Failed deleting address '%s' (%d)\n", aobj_name,
+		    rc);
+		return EIO;
+	}
+
+	return EOK;
+}
+
+static int sroute_create(int argc, char *argv[])
+{
+	char *dest_str;
+	char *router_str;
+	char *route_name;
+
+	inet_naddr_t dest;
+	inet_addr_t router;
+	sysarg_t sroute_id;
+	int rc;
+
+	if (argc < 3) {
+		printf(NAME ": Missing arguments.\n");
+		print_syntax();
+		return EINVAL;
+	}
+
+	if (argc > 3) {
+		printf(NAME ": Too many arguments.\n");
+		print_syntax();
+		return EINVAL;
+	}
+
+	dest_str   = argv[0];
+	router_str = argv[1];
+	route_name = argv[2];
+
+	rc = naddr_parse(dest_str, &dest);
+	if (rc != EOK) {
+		printf(NAME ": Invalid network address format '%s'.\n",
+		    dest_str);
+		return EINVAL;
+	}
+
+	rc = addr_parse(router_str, &router);
+	if (rc != EOK) {
+		printf(NAME ": Invalid address format '%s'.\n", router_str);
+		return EINVAL;
+	}
+
+	rc = inetcfg_sroute_create(route_name, &dest, &router, &sroute_id);
+	if (rc != EOK) {
+		printf(NAME ": Failed creating static route '%s' (%d)\n",
+		    route_name, rc);
+		return EIO;
+	}
+
+	return EOK;
+}
+
+static int sroute_delete(int argc, char *argv[])
+{
+	char *route_name;
+	sysarg_t sroute_id;
+	int rc;
+
+	if (argc < 1) {
+		printf(NAME ": Missing arguments.\n");
+		print_syntax();
+		return EINVAL;
+	}
+
+	if (argc > 1) {
+		printf(NAME ": Too many arguments.\n");
+		print_syntax();
+		return EINVAL;
+	}
+
+	route_name = argv[0];
+
+	rc = inetcfg_sroute_get_id(route_name, &sroute_id);
+	if (rc != EOK) {
+		printf(NAME ": Static route '%s' not found (%d).\n",
+		    route_name, rc);
+		return ENOENT;
+	}
+
+	rc = inetcfg_sroute_delete(sroute_id);
+	if (rc != EOK) {
+		printf(NAME ": Failed deleting static route '%s' (%d)\n",
+		    route_name, rc);
+		return EIO;
+	}
+
+	return EOK;
+}
+
+static int addr_list(void)
+{
+	sysarg_t *addr_list;
+	inet_addr_info_t ainfo;
+	inet_link_info_t linfo;
+
+	size_t count;
+	size_t i;
+	int rc;
+	char *astr;
+
+	rc = inetcfg_get_addr_list(&addr_list, &count);
+	if (rc != EOK) {
+		printf(NAME ": Failed getting address list.\n");
+		return rc;
+	}
+
+	printf("Configured addresses:\n");
+	if (count > 0)
+		printf("    [Addr/Width] [Link-Name] [Addr-Name] [Def-MTU]\n");
+	ainfo.name = linfo.name = astr = NULL;
+
+	for (i = 0; i < count; i++) {
+		rc = inetcfg_addr_get(addr_list[i], &ainfo);
+		if (rc != EOK) {
+			printf("Failed getting properties of address %zu.\n",
+			    (size_t)addr_list[i]);
+			ainfo.name = NULL;
+			continue;
+		}
+
+		rc = inetcfg_link_get(ainfo.ilink, &linfo);
+		if (rc != EOK) {
+			printf("Failed getting properties of link %zu.\n",
+			    (size_t)ainfo.ilink);
+			linfo.name = NULL;
+			continue;
+		}
+
+		rc = naddr_format(&ainfo.naddr, &astr);
+		if (rc != EOK) {
+			printf("Memory allocation failed.\n");
+			astr = NULL;
+			goto out;
+		}
+
+		printf("    %s %s %s %zu\n", astr, linfo.name,
+		    ainfo.name, linfo.def_mtu);
+
+		free(ainfo.name);
+		free(linfo.name);
+		free(astr);
+
+		ainfo.name = linfo.name = astr = NULL;
+	}
+
+	if (count == 0)
+		printf("    None\n");
+out:
+	if (ainfo.name != NULL)
+		free(ainfo.name);
+	if (linfo.name != NULL)
+		free(linfo.name);
+	if (astr != NULL)
+		free(astr);
+
+	free(addr_list);
+
+	return EOK;
+}
+
+static int sroute_list(void)
+{
+	sysarg_t *sroute_list;
+	inet_sroute_info_t srinfo;
+
+	size_t count;
+	size_t i;
+	int rc;
+	char *dest_str;
+	char *router_str;
+
+	rc = inetcfg_get_sroute_list(&sroute_list, &count);
+	if (rc != EOK) {
+		printf(NAME ": Failed getting address list.\n");
+		return rc;
+	}
+
+	printf("Static routes:\n");
+	if (count > 0)
+		printf("    [Dest/Width] [Router-Addr] [Route-Name]\n");
+
+	srinfo.name = dest_str = router_str = NULL;
+
+	for (i = 0; i < count; i++) {
+		rc = inetcfg_sroute_get(sroute_list[i], &srinfo);
+		if (rc != EOK) {
+			printf("Failed getting properties of static route %zu.\n",
+			    (size_t)sroute_list[i]);
+			srinfo.name = NULL;
+			continue;
+		}
+
+		rc = naddr_format(&srinfo.dest, &dest_str);
+		if (rc != EOK) {
+			printf("Memory allocation failed.\n");
+			dest_str = NULL;
+			goto out;
+		}
+
+		rc = addr_format(&srinfo.router, &router_str);
+		if (rc != EOK) {
+			printf("Memory allocation failed.\n");
+			router_str = NULL;
+			goto out;
+		}
+
+		printf("    %s %s %s\n", dest_str, router_str, srinfo.name);
+
+		free(srinfo.name);
+		free(dest_str);
+		free(router_str);
+
+		router_str = srinfo.name = dest_str = NULL;
+	}
+
+	if (count == 0)
+		printf("    None\n");
+out:
+	if (srinfo.name != NULL)
+		free(srinfo.name);
+	if (dest_str != NULL)
+		free(dest_str);
+	if (router_str != NULL)
+		free(router_str);
+
+	free(sroute_list);
+
+	return EOK;
+}
+
+int main(int argc, char *argv[])
+{
+	int rc;
+
+	rc = inetcfg_init();
+	if (rc != EOK) {
+		printf(NAME ": Failed connecting to internet service (%d).\n",
+		    rc);
+		return 1;
+	}
+
+	if (argc < 2) {
+		rc = addr_list();
+		if (rc != EOK)
+			return 1;
+		rc = sroute_list();
+		if (rc != EOK)
+			return 1;
+		return 0;
+	}
+
+	if (str_cmp(argv[1], "create") == 0) {
+		rc = addr_create_static(argc - 2, argv + 2);
+		if (rc != EOK)
+			return 1;
+	} else if (str_cmp(argv[1], "delete") == 0) {
+		rc = addr_delete(argc - 2, argv + 2);
+		if (rc != EOK)
+			return 1;
+	} else if (str_cmp(argv[1], "add-sr") == 0) {
+		rc = sroute_create(argc - 2, argv + 2);
+		if (rc != EOK)
+			return 1;
+	} else if (str_cmp(argv[1], "del-sr") == 0) {
+		rc = sroute_delete(argc - 2, argv + 2);
+		if (rc != EOK)
+			return 1;
+	} else {
+		printf(NAME ": Unknown command '%s'.\n", argv[1]);
+		print_syntax();
+		return 1;
+	}
+
+	return 0;
+}
+
+/** @}
+ */
Index: pace/app/inetcfg/Makefile
===================================================================
--- uspace/app/inetcfg/Makefile	(revision 6df220289417a3a5f0a319426564833d6f527370)
+++ 	(revision )
@@ -1,35 +1,0 @@
-#
-# 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.
-#
-
-USPACE_PREFIX = ../..
-BINARY = inetcfg
-
-SOURCES = \
-	inetcfg.c
-
-include $(USPACE_PREFIX)/Makefile.common
Index: pace/app/inetcfg/inetcfg.c
===================================================================
--- uspace/app/inetcfg/inetcfg.c	(revision 6df220289417a3a5f0a319426564833d6f527370)
+++ 	(revision )
@@ -1,514 +1,0 @@
-/*
- * 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 inetcfg
- * @{
- */
-/** @file Internet configuration utility.
- *
- * Controls the internet service (@c inet).
- */
-
-#include <errno.h>
-#include <inet/inetcfg.h>
-#include <loc.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <str_error.h>
-#include <sys/types.h>
-
-#define NAME "inetcfg"
-
-static void print_syntax(void)
-{
-	printf("syntax:\n");
-	printf("\t" NAME " create <addr>/<width> <link-name> <addr-name>\n");
-	printf("\t" NAME " delete <link-name> <addr-name>\n");
-	printf("\t" NAME " add-sr <dest-addr>/<width> <router-addr> <route-name>\n");
-	printf("\t" NAME " del-sr <route-name>\n");
-}
-
-static int naddr_parse(const char *text, inet_naddr_t *naddr)
-{
-	unsigned long a[4], bits;
-	char *cp = (char *)text;
-	int i;
-
-	for (i = 0; i < 3; i++) {
-		a[i] = strtoul(cp, &cp, 10);
-		if (*cp != '.')
-			return EINVAL;
-		++cp;
-	}
-
-	a[3] = strtoul(cp, &cp, 10);
-	if (*cp != '/')
-		return EINVAL;
-	++cp;
-
-	bits = strtoul(cp, &cp, 10);
-	if (*cp != '\0')
-		return EINVAL;
-
-	naddr->ipv4 = 0;
-	for (i = 0; i < 4; i++) {
-		if (a[i] > 255)
-			return EINVAL;
-		naddr->ipv4 = (naddr->ipv4 << 8) | a[i];
-	}
-
-	if (bits > 31)
-		return EINVAL;
-
-	naddr->bits = bits;
-	return EOK;
-}
-
-static int addr_parse(const char *text, inet_addr_t *addr)
-{
-	unsigned long a[4];
-	char *cp = (char *)text;
-	int i;
-
-	for (i = 0; i < 3; i++) {
-		a[i] = strtoul(cp, &cp, 10);
-		if (*cp != '.')
-			return EINVAL;
-		++cp;
-	}
-
-	a[3] = strtoul(cp, &cp, 10);
-	if (*cp != '\0')
-		return EINVAL;
-
-	addr->ipv4 = 0;
-	for (i = 0; i < 4; i++) {
-		if (a[i] > 255)
-			return EINVAL;
-		addr->ipv4 = (addr->ipv4 << 8) | a[i];
-	}
-
-	return EOK;
-}
-
-static int naddr_format(inet_naddr_t *naddr, char **bufp)
-{
-	int rc;
-
-	rc = asprintf(bufp, "%d.%d.%d.%d/%d", naddr->ipv4 >> 24,
-	    (naddr->ipv4 >> 16) & 0xff, (naddr->ipv4 >> 8) & 0xff,
-	    naddr->ipv4 & 0xff, naddr->bits);
-
-	if (rc < 0)
-		return ENOMEM;
-
-	return EOK;
-}
-
-static int addr_format(inet_addr_t *addr, char **bufp)
-{
-	int rc;
-
-	rc = asprintf(bufp, "%d.%d.%d.%d", addr->ipv4 >> 24,
-	    (addr->ipv4 >> 16) & 0xff, (addr->ipv4 >> 8) & 0xff,
-	    addr->ipv4 & 0xff);
-
-	if (rc < 0)
-		return ENOMEM;
-
-	return EOK;
-}
-
-static int addr_create_static(int argc, char *argv[])
-{
-	char *aobj_name;
-	char *addr_spec;
-	char *link_name;
-
-	inet_naddr_t naddr;
-	sysarg_t link_id;
-	sysarg_t addr_id;
-	int rc;
-
-	if (argc < 3) {
-		printf(NAME ": Missing arguments.\n");
-		print_syntax();
-		return EINVAL;
-	}
-
-	if (argc > 3) {
-		printf(NAME ": Too many arguments.\n");
-		print_syntax();
-		return EINVAL;
-	}
-
-	addr_spec = argv[0];
-	link_name = argv[1];
-	aobj_name = argv[2];
-
-	rc = loc_service_get_id(link_name, &link_id, 0);
-	if (rc != EOK) {
-		printf(NAME ": Service '%s' not found (%d).\n", link_name, rc);
-		return ENOENT;
-	}
-
-	rc = naddr_parse(addr_spec, &naddr);
-	if (rc != EOK) {
-		printf(NAME ": Invalid network address format '%s'.\n",
-		    addr_spec);
-		return EINVAL;
-	}
-
-	rc = inetcfg_addr_create_static(aobj_name, &naddr, link_id, &addr_id);
-	if (rc != EOK) {
-		printf(NAME ": Failed creating static address '%s' (%d)\n",
-		    aobj_name, rc);
-		return EIO;
-	}
-
-	return EOK;
-}
-
-static int addr_delete(int argc, char *argv[])
-{
-	char *aobj_name;
-	char *link_name;
-	sysarg_t link_id;
-	sysarg_t addr_id;
-	int rc;
-
-	if (argc < 2) {
-		printf(NAME ": Missing arguments.\n");
-		print_syntax();
-		return EINVAL;
-	}
-
-	if (argc > 2) {
-		printf(NAME ": Too many arguments.\n");
-		print_syntax();
-		return EINVAL;
-	}
-
-	link_name = argv[0];
-	aobj_name = argv[1];
-
-	rc = loc_service_get_id(link_name, &link_id, 0);
-	if (rc != EOK) {
-		printf(NAME ": Service '%s' not found (%d).\n", link_name, rc);
-		return ENOENT;
-	}
-
-	rc = inetcfg_addr_get_id(aobj_name, link_id, &addr_id);
-	if (rc != EOK) {
-		printf(NAME ": Address '%s' not found (%d).\n", aobj_name, rc);
-		return ENOENT;
-	}
-
-	rc = inetcfg_addr_delete(addr_id);
-	if (rc != EOK) {
-		printf(NAME ": Failed deleting address '%s' (%d)\n", aobj_name,
-		    rc);
-		return EIO;
-	}
-
-	return EOK;
-}
-
-static int sroute_create(int argc, char *argv[])
-{
-	char *dest_str;
-	char *router_str;
-	char *route_name;
-
-	inet_naddr_t dest;
-	inet_addr_t router;
-	sysarg_t sroute_id;
-	int rc;
-
-	if (argc < 3) {
-		printf(NAME ": Missing arguments.\n");
-		print_syntax();
-		return EINVAL;
-	}
-
-	if (argc > 3) {
-		printf(NAME ": Too many arguments.\n");
-		print_syntax();
-		return EINVAL;
-	}
-
-	dest_str   = argv[0];
-	router_str = argv[1];
-	route_name = argv[2];
-
-	rc = naddr_parse(dest_str, &dest);
-	if (rc != EOK) {
-		printf(NAME ": Invalid network address format '%s'.\n",
-		    dest_str);
-		return EINVAL;
-	}
-
-	rc = addr_parse(router_str, &router);
-	if (rc != EOK) {
-		printf(NAME ": Invalid address format '%s'.\n", router_str);
-		return EINVAL;
-	}
-
-	rc = inetcfg_sroute_create(route_name, &dest, &router, &sroute_id);
-	if (rc != EOK) {
-		printf(NAME ": Failed creating static route '%s' (%d)\n",
-		    route_name, rc);
-		return EIO;
-	}
-
-	return EOK;
-}
-
-static int sroute_delete(int argc, char *argv[])
-{
-	char *route_name;
-	sysarg_t sroute_id;
-	int rc;
-
-	if (argc < 1) {
-		printf(NAME ": Missing arguments.\n");
-		print_syntax();
-		return EINVAL;
-	}
-
-	if (argc > 1) {
-		printf(NAME ": Too many arguments.\n");
-		print_syntax();
-		return EINVAL;
-	}
-
-	route_name = argv[0];
-
-	rc = inetcfg_sroute_get_id(route_name, &sroute_id);
-	if (rc != EOK) {
-		printf(NAME ": Static route '%s' not found (%d).\n",
-		    route_name, rc);
-		return ENOENT;
-	}
-
-	rc = inetcfg_sroute_delete(sroute_id);
-	if (rc != EOK) {
-		printf(NAME ": Failed deleting static route '%s' (%d)\n",
-		    route_name, rc);
-		return EIO;
-	}
-
-	return EOK;
-}
-
-static int addr_list(void)
-{
-	sysarg_t *addr_list;
-	inet_addr_info_t ainfo;
-	inet_link_info_t linfo;
-
-	size_t count;
-	size_t i;
-	int rc;
-	char *astr;
-
-	rc = inetcfg_get_addr_list(&addr_list, &count);
-	if (rc != EOK) {
-		printf(NAME ": Failed getting address list.\n");
-		return rc;
-	}
-
-	printf("Configured addresses:\n");
-
-	ainfo.name = linfo.name = astr = NULL;
-
-	for (i = 0; i < count; i++) {
-		rc = inetcfg_addr_get(addr_list[i], &ainfo);
-		if (rc != EOK) {
-			printf("Failed getting properties of address %zu.\n",
-			    (size_t)addr_list[i]);
-			ainfo.name = NULL;
-			continue;
-		}
-
-		rc = inetcfg_link_get(ainfo.ilink, &linfo);
-		if (rc != EOK) {
-			printf("Failed getting properties of link %zu.\n",
-			    (size_t)ainfo.ilink);
-			linfo.name = NULL;
-			continue;
-		}
-
-		rc = naddr_format(&ainfo.naddr, &astr);
-		if (rc != EOK) {
-			printf("Memory allocation failed.\n");
-			astr = NULL;
-			goto out;
-		}
-
-		printf("    %s %s %s %zu\n", astr, linfo.name,
-		    ainfo.name, linfo.def_mtu);
-
-		free(ainfo.name);
-		free(linfo.name);
-		free(astr);
-
-		ainfo.name = linfo.name = astr = NULL;
-	}
-
-	if (count == 0)
-		printf("    None\n");
-out:
-	if (ainfo.name != NULL)
-		free(ainfo.name);
-	if (linfo.name != NULL)
-		free(linfo.name);
-	if (astr != NULL)
-		free(astr);
-
-	free(addr_list);
-
-	return EOK;
-}
-
-static int sroute_list(void)
-{
-	sysarg_t *sroute_list;
-	inet_sroute_info_t srinfo;
-
-	size_t count;
-	size_t i;
-	int rc;
-	char *dest_str;
-	char *router_str;
-
-	rc = inetcfg_get_sroute_list(&sroute_list, &count);
-	if (rc != EOK) {
-		printf(NAME ": Failed getting address list.\n");
-		return rc;
-	}
-
-	printf("Static routes:\n");
-
-	srinfo.name = dest_str = router_str = NULL;
-
-	for (i = 0; i < count; i++) {
-		rc = inetcfg_sroute_get(sroute_list[i], &srinfo);
-		if (rc != EOK) {
-			printf("Failed getting properties of static route %zu.\n",
-			    (size_t)sroute_list[i]);
-			srinfo.name = NULL;
-			continue;
-		}
-
-		rc = naddr_format(&srinfo.dest, &dest_str);
-		if (rc != EOK) {
-			printf("Memory allocation failed.\n");
-			dest_str = NULL;
-			goto out;
-		}
-
-		rc = addr_format(&srinfo.router, &router_str);
-		if (rc != EOK) {
-			printf("Memory allocation failed.\n");
-			router_str = NULL;
-			goto out;
-		}
-
-		printf("    %s %s %s\n", dest_str, router_str, srinfo.name);
-
-		free(srinfo.name);
-		free(dest_str);
-		free(router_str);
-
-		router_str = srinfo.name = dest_str = NULL;
-	}
-
-	if (count == 0)
-		printf("    None\n");
-out:
-	if (srinfo.name != NULL)
-		free(srinfo.name);
-	if (dest_str != NULL)
-		free(dest_str);
-	if (router_str != NULL)
-		free(router_str);
-
-	free(sroute_list);
-
-	return EOK;
-}
-
-int main(int argc, char *argv[])
-{
-	int rc;
-
-	rc = inetcfg_init();
-	if (rc != EOK) {
-		printf(NAME ": Failed connecting to internet service (%d).\n",
-		    rc);
-		return 1;
-	}
-
-	if (argc < 2) {
-		rc = addr_list();
-		if (rc != EOK)
-			return 1;
-		rc = sroute_list();
-		if (rc != EOK)
-			return 1;
-		return 0;
-	}
-
-	if (str_cmp(argv[1], "create") == 0) {
-		rc = addr_create_static(argc - 2, argv + 2);
-		if (rc != EOK)
-			return 1;
-	} else if (str_cmp(argv[1], "delete") == 0) {
-		rc = addr_delete(argc - 2, argv + 2);
-		if (rc != EOK)
-			return 1;
-	} else if (str_cmp(argv[1], "add-sr") == 0) {
-		rc = sroute_create(argc - 2, argv + 2);
-		if (rc != EOK)
-			return 1;
-	} else if (str_cmp(argv[1], "del-sr") == 0) {
-		rc = sroute_delete(argc - 2, argv + 2);
-		if (rc != EOK)
-			return 1;
-	} else {
-		printf(NAME ": Unknown command '%s'.\n", argv[1]);
-		print_syntax();
-		return 1;
-	}
-
-	return 0;
-}
-
-/** @}
- */
Index: uspace/app/init/init.c
===================================================================
--- uspace/app/init/init.c	(revision 6df220289417a3a5f0a319426564833d6f527370)
+++ uspace/app/init/init.c	(revision ce1836ed04784ea0bacc387ac80bfe852f36119b)
@@ -307,5 +307,5 @@
 	spawn("/srv/loopip");
 	spawn("/srv/ethip");
-	spawn("/srv/inet");
+	spawn("/srv/inetsrv");
 	spawn("/srv/tcp");
 	spawn("/srv/udp");
Index: uspace/app/loc/Makefile
===================================================================
--- uspace/app/loc/Makefile	(revision ce1836ed04784ea0bacc387ac80bfe852f36119b)
+++ uspace/app/loc/Makefile	(revision ce1836ed04784ea0bacc387ac80bfe852f36119b)
@@ -0,0 +1,35 @@
+#
+# Copyright (c) 2011 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.
+#
+
+USPACE_PREFIX = ../..
+BINARY = loc
+
+SOURCES = \
+	loc.c
+
+include $(USPACE_PREFIX)/Makefile.common
Index: uspace/app/loc/loc.c
===================================================================
--- uspace/app/loc/loc.c	(revision ce1836ed04784ea0bacc387ac80bfe852f36119b)
+++ uspace/app/loc/loc.c	(revision ce1836ed04784ea0bacc387ac80bfe852f36119b)
@@ -0,0 +1,163 @@
+/*
+ * Copyright (c) 2011 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 loc
+ * @{
+ */
+/** @file loc.c Print information from location service.
+ */
+
+#include <errno.h>
+#include <loc.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <str.h>
+#include <sys/types.h>
+#include <sys/typefmt.h>
+
+#define NAME "loc"
+
+static int show_cat(const char *cat_name, category_id_t cat_id)
+{
+	service_id_t *svc_ids;
+	size_t svc_cnt;
+	char *svc_name;
+	int rc;
+	size_t j;
+
+	printf("%s (%" PRIun "):\n", cat_name, cat_id);
+
+	rc = loc_category_get_svcs(cat_id, &svc_ids, &svc_cnt);
+	if (rc != EOK) {
+		printf(NAME ": Failed getting list of services in "
+		    "category %s, skipping.\n", cat_name);
+		return rc;
+	}
+
+	for (j = 0; j < svc_cnt; j++) {
+		rc = loc_service_get_name(svc_ids[j], &svc_name);
+		if (rc != EOK) {
+			printf(NAME ": Unknown service name (SID %"
+			    PRIun ").\n", svc_ids[j]);
+			continue;
+		}
+		printf("\t%s (%" PRIun ")\n", svc_name, svc_ids[j]);
+		free(svc_name);
+	}
+
+	free(svc_ids);
+	return EOK;
+}
+
+static int list_svcs_by_cat(void)
+{
+	category_id_t *cat_ids;
+	size_t cat_cnt;
+
+	size_t i;
+	char *cat_name;
+	int rc;
+
+	rc = loc_get_categories(&cat_ids, &cat_cnt);
+	if (rc != EOK) {
+		printf(NAME ": Error getting list of categories.\n");
+		return rc;
+	}
+
+	for (i = 0; i < cat_cnt; i++) {
+		rc = loc_category_get_name(cat_ids[i], &cat_name);
+		if (rc != EOK)
+			cat_name = str_dup("<unknown>");
+
+		if (cat_name == NULL) {
+			printf(NAME ": Error allocating memory.\n");
+			free(cat_ids);
+			return rc;
+		}
+
+		rc = show_cat(cat_name, cat_ids[i]);
+		(void) rc;
+
+		free(cat_name);
+	}
+
+	free(cat_ids);
+	return EOK;
+}
+
+static void print_syntax(void)
+{
+	printf("syntax:\n"
+	    "\t" NAME "                      List categories and services "
+		"they contain\n"
+	    "\t" NAME " show-cat <category>  List services in category\n");
+}
+
+int main(int argc, char *argv[])
+{
+	int rc;
+	char *cmd;
+	char *cat_name;
+	category_id_t cat_id;
+
+	if (argc <= 1) {
+		rc = list_svcs_by_cat();
+		if (rc != EOK)
+			return 1;
+		return 0;
+	}
+
+	cmd = argv[1];
+	if (str_cmp(cmd, "show-cat") == 0) {
+		if (argc < 3) {
+			printf("Argument missing.\n");
+			print_syntax();
+			return 1;
+		}
+
+		cat_name = argv[2];
+		rc = loc_category_get_id(cat_name, &cat_id, 0);
+		if (rc != EOK) {
+			printf("Error looking up category '%s'.\n", cat_name);
+			return 1;
+		}
+
+		rc = show_cat(cat_name, cat_id);
+		if (rc != EOK)
+			return 1;
+	} else {
+		printf("Invalid command '%s'\n", cmd);
+		print_syntax();
+		return 1;
+	}
+
+	return 0;
+}
+
+/** @}
+ */
Index: pace/app/locinfo/Makefile
===================================================================
--- uspace/app/locinfo/Makefile	(revision 6df220289417a3a5f0a319426564833d6f527370)
+++ 	(revision )
@@ -1,36 +1,0 @@
-#
-# Copyright (c) 2011 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.
-#
-
-USPACE_PREFIX = ../..
-EXTRA_CFLAGS = -Iinclude
-BINARY = locinfo
-
-SOURCES = \
-	locinfo.c
-
-include $(USPACE_PREFIX)/Makefile.common
Index: pace/app/locinfo/locinfo.c
===================================================================
--- uspace/app/locinfo/locinfo.c	(revision 6df220289417a3a5f0a319426564833d6f527370)
+++ 	(revision )
@@ -1,103 +1,0 @@
-/*
- * Copyright (c) 2011 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 locinfo
- * @{
- */
-/** @file locinfo.c Print information from location service.
- */
-
-#include <errno.h>
-#include <loc.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <str.h>
-#include <sys/types.h>
-#include <sys/typefmt.h>
-
-#define NAME "locinfo"
-
-int main(int argc, char *argv[])
-{
-	category_id_t *cat_ids;
-	size_t cat_cnt;
-	service_id_t *svc_ids;
-	size_t svc_cnt;
-
-	size_t i, j;
-	char *cat_name;
-	char *svc_name;
-	int rc;
-
-	rc = loc_get_categories(&cat_ids, &cat_cnt);
-	if (rc != EOK) {
-		printf(NAME ": Error getting list of categories.\n");
-		return 1;
-	}
-
-	for (i = 0; i < cat_cnt; i++) {
-		rc = loc_category_get_name(cat_ids[i], &cat_name);
-		if (rc != EOK)
-			cat_name = str_dup("<unknown>");
-
-		if (cat_name == NULL) {
-			printf(NAME ": Error allocating memory.\n");
-			return 1;
-		}
-
-		printf("%s (%" PRIun "):\n", cat_name, cat_ids[i]);
-
-		rc = loc_category_get_svcs(cat_ids[i], &svc_ids, &svc_cnt);
-		if (rc != EOK) {
-			printf(NAME ": Failed getting list of services in "
-			    "category %s, skipping.\n", cat_name);
-			free(cat_name);
-			continue;
-		}
-
-		for (j = 0; j < svc_cnt; j++) {
-			rc = loc_service_get_name(svc_ids[j], &svc_name);
-			if (rc != EOK) {
-				printf(NAME ": Unknown service name (SID %"
-				    PRIun ").\n", svc_ids[j]);
-				continue;
-			}
-			printf("\t%s (%" PRIun ")\n", svc_name, svc_ids[j]);
-			free(svc_name);
-		}
-
-		free(svc_ids);
-		free(cat_name);
-	}
-
-	free(cat_ids);
-	return 0;
-}
-
-/** @}
- */
Index: pace/app/websrv/Makefile
===================================================================
--- uspace/app/websrv/Makefile	(revision 6df220289417a3a5f0a319426564833d6f527370)
+++ 	(revision )
@@ -1,37 +1,0 @@
-#
-# Copyright (c) 2010 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.
-#
-
-USPACE_PREFIX = ../..
-LIBS =
-EXTRA_CFLAGS =
-BINARY = websrv
-
-SOURCES = \
-	websrv.c
-
-include $(USPACE_PREFIX)/Makefile.common
Index: pace/app/websrv/websrv.c
===================================================================
--- uspace/app/websrv/websrv.c	(revision 6df220289417a3a5f0a319426564833d6f527370)
+++ 	(revision )
@@ -1,422 +1,0 @@
-/*
- * Copyright (c) 2011 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 websrv
- * @{
- */
-/**
- * @file Skeletal web server.
- */
-
-#include <bool.h>
-#include <errno.h>
-#include <stdio.h>
-#include <sys/types.h>
-#include <sys/stat.h>
-#include <stdlib.h>
-#include <fcntl.h>
-
-#include <net/in.h>
-#include <net/inet.h>
-#include <net/socket.h>
-
-#include <arg_parse.h>
-#include <macros.h>
-#include <str.h>
-#include <str_error.h>
-
-#define NAME  "websrv"
-
-#define DEFAULT_PORT  8080
-#define BACKLOG_SIZE  3
-
-#define WEB_ROOT  "/data/web"
-
-/** Buffer for receiving the request. */
-#define BUFFER_SIZE  1024
-
-static uint16_t port = DEFAULT_PORT;
-
-static char rbuf[BUFFER_SIZE];
-static size_t rbuf_out;
-static size_t rbuf_in;
-
-static char lbuf[BUFFER_SIZE + 1];
-static size_t lbuf_used;
-
-static char fbuf[BUFFER_SIZE];
-
-/** Responses to send to client. */
-
-static const char *msg_ok =
-    "HTTP/1.0 200 OK\r\n"
-    "\r\n";
-
-static const char *msg_bad_request =
-    "HTTP/1.0 400 Bad Request\r\n"
-    "\r\n"
-    "<!DOCTYPE HTML PUBLIC \"-//IETF//DTD HTML 2.0//EN\">\r\n"
-    "<html><head>\r\n"
-    "<title>400 Bad Request</title>\r\n"
-    "</head>\r\n"
-    "<body>\r\n"
-    "<h1>Bad Request</h1>\r\n"
-    "<p>The requested URL has bad syntax.</p>\r\n"
-    "</body>\r\n"
-    "</html>\r\n";
-
-static const char *msg_not_found =
-    "HTTP/1.0 404 Not Found\r\n"
-    "\r\n"
-    "<!DOCTYPE HTML PUBLIC \"-//IETF//DTD HTML 2.0//EN\">\r\n"
-    "<html><head>\r\n"
-    "<title>404 Not Found</title>\r\n"
-    "</head>\r\n"
-    "<body>\r\n"
-    "<h1>Not Found</h1>\r\n"
-    "<p>The requested URL was not found on this server.</p>\r\n"
-    "</body>\r\n"
-    "</html>\r\n";
-
-static const char *msg_not_implemented =
-    "HTTP/1.0 501 Not Implemented\r\n"
-    "\r\n"
-    "<!DOCTYPE HTML PUBLIC \"-//IETF//DTD HTML 2.0//EN\">\r\n"
-    "<html><head>\r\n"
-    "<title>501 Not Implemented</title>\r\n"
-    "</head>\r\n"
-    "<body>\r\n"
-    "<h1>Not Implemented</h1>\r\n"
-    "<p>The requested method is not implemented on this server.</p>\r\n"
-    "</body>\r\n"
-    "</html>\r\n";
-
-/** Receive one character (with buffering) */
-static int recv_char(int fd, char *c)
-{
-	if (rbuf_out == rbuf_in) {
-		rbuf_out = 0;
-		rbuf_in = 0;
-		
-		ssize_t rc = recv(fd, rbuf, BUFFER_SIZE, 0);
-		if (rc <= 0) {
-			fprintf(stderr, "recv() failed (%zd)\n", rc);
-			return rc;
-		}
-		
-		rbuf_in = rc;
-	}
-	
-	*c = rbuf[rbuf_out++];
-	return EOK;
-}
-
-/** Receive one line with length limit */
-static int recv_line(int fd)
-{
-	char *bp = lbuf;
-	char c = '\0';
-	
-	while (bp < lbuf + BUFFER_SIZE) {
-		char prev = c;
-		int rc = recv_char(fd, &c);
-		
-		if (rc != EOK)
-			return rc;
-		
-		*bp++ = c;
-		if ((prev == '\r') && (c == '\n'))
-			break;
-	}
-	
-	lbuf_used = bp - lbuf;
-	*bp = '\0';
-	
-	if (bp == lbuf + BUFFER_SIZE)
-		return ELIMIT;
-	
-	return EOK;
-}
-
-static bool uri_is_valid(char *uri)
-{
-	if (uri[0] != '/')
-		return false;
-	
-	if (uri[1] == '.')
-		return false;
-	
-	char *cp = uri + 1;
-	
-	while (*cp != '\0') {
-		char c = *cp++;
-		if (c == '/')
-			return false;
-	}
-	
-	return true;
-}
-
-static int send_response(int conn_sd, const char *msg)
-{
-	size_t response_size = str_size(msg);
-	
-	fprintf(stderr, "Sending response\n");
-	ssize_t rc = send(conn_sd, (void *) msg, response_size, 0);
-	if (rc < 0) {
-		fprintf(stderr, "send() failed\n");
-		return rc;
-	}
-	
-	return EOK;
-}
-
-static int uri_get(const char *uri, int conn_sd)
-{
-	if (str_cmp(uri, "/") == 0)
-		uri = "/index.html";
-	
-	char *fname;
-	int rc = asprintf(&fname, "%s%s", WEB_ROOT, uri);
-	if (rc < 0)
-		return ENOMEM;
-	
-	int fd = open(fname, O_RDONLY);
-	if (fd < 0) {
-		rc = send_response(conn_sd, msg_not_found);
-		free(fname);
-		return rc;
-	}
-	
-	free(fname);
-	
-	rc = send_response(conn_sd, msg_ok);
-	if (rc != EOK)
-		return rc;
-	
-	while (true) {
-		ssize_t nr = read(fd, fbuf, BUFFER_SIZE);
-		if (nr == 0)
-			break;
-		
-		if (nr < 0) {
-			close(fd);
-			return EIO;
-		}
-		
-		rc = send(conn_sd, fbuf, nr, 0);
-		if (rc < 0) {
-			fprintf(stderr, "send() failed\n");
-			close(fd);
-			return rc;
-		}
-	}
-	
-	close(fd);
-	
-	return EOK;
-}
-
-static int req_process(int conn_sd)
-{
-	int rc = recv_line(conn_sd);
-	if (rc != EOK) {
-		fprintf(stderr, "recv_line() failed\n");
-		return rc;
-	}
-	
-	fprintf(stderr, "Request: %s", lbuf);
-	
-	if (str_lcmp(lbuf, "GET ", 4) != 0) {
-		rc = send_response(conn_sd, msg_not_implemented);
-		return rc;
-	}
-	
-	char *uri = lbuf + 4;
-	char *end_uri = str_chr(uri, ' ');
-	if (end_uri == NULL) {
-		end_uri = lbuf + lbuf_used - 2;
-		assert(*end_uri == '\r');
-	}
-	
-	*end_uri = '\0';
-	fprintf(stderr, "Requested URI: %s\n", uri);
-	
-	if (!uri_is_valid(uri)) {
-		rc = send_response(conn_sd, msg_bad_request);
-		return rc;
-	}
-	
-	return uri_get(uri, conn_sd);
-}
-
-static void usage(void)
-{
-	printf("Skeletal server\n"
-	    "\n"
-	    "Usage: " NAME " [options]\n"
-	    "\n"
-	    "Where options are:\n"
-	    "-p port_number | --port=port_number\n"
-	    "\tListening port (default " STRING(DEFAULT_PORT) ").\n"
-	    "\n"
-	    "-h | --help\n"
-	    "\tShow this application help.\n");
-}
-
-static int parse_option(int argc, char *argv[], int *index)
-{
-	int value;
-	int rc;
-	
-	switch (argv[*index][1]) {
-	case 'h':
-		usage();
-		exit(0);
-		break;
-	case 'p':
-		rc = arg_parse_int(argc, argv, index, &value, 0);
-		if (rc != EOK)
-			return rc;
-		
-		port = (uint16_t) value;
-		break;
-	/* Long options with double dash */
-	case '-':
-		if (str_lcmp(argv[*index] + 2, "help", 5) == 0) {
-			usage();
-			exit(0);
-		} else if (str_lcmp(argv[*index] + 2, "port=", 5) == 0) {
-			rc = arg_parse_int(argc, argv, index, &value, 7);
-			if (rc != EOK)
-				return rc;
-			
-			port = (uint16_t) value;
-		} else {
-			usage();
-			return EINVAL;
-		}
-		break;
-	default:
-		usage();
-		return EINVAL;
-	}
-	
-	return EOK;
-}
-
-int main(int argc, char *argv[])
-{
-	/* Parse command line arguments */
-	for (int i = 1; i < argc; i++) {
-		if (argv[i][0] == '-') {
-			int rc = parse_option(argc, argv, &i);
-			if (rc != EOK)
-				return rc;
-		} else {
-			usage();
-			return EINVAL;
-		}
-	}
-	
-	struct sockaddr_in addr;
-	
-	addr.sin_family = AF_INET;
-	addr.sin_port = htons(port);
-	
-	int rc = inet_pton(AF_INET, "127.0.0.1", (void *)
-	    &addr.sin_addr.s_addr);
-	if (rc != EOK) {
-		fprintf(stderr, "Error parsing network address (%s)\n",
-		    str_error(rc));
-		return 1;
-	}
-	
-	fprintf(stderr, "Creating socket\n");
-	
-	int listen_sd = socket(PF_INET, SOCK_STREAM, 0);
-	if (listen_sd < 0) {
-		fprintf(stderr, "Error creating listening socket (%s)\n",
-		    str_error(listen_sd));
-		return 2;
-	}
-	
-	rc = bind(listen_sd, (struct sockaddr *) &addr, sizeof(addr));
-	if (rc != EOK) {
-		fprintf(stderr, "Error binding socket (%s)\n",
-		    str_error(rc));
-		return 3;
-	}
-	
-	rc = listen(listen_sd, BACKLOG_SIZE);
-	if (rc != EOK) {
-		fprintf(stderr, "listen() failed (%s)\n", str_error(rc));
-		return 4;
-	}
-	
-	fprintf(stderr, "Listening for connections at port %" PRIu16 "\n",
-	    port);
-	while (true) {
-		struct sockaddr_in raddr;
-		socklen_t raddr_len = sizeof(raddr);
-		int conn_sd = accept(listen_sd, (struct sockaddr *) &raddr,
-		    &raddr_len);
-		
-		if (conn_sd < 0) {
-			fprintf(stderr, "accept() failed (%s)\n", str_error(rc));
-			continue;
-		}
-		
-		fprintf(stderr, "Connection accepted (sd=%d), "
-		    "waiting for request\n", conn_sd);
-		
-		rbuf_out = 0;
-		rbuf_in = 0;
-		
-		rc = req_process(conn_sd);
-		if (rc != EOK)
-			fprintf(stderr, "Error processing request (%s)\n",
-			    str_error(rc));
-		
-		rc = closesocket(conn_sd);
-		if (rc != EOK) {
-			fprintf(stderr, "Error closing connection socket (%s)\n",
-			    str_error(rc));
-			closesocket(listen_sd);
-			return 5;
-		}
-		
-		fprintf(stderr, "Connection closed\n");
-	}
-	
-	/* Not reached */
-	return 0;
-}
-
-/** @}
- */
Index: pace/srv/loc/Makefile
===================================================================
--- uspace/srv/loc/Makefile	(revision 6df220289417a3a5f0a319426564833d6f527370)
+++ 	(revision )
@@ -1,38 +1,0 @@
-#
-# Copyright (c) 2005 Martin Decky
-# Copyright (c) 2007 Jakub Jermar
-# 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.
-#
-
-USPACE_PREFIX = ../..
-BINARY = loc
-STATIC_NEEDED = y
-
-SOURCES = \
-	category.c \
-	loc.c
-
-include $(USPACE_PREFIX)/Makefile.common
Index: pace/srv/loc/category.c
===================================================================
--- uspace/srv/loc/category.c	(revision 6df220289417a3a5f0a319426564833d6f527370)
+++ 	(revision )
@@ -1,213 +1,0 @@
-/*
- * Copyright (c) 2011 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 loc
- * @{
- */
-/** @file Categories for location service.
- */
-
-#include <adt/list.h>
-#include <errno.h>
-#include <fibril_synch.h>
-#include <stdlib.h>
-#include <str.h>
-
-#include "category.h"
-#include "loc.h"
-
-/** Initialize category directory. */
-void categ_dir_init(categ_dir_t *cdir)
-{
-	fibril_mutex_initialize(&cdir->mutex);
-	list_initialize(&cdir->categories);
-}
-
-/** Add new category to directory. */
-void categ_dir_add_cat(categ_dir_t *cdir, category_t *cat)
-{
-	list_append(&cat->cat_list, &cdir->categories);
-}
-
-/** Get list of categories. */
-int categ_dir_get_categories(categ_dir_t *cdir, category_id_t *id_buf,
-    size_t buf_size, size_t *act_size)
-{
-	size_t act_cnt;
-	size_t buf_cnt;
-
-	assert(fibril_mutex_is_locked(&cdir->mutex));
-
-	buf_cnt = buf_size / sizeof(category_id_t);
-
-	act_cnt = list_count(&cdir->categories);
-	*act_size = act_cnt * sizeof(category_id_t);
-
-	if (buf_size % sizeof(category_id_t) != 0)
-		return EINVAL;
-
-	size_t pos = 0;
-	list_foreach(cdir->categories, item) {
-		category_t *cat =
-		    list_get_instance(item, category_t, cat_list);
-
-		if (pos < buf_cnt)
-			id_buf[pos] = cat->id;
-		pos++;
-	}
-
-	return EOK;
-}
-
-
-/** Initialize category structure. */
-static void category_init(category_t *cat, const char *name)
-{
-	fibril_mutex_initialize(&cat->mutex);
-	cat->name = str_dup(name);
-	cat->id = loc_create_id();
-	link_initialize(&cat->cat_list);
-	list_initialize(&cat->svc_memb);
-}
-
-/** Allocate new category. */
-category_t *category_new(const char *name)
-{
-	category_t *cat;
-
-	cat = malloc(sizeof(category_t));
-	if (cat == NULL)
-		return NULL;
-
-	category_init(cat, name);
-	return cat;
-}
-
-/** Add service to category. */
-int category_add_service(category_t *cat, loc_service_t *svc)
-{
-	assert(fibril_mutex_is_locked(&cat->mutex));
-	assert(fibril_mutex_is_locked(&services_list_mutex));
-
-	/* Verify that category does not contain this service yet. */
-	list_foreach(cat->svc_memb, item) {
-		svc_categ_t *memb = list_get_instance(item, svc_categ_t,
-		    cat_link);
-		if (memb->svc == svc) {
-			return EEXIST;
-		}
-	}
-
-	svc_categ_t *nmemb = malloc(sizeof(svc_categ_t));
-	if (nmemb == NULL)
-		return ENOMEM;
-
-	nmemb->svc = svc;
-	nmemb->cat = cat;
-
-	list_append(&nmemb->cat_link, &cat->svc_memb);
-	list_append(&nmemb->svc_link, &svc->cat_memb);
-
-	return EOK;
-}
-
-/** Remove service from category. */
-void category_remove_service(svc_categ_t *memb)
-{
-	assert(fibril_mutex_is_locked(&memb->cat->mutex));
-	assert(fibril_mutex_is_locked(&services_list_mutex));
-
-	list_remove(&memb->cat_link);
-	list_remove(&memb->svc_link);
-
-	free(memb);
-}
-
-/** Get category by ID. */
-category_t *category_get(categ_dir_t *cdir, catid_t catid)
-{
-	assert(fibril_mutex_is_locked(&cdir->mutex));
-
-	list_foreach(cdir->categories, item) {
-		category_t *cat = list_get_instance(item, category_t,
-		    cat_list);
-		if (cat->id == catid)
-			return cat;
-	}
-
-	return NULL;
-}
-
-/** Find category by name. */
-category_t *category_find_by_name(categ_dir_t *cdir, const char *name)
-{
-	assert(fibril_mutex_is_locked(&cdir->mutex));
-
-	list_foreach(cdir->categories, item) {
-		category_t *cat = list_get_instance(item, category_t,
-		    cat_list);
-		if (str_cmp(cat->name, name) == 0)
-			return cat;
-	}
-
-	return NULL;
-}
-
-/** Get list of services in category. */
-int category_get_services(category_t *cat, service_id_t *id_buf,
-    size_t buf_size, size_t *act_size)
-{
-	size_t act_cnt;
-	size_t buf_cnt;
-
-	assert(fibril_mutex_is_locked(&cat->mutex));
-
-	buf_cnt = buf_size / sizeof(service_id_t);
-
-	act_cnt = list_count(&cat->svc_memb);
-	*act_size = act_cnt * sizeof(service_id_t);
-
-	if (buf_size % sizeof(service_id_t) != 0)
-		return EINVAL;
-
-	size_t pos = 0;
-	list_foreach(cat->svc_memb, item) {
-		svc_categ_t *memb =
-		    list_get_instance(item, svc_categ_t, cat_link);
-
-		if (pos < buf_cnt)
-			id_buf[pos] = memb->svc->id;
-		pos++;
-	}
-
-	return EOK;
-}
-
-/**
- * @}
- */
Index: pace/srv/loc/category.h
===================================================================
--- uspace/srv/loc/category.h	(revision 6df220289417a3a5f0a319426564833d6f527370)
+++ 	(revision )
@@ -1,98 +1,0 @@
-/*
- * Copyright (c) 2011 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 loc
- * @{
- */
-/** @file Categories for location service.
- */
-
-#ifndef CATEGORY_H_
-#define CATEGORY_H_
-
-#include <adt/list.h>
-#include "loc.h"
-
-typedef sysarg_t catid_t;
-
-/** Service category */
-typedef struct {
-	/** Protects this structure, list of services */
-	fibril_mutex_t mutex;
-
-	/** Identifier */
-	catid_t id;
-
-	/** Category name */
-	const char *name;
-
-	/** Link to list of categories (categ_dir_t.categories) */
-	link_t cat_list;
-
-	/** List of service memberships in this category (svc_categ_t) */
-	list_t svc_memb;
-} category_t;
-
-/** Service directory ogranized by categories (yellow pages) */
-typedef struct {
-	/** Protects this structure, list of categories */
-	fibril_mutex_t mutex;
-	/** List of all categories (category_t) */
-	list_t categories;
-} categ_dir_t;
-
-/** Service in category membership. */
-typedef struct {
-	/** Link to category_t.svc_memb list */
-	link_t cat_link;
-	/** Link to loc_service_t.cat_memb list */
-	link_t svc_link;
-	
-	/** Category */
-	category_t *cat;
-	/** Service */
-	loc_service_t *svc;
-} svc_categ_t;
-
-extern void categ_dir_init(categ_dir_t *);
-extern void categ_dir_add_cat(categ_dir_t *, category_t *);
-extern int categ_dir_get_categories(categ_dir_t *, service_id_t *, size_t,
-    size_t *);
-extern category_t *category_new(const char *);
-extern int category_add_service(category_t *, loc_service_t *);
-extern void category_remove_service(svc_categ_t *);
-extern category_t *category_get(categ_dir_t *, catid_t);
-extern category_t *category_find_by_name(categ_dir_t *, const char *);
-extern int category_get_services(category_t *, service_id_t *, size_t,
-    size_t *);
-
-
-#endif
-
-/** @}
- */
Index: pace/srv/loc/loc.c
===================================================================
--- uspace/srv/loc/loc.c	(revision 6df220289417a3a5f0a319426564833d6f527370)
+++ 	(revision )
@@ -1,1504 +1,0 @@
-/*
- * Copyright (c) 2007 Josef Cejka
- * Copyright (c) 2011 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.
- */
-
-/**
- * @defgroup loc Location Service.
- * @brief HelenOS location service.
- * @{
- */
-
-/** @file
- */
-
-#include <ipc/services.h>
-#include <ns.h>
-#include <async.h>
-#include <stdio.h>
-#include <errno.h>
-#include <bool.h>
-#include <fibril_synch.h>
-#include <macros.h>
-#include <stdlib.h>
-#include <str.h>
-#include <ipc/loc.h>
-#include <assert.h>
-
-#include "category.h"
-#include "loc.h"
-
-#define NAME          "loc"
-#define NULL_SERVICES  256
-
-/** Callback session */
-typedef struct {
-	link_t cb_sess_list;
-	async_sess_t *sess;
-} cb_sess_t;
-
-LIST_INITIALIZE(services_list);
-LIST_INITIALIZE(namespaces_list);
-LIST_INITIALIZE(servers_list);
-
-/* Locking order:
- *  servers_list_mutex
- *  services_list_mutex
- *  (loc_server_t *)->services_mutex
- *  create_id_mutex
- **/
-
-FIBRIL_MUTEX_INITIALIZE(services_list_mutex);
-static FIBRIL_CONDVAR_INITIALIZE(services_list_cv);
-static FIBRIL_MUTEX_INITIALIZE(servers_list_mutex);
-static FIBRIL_MUTEX_INITIALIZE(create_id_mutex);
-static FIBRIL_MUTEX_INITIALIZE(null_services_mutex);
-
-static service_id_t last_id = 0;
-static loc_service_t *null_services[NULL_SERVICES];
-
-/*
- * Dummy list for null services. This is necessary so that null services can
- * be used just as any other services, e.g. in loc_service_unregister_core().
- */
-static LIST_INITIALIZE(dummy_null_services);
-
-/** Service directory ogranized by categories (yellow pages) */
-static categ_dir_t cdir;
-
-static FIBRIL_MUTEX_INITIALIZE(callback_sess_mutex);
-static LIST_INITIALIZE(callback_sess_list);
-
-service_id_t loc_create_id(void)
-{
-	/* TODO: allow reusing old ids after their unregistration
-	 * and implement some version of LRU algorithm, avoid overflow
-	 */
-	
-	fibril_mutex_lock(&create_id_mutex);
-	last_id++;
-	fibril_mutex_unlock(&create_id_mutex);
-	
-	return last_id;
-}
-
-/** Convert fully qualified service name to namespace and service name.
- *
- * A fully qualified service name can be either a plain service name
- * (then the namespace is considered to be an empty string) or consist
- * of two components separated by a slash. No more than one slash
- * is allowed.
- *
- */
-static bool loc_fqsn_split(const char *fqsn, char **ns_name, char **name)
-{
-	size_t cnt = 0;
-	size_t slash_offset = 0;
-	size_t slash_after = 0;
-	
-	size_t offset = 0;
-	size_t offset_prev = 0;
-	wchar_t c;
-	
-	while ((c = str_decode(fqsn, &offset, STR_NO_LIMIT)) != 0) {
-		if (c == '/') {
-			cnt++;
-			slash_offset = offset_prev;
-			slash_after = offset;
-		}
-		offset_prev = offset;
-	}
-	
-	/* More than one slash */
-	if (cnt > 1)
-		return false;
-	
-	/* No slash -> namespace is empty */
-	if (cnt == 0) {
-		*ns_name = str_dup("");
-		if (*ns_name == NULL)
-			return false;
-		
-		*name = str_dup(fqsn);
-		if (*name == NULL) {
-			free(*ns_name);
-			return false;
-		}
-		
-		if (str_cmp(*name, "") == 0) {
-			free(*name);
-			free(*ns_name);
-			return false;
-		}
-		
-		return true;
-	}
-	
-	/* Exactly one slash */
-	*ns_name = str_ndup(fqsn, slash_offset);
-	if (*ns_name == NULL)
-		return false;
-	
-	*name = str_dup(fqsn + slash_after);
-	if (*name == NULL) {
-		free(*ns_name);
-		return false;
-	}
-	
-	if (str_cmp(*name, "") == 0) {
-		free(*name);
-		free(*ns_name);
-		return false;
-	}
-	
-	return true;
-}
-
-/** Find namespace with given name. */
-static loc_namespace_t *loc_namespace_find_name(const char *name)
-{
-	assert(fibril_mutex_is_locked(&services_list_mutex));
-	
-	list_foreach(namespaces_list, item) {
-		loc_namespace_t *namespace =
-		    list_get_instance(item, loc_namespace_t, namespaces);
-		if (str_cmp(namespace->name, name) == 0)
-			return namespace;
-	}
-	
-	return NULL;
-}
-
-/** Find namespace with given ID.
- *
- * @todo: use hash table
- *
- */
-static loc_namespace_t *loc_namespace_find_id(service_id_t id)
-{
-	assert(fibril_mutex_is_locked(&services_list_mutex));
-	
-	list_foreach(namespaces_list, item) {
-		loc_namespace_t *namespace =
-		    list_get_instance(item, loc_namespace_t, namespaces);
-		if (namespace->id == id)
-			return namespace;
-	}
-	
-	return NULL;
-}
-
-/** Find service with given name. */
-static loc_service_t *loc_service_find_name(const char *ns_name,
-    const char *name)
-{
-	assert(fibril_mutex_is_locked(&services_list_mutex));
-	
-	list_foreach(services_list, item) {
-		loc_service_t *service =
-		    list_get_instance(item, loc_service_t, services);
-		if ((str_cmp(service->namespace->name, ns_name) == 0)
-		    && (str_cmp(service->name, name) == 0))
-			return service;
-	}
-	
-	return NULL;
-}
-
-/** Find service with given ID.
- *
- * @todo: use hash table
- *
- */
-static loc_service_t *loc_service_find_id(service_id_t id)
-{
-	assert(fibril_mutex_is_locked(&services_list_mutex));
-	
-	list_foreach(services_list, item) {
-		loc_service_t *service =
-		    list_get_instance(item, loc_service_t, services);
-		if (service->id == id)
-			return service;
-	}
-	
-	return NULL;
-}
-
-/** Create a namespace (if not already present). */
-static loc_namespace_t *loc_namespace_create(const char *ns_name)
-{
-	loc_namespace_t *namespace;
-	
-	assert(fibril_mutex_is_locked(&services_list_mutex));
-	
-	namespace = loc_namespace_find_name(ns_name);
-	if (namespace != NULL)
-		return namespace;
-	
-	namespace = (loc_namespace_t *) malloc(sizeof(loc_namespace_t));
-	if (namespace == NULL)
-		return NULL;
-	
-	namespace->name = str_dup(ns_name);
-	if (namespace->name == NULL) {
-		free(namespace);
-		return NULL;
-	}
-	
-	namespace->id = loc_create_id();
-	namespace->refcnt = 0;
-	
-	/*
-	 * Insert new namespace into list of registered namespaces
-	 */
-	list_append(&(namespace->namespaces), &namespaces_list);
-	
-	return namespace;
-}
-
-/** Destroy a namespace (if it is no longer needed). */
-static void loc_namespace_destroy(loc_namespace_t *namespace)
-{
-	assert(fibril_mutex_is_locked(&services_list_mutex));
-
-	if (namespace->refcnt == 0) {
-		list_remove(&(namespace->namespaces));
-		
-		free(namespace->name);
-		free(namespace);
-	}
-}
-
-/** Increase namespace reference count by including service. */
-static void loc_namespace_addref(loc_namespace_t *namespace,
-    loc_service_t *service)
-{
-	assert(fibril_mutex_is_locked(&services_list_mutex));
-
-	service->namespace = namespace;
-	namespace->refcnt++;
-}
-
-/** Decrease namespace reference count. */
-static void loc_namespace_delref(loc_namespace_t *namespace)
-{
-	assert(fibril_mutex_is_locked(&services_list_mutex));
-
-	namespace->refcnt--;
-	loc_namespace_destroy(namespace);
-}
-
-/** Unregister service and free it. */
-static void loc_service_unregister_core(loc_service_t *service)
-{
-	assert(fibril_mutex_is_locked(&services_list_mutex));
-	assert(fibril_mutex_is_locked(&cdir.mutex));
-	
-	loc_namespace_delref(service->namespace);
-	list_remove(&(service->services));
-	list_remove(&(service->server_services));
-	
-	/* Remove service from all categories. */
-	while (!list_empty(&service->cat_memb)) {
-		link_t *link = list_first(&service->cat_memb);
-		svc_categ_t *memb = list_get_instance(link, svc_categ_t,
-		    svc_link);
-		fibril_mutex_lock(&memb->cat->mutex);
-		category_remove_service(memb);
-		fibril_mutex_unlock(&memb->cat->mutex);
-	}
-	
-	free(service->name);
-	free(service);
-}
-
-/**
- * Read info about new server and add it into linked list of registered
- * servers.
- */
-static loc_server_t *loc_server_register(void)
-{
-	ipc_call_t icall;
-	ipc_callid_t iid = async_get_call(&icall);
-	
-	if (IPC_GET_IMETHOD(icall) != LOC_SERVER_REGISTER) {
-		async_answer_0(iid, EREFUSED);
-		return NULL;
-	}
-	
-	loc_server_t *server =
-	    (loc_server_t *) malloc(sizeof(loc_server_t));
-	if (server == NULL) {
-		async_answer_0(iid, ENOMEM);
-		return NULL;
-	}
-	
-	/*
-	 * Get server name
-	 */
-	int rc = async_data_write_accept((void **) &server->name, true, 0,
-	    LOC_NAME_MAXLEN, 0, NULL);
-	if (rc != EOK) {
-		free(server);
-		async_answer_0(iid, rc);
-		return NULL;
-	}
-	
-	/*
-	 * Create connection to the server
-	 */
-	server->sess = async_callback_receive(EXCHANGE_SERIALIZE);
-	if (!server->sess) {
-		free(server->name);
-		free(server);
-		async_answer_0(iid, ENOTSUP);
-		return NULL;
-	}
-	
-	/*
-	 * Initialize mutex for list of services
-	 * supplied by this server
-	 */
-	fibril_mutex_initialize(&server->services_mutex);
-	
-	/*
-	 * Initialize list of supplied services
-	 */
-	list_initialize(&server->services);
-
-	link_initialize(&server->servers);
-	
-	fibril_mutex_lock(&servers_list_mutex);
-	
-	/* TODO:
-	 * Check that no server with name equal to
-	 * server->name is registered
-	 */
-	
-	/*
-	 * Insert new server into list of registered servers
-	 */
-	list_append(&(server->servers), &servers_list);
-	fibril_mutex_unlock(&servers_list_mutex);
-	
-	async_answer_0(iid, EOK);
-	
-	return server;
-}
-
-/**
- * Unregister server, unregister all its services and free server
- * structure.
- *
- */
-static int loc_server_unregister(loc_server_t *server)
-{
-	if (server == NULL)
-		return EEXISTS;
-	
-	fibril_mutex_lock(&servers_list_mutex);
-	
-	if (server->sess)
-		async_hangup(server->sess);
-	
-	/* Remove it from list of servers */
-	list_remove(&(server->servers));
-	
-	/* Unregister all its services */
-	fibril_mutex_lock(&services_list_mutex);
-	fibril_mutex_lock(&server->services_mutex);
-	fibril_mutex_lock(&cdir.mutex);
-	
-	while (!list_empty(&server->services)) {
-		loc_service_t *service = list_get_instance(
-		    list_first(&server->services), loc_service_t,
-		    server_services);
-		loc_service_unregister_core(service);
-	}
-	
-	fibril_mutex_unlock(&cdir.mutex);
-	fibril_mutex_unlock(&server->services_mutex);
-	fibril_mutex_unlock(&services_list_mutex);
-	fibril_mutex_unlock(&servers_list_mutex);
-	
-	/* Free name and server */
-	if (server->name != NULL)
-		free(server->name);
-	
-	free(server);
-	
-	return EOK;
-}
-
-/** Register service
- *
- */
-static void loc_service_register(ipc_callid_t iid, ipc_call_t *icall,
-    loc_server_t *server)
-{
-	if (server == NULL) {
-		async_answer_0(iid, EREFUSED);
-		return;
-	}
-	
-	/* Create new service entry */
-	loc_service_t *service =
-	    (loc_service_t *) malloc(sizeof(loc_service_t));
-	if (service == NULL) {
-		async_answer_0(iid, ENOMEM);
-		return;
-	}
-	
-	/* Set the interface, if any. */
-	service->forward_interface = IPC_GET_ARG1(*icall);
-
-	/* Get fqsn */
-	char *fqsn;
-	int rc = async_data_write_accept((void **) &fqsn, true, 0,
-	    LOC_NAME_MAXLEN, 0, NULL);
-	if (rc != EOK) {
-		free(service);
-		async_answer_0(iid, rc);
-		return;
-	}
-	
-	char *ns_name;
-	if (!loc_fqsn_split(fqsn, &ns_name, &service->name)) {
-		free(fqsn);
-		free(service);
-		async_answer_0(iid, EINVAL);
-		return;
-	}
-	
-	free(fqsn);
-	
-	fibril_mutex_lock(&services_list_mutex);
-	
-	loc_namespace_t *namespace = loc_namespace_create(ns_name);
-	free(ns_name);
-	if (namespace == NULL) {
-		fibril_mutex_unlock(&services_list_mutex);
-		free(service->name);
-		free(service);
-		async_answer_0(iid, ENOMEM);
-		return;
-	}
-	
-	link_initialize(&service->services);
-	link_initialize(&service->server_services);
-	list_initialize(&service->cat_memb);
-	
-	/* Check that service is not already registered */
-	if (loc_service_find_name(namespace->name, service->name) != NULL) {
-		printf("%s: Service '%s/%s' already registered\n", NAME,
-		    namespace->name, service->name);
-		loc_namespace_destroy(namespace);
-		fibril_mutex_unlock(&services_list_mutex);
-		free(service->name);
-		free(service);
-		async_answer_0(iid, EEXISTS);
-		return;
-	}
-	
-	/* Get unique service ID */
-	service->id = loc_create_id();
-
-	loc_namespace_addref(namespace, service);
-	service->server = server;
-	
-	/* Insert service into list of all services  */
-	list_append(&service->services, &services_list);
-	
-	/* Insert service into list of services supplied by one server */
-	fibril_mutex_lock(&service->server->services_mutex);
-	
-	list_append(&service->server_services, &service->server->services);
-	
-	fibril_mutex_unlock(&service->server->services_mutex);
-	fibril_condvar_broadcast(&services_list_cv);
-	fibril_mutex_unlock(&services_list_mutex);
-	
-	async_answer_1(iid, EOK, service->id);
-}
-
-/**
- *
- */
-static void loc_service_unregister(ipc_callid_t iid, ipc_call_t *icall, 
-    loc_server_t *server)
-{
-	loc_service_t *svc;
-	
-	fibril_mutex_lock(&services_list_mutex);
-	svc = loc_service_find_id(IPC_GET_ARG1(*icall));
-	if (svc == NULL) {
-		fibril_mutex_unlock(&services_list_mutex);
-		async_answer_0(iid, ENOENT);
-		return;
-	}
-	
-	fibril_mutex_lock(&cdir.mutex);
-	loc_service_unregister_core(svc);
-	fibril_mutex_unlock(&cdir.mutex);
-	fibril_mutex_unlock(&services_list_mutex);
-	async_answer_0(iid, EOK);
-}
-
-static void loc_category_get_name(ipc_callid_t iid, ipc_call_t *icall)
-{
-	ipc_callid_t callid;
-	size_t size;
-	size_t act_size;
-	category_t *cat;
-	
-	if (!async_data_read_receive(&callid, &size)) {
-		async_answer_0(callid, EREFUSED);
-		async_answer_0(iid, EREFUSED);
-		return;
-	}
-	
-	fibril_mutex_lock(&cdir.mutex);
-	
-	cat = category_get(&cdir, IPC_GET_ARG1(*icall));
-	if (cat == NULL) {
-		fibril_mutex_unlock(&cdir.mutex);
-		async_answer_0(callid, ENOENT);
-		async_answer_0(iid, ENOENT);
-		return;
-	}
-	
-	act_size = str_size(cat->name);
-	if (act_size > size) {
-		fibril_mutex_unlock(&cdir.mutex);
-		async_answer_0(callid, EOVERFLOW);
-		async_answer_0(iid, EOVERFLOW);
-		return;
-	}
-	
-	sysarg_t retval = async_data_read_finalize(callid, cat->name,
-	    min(size, act_size));
-	
-	fibril_mutex_unlock(&cdir.mutex);
-	
-	async_answer_0(iid, retval);
-}
-
-static void loc_service_get_name(ipc_callid_t iid, ipc_call_t *icall)
-{
-	ipc_callid_t callid;
-	size_t size;
-	size_t act_size;
-	loc_service_t *svc;
-	char *fqn;
-	
-	if (!async_data_read_receive(&callid, &size)) {
-		async_answer_0(callid, EREFUSED);
-		async_answer_0(iid, EREFUSED);
-		return;
-	}
-	
-	fibril_mutex_lock(&services_list_mutex);
-	
-	svc = loc_service_find_id(IPC_GET_ARG1(*icall));
-	if (svc == NULL) {
-		fibril_mutex_unlock(&services_list_mutex);
-		async_answer_0(callid, ENOENT);
-		async_answer_0(iid, ENOENT);
-		return;
-	}
-	
-	if (asprintf(&fqn, "%s/%s", svc->namespace->name, svc->name) < 0) {
-		fibril_mutex_unlock(&services_list_mutex);
-		async_answer_0(callid, ENOMEM);
-		async_answer_0(iid, ENOMEM);
-		return;
-	}
-	
-	act_size = str_size(fqn);
-	if (act_size > size) {
-		free(fqn);
-		fibril_mutex_unlock(&services_list_mutex);
-		async_answer_0(callid, EOVERFLOW);
-		async_answer_0(iid, EOVERFLOW);
-		return;
-	}
-	
-	sysarg_t retval = async_data_read_finalize(callid, fqn,
-	    min(size, act_size));
-	free(fqn);
-	
-	fibril_mutex_unlock(&services_list_mutex);
-	
-	async_answer_0(iid, retval);
-}
-
-/** Connect client to the service.
- *
- * Find server supplying requested service and forward
- * the message to it.
- *
- */
-static void loc_forward(ipc_callid_t callid, ipc_call_t *call)
-{
-	fibril_mutex_lock(&services_list_mutex);
-	
-	/*
-	 * Get ID from request
-	 */
-	service_id_t id = IPC_GET_ARG2(*call);
-	loc_service_t *svc = loc_service_find_id(id);
-	
-	if ((svc == NULL) || (svc->server == NULL) || (!svc->server->sess)) {
-		fibril_mutex_unlock(&services_list_mutex);
-		async_answer_0(callid, ENOENT);
-		return;
-	}
-	
-	async_exch_t *exch = async_exchange_begin(svc->server->sess);
-	
-	if (svc->forward_interface == 0)
-		async_forward_fast(callid, exch, svc->id, 0, 0, IPC_FF_NONE);
-	else
-		async_forward_fast(callid, exch, svc->forward_interface,
-		    svc->id, 0, IPC_FF_NONE);
-	
-	async_exchange_end(exch);
-	
-	fibril_mutex_unlock(&services_list_mutex);
-}
-
-/** Find ID for service identified by name.
- *
- * In answer will be send EOK and service ID in arg1 or a error
- * code from errno.h.
- *
- */
-static void loc_service_get_id(ipc_callid_t iid, ipc_call_t *icall)
-{
-	char *fqsn;
-	
-	/* Get fqsn */
-	int rc = async_data_write_accept((void **) &fqsn, true, 0,
-	    LOC_NAME_MAXLEN, 0, NULL);
-	if (rc != EOK) {
-		async_answer_0(iid, rc);
-		return;
-	}
-	
-	char *ns_name;
-	char *name;
-	if (!loc_fqsn_split(fqsn, &ns_name, &name)) {
-		free(fqsn);
-		async_answer_0(iid, EINVAL);
-		return;
-	}
-	
-	free(fqsn);
-	
-	fibril_mutex_lock(&services_list_mutex);
-	const loc_service_t *svc;
-	
-recheck:
-	
-	/*
-	 * Find service name in the list of known services.
-	 */
-	svc = loc_service_find_name(ns_name, name);
-	
-	/*
-	 * Device was not found.
-	 */
-	if (svc == NULL) {
-		if (IPC_GET_ARG1(*icall) & IPC_FLAG_BLOCKING) {
-			/* Blocking lookup */
-			fibril_condvar_wait(&services_list_cv,
-			    &services_list_mutex);
-			goto recheck;
-		}
-		
-		async_answer_0(iid, ENOENT);
-		free(ns_name);
-		free(name);
-		fibril_mutex_unlock(&services_list_mutex);
-		return;
-	}
-	
-	async_answer_1(iid, EOK, svc->id);
-	
-	fibril_mutex_unlock(&services_list_mutex);
-	free(ns_name);
-	free(name);
-}
-
-/** Find ID for namespace identified by name.
- *
- * In answer will be send EOK and service ID in arg1 or a error
- * code from errno.h.
- *
- */
-static void loc_namespace_get_id(ipc_callid_t iid, ipc_call_t *icall)
-{
-	char *name;
-	
-	/* Get service name */
-	int rc = async_data_write_accept((void **) &name, true, 0,
-	    LOC_NAME_MAXLEN, 0, NULL);
-	if (rc != EOK) {
-		async_answer_0(iid, rc);
-		return;
-	}
-	
-	fibril_mutex_lock(&services_list_mutex);
-	const loc_namespace_t *namespace;
-	
-recheck:
-	
-	/*
-	 * Find namespace name in the list of known namespaces.
-	 */
-	namespace = loc_namespace_find_name(name);
-	
-	/*
-	 * Namespace was not found.
-	 */
-	if (namespace == NULL) {
-		if (IPC_GET_ARG1(*icall) & IPC_FLAG_BLOCKING) {
-			/* Blocking lookup */
-			fibril_condvar_wait(&services_list_cv,
-			    &services_list_mutex);
-			goto recheck;
-		}
-		
-		async_answer_0(iid, ENOENT);
-		free(name);
-		fibril_mutex_unlock(&services_list_mutex);
-		return;
-	}
-	
-	async_answer_1(iid, EOK, namespace->id);
-	
-	fibril_mutex_unlock(&services_list_mutex);
-	free(name);
-}
-
-/** Create callback connection.
- *
- * Create callback connection which will be used to send category change
- * events.
- *
- * On success, answer will contain EOK int retval.
- * On failure, error code will be sent in retval.
- *
- */
-static void loc_callback_create(ipc_callid_t iid, ipc_call_t *icall)
-{
-	cb_sess_t *cb_sess = calloc(1, sizeof(cb_sess_t));
-	if (cb_sess == NULL) {
-		async_answer_0(iid, ENOMEM);
-		return;
-	}
-	
-	async_sess_t *sess = async_callback_receive(EXCHANGE_SERIALIZE);
-	if (sess == NULL) {
-		free(cb_sess);
-		async_answer_0(iid, ENOMEM);
-		return;
-	}
-	
-	cb_sess->sess = sess;
-	link_initialize(&cb_sess->cb_sess_list);
-	
-	fibril_mutex_lock(&callback_sess_mutex);
-	list_append(&cb_sess->cb_sess_list, &callback_sess_list);
-	fibril_mutex_unlock(&callback_sess_mutex);
-	
-	async_answer_0(iid, EOK);
-}
-
-void loc_category_change_event(void)
-{
-	fibril_mutex_lock(&callback_sess_mutex);
-	
-	list_foreach(callback_sess_list, link) {
-		cb_sess_t *cb_sess;
-		
-		cb_sess = list_get_instance(link, cb_sess_t, cb_sess_list);
-		
-		async_exch_t *exch = async_exchange_begin(cb_sess->sess);
-		async_msg_0(exch, LOC_EVENT_CAT_CHANGE);
-		async_exchange_end(exch);
-	}
-	
-	fibril_mutex_unlock(&callback_sess_mutex);
-}
-
-/** Find ID for category specified by name.
- *
- * On success, answer will contain EOK int retval and service ID in arg1.
- * On failure, error code will be sent in retval.
- *
- */
-static void loc_category_get_id(ipc_callid_t iid, ipc_call_t *icall)
-{
-	char *name;
-	category_t *cat;
-	
-	/* Get service name */
-	int rc = async_data_write_accept((void **) &name, true, 0,
-	    LOC_NAME_MAXLEN, 0, NULL);
-	if (rc != EOK) {
-		async_answer_0(iid, rc);
-		return;
-	}
-	
-	fibril_mutex_lock(&cdir.mutex);
-
-	cat = category_find_by_name(&cdir, name);
-	if (cat == NULL) {
-		/* Category not found */
-		async_answer_0(iid, ENOENT);
-		goto cleanup;
-	}
-	
-	async_answer_1(iid, EOK, cat->id);
-cleanup:
-	fibril_mutex_unlock(&cdir.mutex);
-	free(name);
-}
-
-static void loc_id_probe(ipc_callid_t iid, ipc_call_t *icall)
-{
-	fibril_mutex_lock(&services_list_mutex);
-	
-	loc_namespace_t *namespace =
-	    loc_namespace_find_id(IPC_GET_ARG1(*icall));
-	if (namespace == NULL) {
-		loc_service_t *svc =
-		    loc_service_find_id(IPC_GET_ARG1(*icall));
-		if (svc == NULL)
-			async_answer_1(iid, EOK, LOC_OBJECT_NONE);
-		else
-			async_answer_1(iid, EOK, LOC_OBJECT_SERVICE);
-	} else
-		async_answer_1(iid, EOK, LOC_OBJECT_NAMESPACE);
-	
-	fibril_mutex_unlock(&services_list_mutex);
-}
-
-static void loc_get_namespace_count(ipc_callid_t iid, ipc_call_t *icall)
-{
-	fibril_mutex_lock(&services_list_mutex);
-	async_answer_1(iid, EOK, list_count(&namespaces_list));
-	fibril_mutex_unlock(&services_list_mutex);
-}
-
-static void loc_get_service_count(ipc_callid_t iid, ipc_call_t *icall)
-{
-	fibril_mutex_lock(&services_list_mutex);
-	
-	loc_namespace_t *namespace =
-	    loc_namespace_find_id(IPC_GET_ARG1(*icall));
-	if (namespace == NULL)
-		async_answer_0(iid, EEXISTS);
-	else
-		async_answer_1(iid, EOK, namespace->refcnt);
-	
-	fibril_mutex_unlock(&services_list_mutex);
-}
-
-static void loc_get_categories(ipc_callid_t iid, ipc_call_t *icall)
-{
-	ipc_callid_t callid;
-	size_t size;
-	size_t act_size;
-	int rc;
-	
-	if (!async_data_read_receive(&callid, &size)) {
-		async_answer_0(callid, EREFUSED);
-		async_answer_0(iid, EREFUSED);
-		return;
-	}
-	
-	category_id_t *id_buf = (category_id_t *) malloc(size);
-	if (id_buf == NULL) {
-		fibril_mutex_unlock(&cdir.mutex);
-		async_answer_0(callid, ENOMEM);
-		async_answer_0(iid, ENOMEM);
-		return;
-	}
-	
-	fibril_mutex_lock(&cdir.mutex);
-	
-	rc = categ_dir_get_categories(&cdir, id_buf, size, &act_size);
-	if (rc != EOK) {
-		fibril_mutex_unlock(&cdir.mutex);
-		async_answer_0(callid, rc);
-		async_answer_0(iid, rc);
-		return;
-	}
-	
-	fibril_mutex_unlock(&cdir.mutex);
-	
-	sysarg_t retval = async_data_read_finalize(callid, id_buf, size);
-	free(id_buf);
-	
-	async_answer_1(iid, retval, act_size);
-}
-
-static void loc_get_namespaces(ipc_callid_t iid, ipc_call_t *icall)
-{
-	ipc_callid_t callid;
-	size_t size;
-	if (!async_data_read_receive(&callid, &size)) {
-		async_answer_0(callid, EREFUSED);
-		async_answer_0(iid, EREFUSED);
-		return;
-	}
-	
-	if ((size % sizeof(loc_sdesc_t)) != 0) {
-		async_answer_0(callid, EINVAL);
-		async_answer_0(iid, EINVAL);
-		return;
-	}
-	
-	fibril_mutex_lock(&services_list_mutex);
-	
-	size_t count = size / sizeof(loc_sdesc_t);
-	if (count != list_count(&namespaces_list)) {
-		fibril_mutex_unlock(&services_list_mutex);
-		async_answer_0(callid, EOVERFLOW);
-		async_answer_0(iid, EOVERFLOW);
-		return;
-	}
-	
-	loc_sdesc_t *desc = (loc_sdesc_t *) malloc(size);
-	if (desc == NULL) {
-		fibril_mutex_unlock(&services_list_mutex);
-		async_answer_0(callid, ENOMEM);
-		async_answer_0(iid, ENOMEM);
-		return;
-	}
-	
-	size_t pos = 0;
-	list_foreach(namespaces_list, item) {
-		loc_namespace_t *namespace =
-		    list_get_instance(item, loc_namespace_t, namespaces);
-		
-		desc[pos].id = namespace->id;
-		str_cpy(desc[pos].name, LOC_NAME_MAXLEN, namespace->name);
-		pos++;
-	}
-	
-	sysarg_t retval = async_data_read_finalize(callid, desc, size);
-	
-	free(desc);
-	fibril_mutex_unlock(&services_list_mutex);
-	
-	async_answer_0(iid, retval);
-}
-
-static void loc_get_services(ipc_callid_t iid, ipc_call_t *icall)
-{
-	/* FIXME: Use faster algorithm which can make better use
-	   of namespaces */
-	
-	ipc_callid_t callid;
-	size_t size;
-	if (!async_data_read_receive(&callid, &size)) {
-		async_answer_0(callid, EREFUSED);
-		async_answer_0(iid, EREFUSED);
-		return;
-	}
-	
-	if ((size % sizeof(loc_sdesc_t)) != 0) {
-		async_answer_0(callid, EINVAL);
-		async_answer_0(iid, EINVAL);
-		return;
-	}
-	
-	fibril_mutex_lock(&services_list_mutex);
-	
-	loc_namespace_t *namespace =
-	    loc_namespace_find_id(IPC_GET_ARG1(*icall));
-	if (namespace == NULL) {
-		fibril_mutex_unlock(&services_list_mutex);
-		async_answer_0(callid, ENOENT);
-		async_answer_0(iid, ENOENT);
-		return;
-	}
-	
-	size_t count = size / sizeof(loc_sdesc_t);
-	if (count != namespace->refcnt) {
-		fibril_mutex_unlock(&services_list_mutex);
-		async_answer_0(callid, EOVERFLOW);
-		async_answer_0(iid, EOVERFLOW);
-		return;
-	}
-	
-	loc_sdesc_t *desc = (loc_sdesc_t *) malloc(size);
-	if (desc == NULL) {
-		fibril_mutex_unlock(&services_list_mutex);
-		async_answer_0(callid, ENOMEM);
-		async_answer_0(iid, EREFUSED);
-		return;
-	}
-	
-	size_t pos = 0;
-	list_foreach(services_list, item) {
-		loc_service_t *service =
-		    list_get_instance(item, loc_service_t, services);
-		
-		if (service->namespace == namespace) {
-			desc[pos].id = service->id;
-			str_cpy(desc[pos].name, LOC_NAME_MAXLEN, service->name);
-			pos++;
-		}
-	}
-	
-	sysarg_t retval = async_data_read_finalize(callid, desc, size);
-	
-	free(desc);
-	fibril_mutex_unlock(&services_list_mutex);
-	
-	async_answer_0(iid, retval);
-}
-
-static void loc_category_get_svcs(ipc_callid_t iid, ipc_call_t *icall)
-{
-	ipc_callid_t callid;
-	size_t size;
-	size_t act_size;
-	int rc;
-	
-	if (!async_data_read_receive(&callid, &size)) {
-		async_answer_0(callid, EREFUSED);
-		async_answer_0(iid, EREFUSED);
-		return;
-	}
-	
-	fibril_mutex_lock(&cdir.mutex);
-	
-	category_t *cat = category_get(&cdir, IPC_GET_ARG1(*icall));
-	if (cat == NULL) {
-		fibril_mutex_unlock(&cdir.mutex);
-		async_answer_0(callid, ENOENT);
-		async_answer_0(iid, ENOENT);
-		return;
-	}
-	
-	category_id_t *id_buf = (category_id_t *) malloc(size);
-	if (id_buf == NULL) {
-		fibril_mutex_unlock(&cdir.mutex);
-		async_answer_0(callid, ENOMEM);
-		async_answer_0(iid, ENOMEM);
-		return;
-	}
-	
-	fibril_mutex_lock(&cat->mutex);
-	
-	rc = category_get_services(cat, id_buf, size, &act_size);
-	if (rc != EOK) {
-		fibril_mutex_unlock(&cat->mutex);
-		fibril_mutex_unlock(&cdir.mutex);
-		async_answer_0(callid, rc);
-		async_answer_0(iid, rc);
-		return;
-	}
-	
-	fibril_mutex_unlock(&cat->mutex);
-	fibril_mutex_unlock(&cdir.mutex);
-	
-	sysarg_t retval = async_data_read_finalize(callid, id_buf, size);
-	free(id_buf);
-	
-	async_answer_1(iid, retval, act_size);
-}
-
-
-static void loc_null_create(ipc_callid_t iid, ipc_call_t *icall)
-{
-	fibril_mutex_lock(&null_services_mutex);
-	
-	unsigned int i;
-	bool fnd = false;
-	
-	for (i = 0; i < NULL_SERVICES; i++) {
-		if (null_services[i] == NULL) {
-			fnd = true;
-			break;
-		}
-	}
-	
-	if (!fnd) {
-		fibril_mutex_unlock(&null_services_mutex);
-		async_answer_0(iid, ENOMEM);
-		return;
-	}
-	
-	char null[LOC_NAME_MAXLEN];
-	snprintf(null, LOC_NAME_MAXLEN, "%u", i);
-	
-	char *dev_name = str_dup(null);
-	if (dev_name == NULL) {
-		fibril_mutex_unlock(&null_services_mutex);
-		async_answer_0(iid, ENOMEM);
-		return;
-	}
-	
-	loc_service_t *service =
-	    (loc_service_t *) malloc(sizeof(loc_service_t));
-	if (service == NULL) {
-		fibril_mutex_unlock(&null_services_mutex);
-		async_answer_0(iid, ENOMEM);
-		return;
-	}
-	
-	fibril_mutex_lock(&services_list_mutex);
-	
-	loc_namespace_t *namespace = loc_namespace_create("null");
-	if (namespace == NULL) {
-		fibril_mutex_lock(&services_list_mutex);
-		fibril_mutex_unlock(&null_services_mutex);
-		async_answer_0(iid, ENOMEM);
-		return;
-	}
-	
-	link_initialize(&service->services);
-	link_initialize(&service->server_services);
-	list_initialize(&service->cat_memb);
-	
-	/* Get unique service ID */
-	service->id = loc_create_id();
-	service->server = NULL;
-	
-	loc_namespace_addref(namespace, service);
-	service->name = dev_name;
-	
-	/*
-	 * Insert service into list of all services and into null services array.
-	 * Insert service into a dummy list of null server's services so that it
-	 * can be safely removed later.
-	 */
-	list_append(&service->services, &services_list);
-	list_append(&service->server_services, &dummy_null_services);
-	null_services[i] = service;
-	
-	fibril_mutex_unlock(&services_list_mutex);
-	fibril_mutex_unlock(&null_services_mutex);
-	
-	async_answer_1(iid, EOK, (sysarg_t) i);
-}
-
-static void loc_null_destroy(ipc_callid_t iid, ipc_call_t *icall)
-{
-	sysarg_t i = IPC_GET_ARG1(*icall);
-	if (i >= NULL_SERVICES) {
-		async_answer_0(iid, ELIMIT);
-		return;
-	}
-	
-	fibril_mutex_lock(&null_services_mutex);
-	
-	if (null_services[i] == NULL) {
-		fibril_mutex_unlock(&null_services_mutex);
-		async_answer_0(iid, ENOENT);
-		return;
-	}
-	
-	fibril_mutex_lock(&services_list_mutex);
-	fibril_mutex_lock(&cdir.mutex);
-	loc_service_unregister_core(null_services[i]);
-	fibril_mutex_unlock(&cdir.mutex);
-	fibril_mutex_unlock(&services_list_mutex);
-	
-	null_services[i] = NULL;
-	
-	fibril_mutex_unlock(&null_services_mutex);
-	async_answer_0(iid, EOK);
-}
-
-static void loc_service_add_to_cat(ipc_callid_t iid, ipc_call_t *icall)
-{
-	category_t *cat;
-	loc_service_t *svc;
-	catid_t cat_id;
-	service_id_t svc_id;
-	sysarg_t retval;
-	
-	svc_id = IPC_GET_ARG1(*icall);
-	cat_id = IPC_GET_ARG2(*icall);
-	
-	fibril_mutex_lock(&services_list_mutex);
-	fibril_mutex_lock(&cdir.mutex);
-	
-	cat = category_get(&cdir, cat_id);
-	svc = loc_service_find_id(svc_id);
-	
-	if (cat == NULL || svc == NULL) {
-		fibril_mutex_unlock(&cdir.mutex);
-		fibril_mutex_unlock(&services_list_mutex);
-		async_answer_0(iid, ENOENT);
-		return;
-	}
-	
-	fibril_mutex_lock(&cat->mutex);
-	retval = category_add_service(cat, svc);
-
-	fibril_mutex_unlock(&cat->mutex);
-	fibril_mutex_unlock(&cdir.mutex);
-	fibril_mutex_unlock(&services_list_mutex);
-
-	async_answer_0(iid, retval);
-
-	loc_category_change_event();
-}
-
-
-/** Initialize location service.
- *
- *
- */
-static bool loc_init(void)
-{
-	unsigned int i;
-	category_t *cat;
-
-	for (i = 0; i < NULL_SERVICES; i++)
-		null_services[i] = NULL;
-	
-	categ_dir_init(&cdir);
-
-	cat = category_new("bd");
-	categ_dir_add_cat(&cdir, cat);
-
-	cat = category_new("iplink");
-	categ_dir_add_cat(&cdir, cat);
-
-	cat = category_new("keyboard");
-	categ_dir_add_cat(&cdir, cat);
-
-	cat = category_new("mouse");
-	categ_dir_add_cat(&cdir, cat);
-
-	cat = category_new("serial");
-	categ_dir_add_cat(&cdir, cat);
-
-	cat = category_new("test3");
-	categ_dir_add_cat(&cdir, cat);
-
-	cat = category_new("usbhc");
-	categ_dir_add_cat(&cdir, cat);
-
-	cat = category_new("virt-null");
-	categ_dir_add_cat(&cdir, cat);
-
-	cat = category_new("virtual");
-	categ_dir_add_cat(&cdir, cat);
-	
-	cat = category_new("nic");
-	categ_dir_add_cat(&cdir, cat);
-	
-	return true;
-}
-
-/** Handle connection on supplier port.
- *
- */
-static void loc_connection_supplier(ipc_callid_t iid, ipc_call_t *icall)
-{
-	/* Accept connection */
-	async_answer_0(iid, EOK);
-	
-	loc_server_t *server = loc_server_register();
-	if (server == NULL)
-		return;
-	
-	while (true) {
-		ipc_call_t call;
-		ipc_callid_t callid = async_get_call(&call);
-		
-		if (!IPC_GET_IMETHOD(call))
-			break;
-		
-		switch (IPC_GET_IMETHOD(call)) {
-		case LOC_SERVER_UNREGISTER:
-			if (server == NULL)
-				async_answer_0(callid, ENOENT);
-			else
-				async_answer_0(callid, EOK);
-			break;
-		case LOC_SERVICE_ADD_TO_CAT:
-			/* Add service to category */
-			loc_service_add_to_cat(callid, &call);
-			break;
-		case LOC_SERVICE_REGISTER:
-			/* Register one service */
-			loc_service_register(callid, &call, server);
-			break;
-		case LOC_SERVICE_UNREGISTER:
-			/* Remove one service */
-			loc_service_unregister(callid, &call, server);
-			break;
-		case LOC_SERVICE_GET_ID:
-			loc_service_get_id(callid, &call);
-			break;
-		case LOC_NAMESPACE_GET_ID:
-			loc_namespace_get_id(callid, &call);
-			break;
-		default:
-			async_answer_0(callid, ENOENT);
-		}
-	}
-	
-	if (server != NULL) {
-		/*
-		 * Unregister the server and all its services.
-		 */
-		loc_server_unregister(server);
-		server = NULL;
-	}
-}
-
-/** Handle connection on consumer port.
- *
- */
-static void loc_connection_consumer(ipc_callid_t iid, ipc_call_t *icall)
-{
-	/* Accept connection */
-	async_answer_0(iid, EOK);
-	
-	while (true) {
-		ipc_call_t call;
-		ipc_callid_t callid = async_get_call(&call);
-		
-		if (!IPC_GET_IMETHOD(call))
-			break;
-		
-		switch (IPC_GET_IMETHOD(call)) {
-		case LOC_SERVICE_GET_ID:
-			loc_service_get_id(callid, &call);
-			break;
-		case LOC_SERVICE_GET_NAME:
-			loc_service_get_name(callid, &call);
-			break;
-		case LOC_NAMESPACE_GET_ID:
-			loc_namespace_get_id(callid, &call);
-			break;
-		case LOC_CALLBACK_CREATE:
-			loc_callback_create(callid, &call);
-			break;
-		case LOC_CATEGORY_GET_ID:
-			loc_category_get_id(callid, &call);
-			break;
-		case LOC_CATEGORY_GET_NAME:
-			loc_category_get_name(callid, &call);
-			break;
-		case LOC_CATEGORY_GET_SVCS:
-			loc_category_get_svcs(callid, &call);
-			break;
-		case LOC_ID_PROBE:
-			loc_id_probe(callid, &call);
-			break;
-		case LOC_NULL_CREATE:
-			loc_null_create(callid, &call);
-			break;
-		case LOC_NULL_DESTROY:
-			loc_null_destroy(callid, &call);
-			break;
-		case LOC_GET_NAMESPACE_COUNT:
-			loc_get_namespace_count(callid, &call);
-			break;
-		case LOC_GET_SERVICE_COUNT:
-			loc_get_service_count(callid, &call);
-			break;
-		case LOC_GET_CATEGORIES:
-			loc_get_categories(callid, &call);
-			break;
-		case LOC_GET_NAMESPACES:
-			loc_get_namespaces(callid, &call);
-			break;
-		case LOC_GET_SERVICES:
-			loc_get_services(callid, &call);
-			break;
-		default:
-			async_answer_0(callid, ENOENT);
-		}
-	}
-}
-
-/** Function for handling connections to location service
- *
- */
-static void loc_connection(ipc_callid_t iid, ipc_call_t *icall, void *arg)
-{
-	/* Select interface */
-	switch ((sysarg_t) (IPC_GET_ARG1(*icall))) {
-	case LOC_PORT_SUPPLIER:
-		loc_connection_supplier(iid, icall);
-		break;
-	case LOC_PORT_CONSUMER:
-		loc_connection_consumer(iid, icall);
-		break;
-	case LOC_CONNECT_TO_SERVICE:
-		/* Connect client to selected service */
-		loc_forward(iid, icall);
-		break;
-	default:
-		/* No such interface */
-		async_answer_0(iid, ENOENT);
-	}
-}
-
-/**
- *
- */
-int main(int argc, char *argv[])
-{
-	printf("%s: HelenOS Location Service\n", NAME);
-	
-	if (!loc_init()) {
-		printf("%s: Error while initializing service\n", NAME);
-		return -1;
-	}
-	
-	/* Set a handler of incomming connections */
-	async_set_client_connection(loc_connection);
-	
-	/* Register location service at naming service */
-	if (service_register(SERVICE_LOC) != EOK)
-		return -1;
-	
-	printf("%s: Accepting connections\n", NAME);
-	async_manager();
-	
-	/* Never reached */
-	return 0;
-}
-
-/**
- * @}
- */
Index: pace/srv/loc/loc.h
===================================================================
--- uspace/srv/loc/loc.h	(revision 6df220289417a3a5f0a319426564833d6f527370)
+++ 	(revision )
@@ -1,114 +1,0 @@
-/*
- * Copyright (c) 2011 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 loc
- * @{
- */
-/** @file HelenOS location service.
- */
-
-#ifndef LOC_H_
-#define LOC_H_
-
-#include <ipc/loc.h>
-#include <async.h>
-#include <fibril_synch.h>
-#include <sys/types.h>
-
-/** Representation of server (supplier).
- *
- * Each server supplies a set of services.
- *
- */
-typedef struct {
-	/** Link to servers_list */
-	link_t servers;
-	
-	/** List of services supplied by this server */
-	list_t services;
-	
-	/** Session asociated with this server */
-	async_sess_t *sess;
-	
-	/** Server name */
-	char *name;
-	
-	/** Fibril mutex for list of services owned by this server */
-	fibril_mutex_t services_mutex;
-} loc_server_t;
-
-/** Info about registered namespaces
- *
- */
-typedef struct {
-	/** Link to namespaces_list */
-	link_t namespaces;
-	
-	/** Unique namespace identifier */
-	service_id_t id;
-	
-	/** Namespace name */
-	char *name;
-	
-	/** Reference count */
-	size_t refcnt;
-} loc_namespace_t;
-
-/** Info about registered service
- *
- */
-typedef struct {
-	/** Link to global list of services (services_list) */
-	link_t services;
-	/** Link to server list of services (loc_server_t.services) */
-	link_t server_services;
-	/** Link to list of services in category (category_t.services) */
-	link_t cat_services;
-	/** List of category memberships (svc_categ_t) */
-	list_t cat_memb;
-	/** Unique service identifier */
-	service_id_t id;
-	/** Service namespace */
-	loc_namespace_t *namespace;
-	/** Service name */
-	char *name;
-	/** Supplier of this service */
-	loc_server_t *server;
-	/** Use this interface when forwarding to server. */
-	sysarg_t forward_interface;
-} loc_service_t;
-
-extern fibril_mutex_t services_list_mutex;
-
-extern service_id_t loc_create_id(void);
-extern void loc_category_change_event(void);
-
-#endif
-
-/** @}
- */
Index: uspace/srv/locsrv/Makefile
===================================================================
--- uspace/srv/locsrv/Makefile	(revision ce1836ed04784ea0bacc387ac80bfe852f36119b)
+++ uspace/srv/locsrv/Makefile	(revision ce1836ed04784ea0bacc387ac80bfe852f36119b)
@@ -0,0 +1,38 @@
+#
+# Copyright (c) 2005 Martin Decky
+# Copyright (c) 2007 Jakub Jermar
+# 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.
+#
+
+USPACE_PREFIX = ../..
+BINARY = locsrv
+STATIC_NEEDED = y
+
+SOURCES = \
+	category.c \
+	locsrv.c
+
+include $(USPACE_PREFIX)/Makefile.common
Index: uspace/srv/locsrv/category.c
===================================================================
--- uspace/srv/locsrv/category.c	(revision ce1836ed04784ea0bacc387ac80bfe852f36119b)
+++ uspace/srv/locsrv/category.c	(revision ce1836ed04784ea0bacc387ac80bfe852f36119b)
@@ -0,0 +1,213 @@
+/*
+ * Copyright (c) 2011 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 loc
+ * @{
+ */
+/** @file Categories for location service.
+ */
+
+#include <adt/list.h>
+#include <errno.h>
+#include <fibril_synch.h>
+#include <stdlib.h>
+#include <str.h>
+
+#include "category.h"
+#include "locsrv.h"
+
+/** Initialize category directory. */
+void categ_dir_init(categ_dir_t *cdir)
+{
+	fibril_mutex_initialize(&cdir->mutex);
+	list_initialize(&cdir->categories);
+}
+
+/** Add new category to directory. */
+void categ_dir_add_cat(categ_dir_t *cdir, category_t *cat)
+{
+	list_append(&cat->cat_list, &cdir->categories);
+}
+
+/** Get list of categories. */
+int categ_dir_get_categories(categ_dir_t *cdir, category_id_t *id_buf,
+    size_t buf_size, size_t *act_size)
+{
+	size_t act_cnt;
+	size_t buf_cnt;
+
+	assert(fibril_mutex_is_locked(&cdir->mutex));
+
+	buf_cnt = buf_size / sizeof(category_id_t);
+
+	act_cnt = list_count(&cdir->categories);
+	*act_size = act_cnt * sizeof(category_id_t);
+
+	if (buf_size % sizeof(category_id_t) != 0)
+		return EINVAL;
+
+	size_t pos = 0;
+	list_foreach(cdir->categories, item) {
+		category_t *cat =
+		    list_get_instance(item, category_t, cat_list);
+
+		if (pos < buf_cnt)
+			id_buf[pos] = cat->id;
+		pos++;
+	}
+
+	return EOK;
+}
+
+
+/** Initialize category structure. */
+static void category_init(category_t *cat, const char *name)
+{
+	fibril_mutex_initialize(&cat->mutex);
+	cat->name = str_dup(name);
+	cat->id = loc_create_id();
+	link_initialize(&cat->cat_list);
+	list_initialize(&cat->svc_memb);
+}
+
+/** Allocate new category. */
+category_t *category_new(const char *name)
+{
+	category_t *cat;
+
+	cat = malloc(sizeof(category_t));
+	if (cat == NULL)
+		return NULL;
+
+	category_init(cat, name);
+	return cat;
+}
+
+/** Add service to category. */
+int category_add_service(category_t *cat, loc_service_t *svc)
+{
+	assert(fibril_mutex_is_locked(&cat->mutex));
+	assert(fibril_mutex_is_locked(&services_list_mutex));
+
+	/* Verify that category does not contain this service yet. */
+	list_foreach(cat->svc_memb, item) {
+		svc_categ_t *memb = list_get_instance(item, svc_categ_t,
+		    cat_link);
+		if (memb->svc == svc) {
+			return EEXIST;
+		}
+	}
+
+	svc_categ_t *nmemb = malloc(sizeof(svc_categ_t));
+	if (nmemb == NULL)
+		return ENOMEM;
+
+	nmemb->svc = svc;
+	nmemb->cat = cat;
+
+	list_append(&nmemb->cat_link, &cat->svc_memb);
+	list_append(&nmemb->svc_link, &svc->cat_memb);
+
+	return EOK;
+}
+
+/** Remove service from category. */
+void category_remove_service(svc_categ_t *memb)
+{
+	assert(fibril_mutex_is_locked(&memb->cat->mutex));
+	assert(fibril_mutex_is_locked(&services_list_mutex));
+
+	list_remove(&memb->cat_link);
+	list_remove(&memb->svc_link);
+
+	free(memb);
+}
+
+/** Get category by ID. */
+category_t *category_get(categ_dir_t *cdir, catid_t catid)
+{
+	assert(fibril_mutex_is_locked(&cdir->mutex));
+
+	list_foreach(cdir->categories, item) {
+		category_t *cat = list_get_instance(item, category_t,
+		    cat_list);
+		if (cat->id == catid)
+			return cat;
+	}
+
+	return NULL;
+}
+
+/** Find category by name. */
+category_t *category_find_by_name(categ_dir_t *cdir, const char *name)
+{
+	assert(fibril_mutex_is_locked(&cdir->mutex));
+
+	list_foreach(cdir->categories, item) {
+		category_t *cat = list_get_instance(item, category_t,
+		    cat_list);
+		if (str_cmp(cat->name, name) == 0)
+			return cat;
+	}
+
+	return NULL;
+}
+
+/** Get list of services in category. */
+int category_get_services(category_t *cat, service_id_t *id_buf,
+    size_t buf_size, size_t *act_size)
+{
+	size_t act_cnt;
+	size_t buf_cnt;
+
+	assert(fibril_mutex_is_locked(&cat->mutex));
+
+	buf_cnt = buf_size / sizeof(service_id_t);
+
+	act_cnt = list_count(&cat->svc_memb);
+	*act_size = act_cnt * sizeof(service_id_t);
+
+	if (buf_size % sizeof(service_id_t) != 0)
+		return EINVAL;
+
+	size_t pos = 0;
+	list_foreach(cat->svc_memb, item) {
+		svc_categ_t *memb =
+		    list_get_instance(item, svc_categ_t, cat_link);
+
+		if (pos < buf_cnt)
+			id_buf[pos] = memb->svc->id;
+		pos++;
+	}
+
+	return EOK;
+}
+
+/**
+ * @}
+ */
Index: uspace/srv/locsrv/category.h
===================================================================
--- uspace/srv/locsrv/category.h	(revision ce1836ed04784ea0bacc387ac80bfe852f36119b)
+++ uspace/srv/locsrv/category.h	(revision ce1836ed04784ea0bacc387ac80bfe852f36119b)
@@ -0,0 +1,98 @@
+/*
+ * Copyright (c) 2011 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 loc
+ * @{
+ */
+/** @file Categories for location service.
+ */
+
+#ifndef CATEGORY_H_
+#define CATEGORY_H_
+
+#include <adt/list.h>
+#include "locsrv.h"
+
+typedef sysarg_t catid_t;
+
+/** Service category */
+typedef struct {
+	/** Protects this structure, list of services */
+	fibril_mutex_t mutex;
+
+	/** Identifier */
+	catid_t id;
+
+	/** Category name */
+	const char *name;
+
+	/** Link to list of categories (categ_dir_t.categories) */
+	link_t cat_list;
+
+	/** List of service memberships in this category (svc_categ_t) */
+	list_t svc_memb;
+} category_t;
+
+/** Service directory ogranized by categories (yellow pages) */
+typedef struct {
+	/** Protects this structure, list of categories */
+	fibril_mutex_t mutex;
+	/** List of all categories (category_t) */
+	list_t categories;
+} categ_dir_t;
+
+/** Service in category membership. */
+typedef struct {
+	/** Link to category_t.svc_memb list */
+	link_t cat_link;
+	/** Link to loc_service_t.cat_memb list */
+	link_t svc_link;
+	
+	/** Category */
+	category_t *cat;
+	/** Service */
+	loc_service_t *svc;
+} svc_categ_t;
+
+extern void categ_dir_init(categ_dir_t *);
+extern void categ_dir_add_cat(categ_dir_t *, category_t *);
+extern int categ_dir_get_categories(categ_dir_t *, service_id_t *, size_t,
+    size_t *);
+extern category_t *category_new(const char *);
+extern int category_add_service(category_t *, loc_service_t *);
+extern void category_remove_service(svc_categ_t *);
+extern category_t *category_get(categ_dir_t *, catid_t);
+extern category_t *category_find_by_name(categ_dir_t *, const char *);
+extern int category_get_services(category_t *, service_id_t *, size_t,
+    size_t *);
+
+
+#endif
+
+/** @}
+ */
Index: uspace/srv/locsrv/locsrv.c
===================================================================
--- uspace/srv/locsrv/locsrv.c	(revision ce1836ed04784ea0bacc387ac80bfe852f36119b)
+++ uspace/srv/locsrv/locsrv.c	(revision ce1836ed04784ea0bacc387ac80bfe852f36119b)
@@ -0,0 +1,1504 @@
+/*
+ * Copyright (c) 2007 Josef Cejka
+ * Copyright (c) 2011 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.
+ */
+
+/**
+ * @defgroup loc Location Service.
+ * @brief HelenOS location service.
+ * @{
+ */
+
+/** @file
+ */
+
+#include <ipc/services.h>
+#include <ns.h>
+#include <async.h>
+#include <stdio.h>
+#include <errno.h>
+#include <bool.h>
+#include <fibril_synch.h>
+#include <macros.h>
+#include <stdlib.h>
+#include <str.h>
+#include <ipc/loc.h>
+#include <assert.h>
+
+#include "category.h"
+#include "locsrv.h"
+
+#define NAME          "loc"
+#define NULL_SERVICES  256
+
+/** Callback session */
+typedef struct {
+	link_t cb_sess_list;
+	async_sess_t *sess;
+} cb_sess_t;
+
+LIST_INITIALIZE(services_list);
+LIST_INITIALIZE(namespaces_list);
+LIST_INITIALIZE(servers_list);
+
+/* Locking order:
+ *  servers_list_mutex
+ *  services_list_mutex
+ *  (loc_server_t *)->services_mutex
+ *  create_id_mutex
+ **/
+
+FIBRIL_MUTEX_INITIALIZE(services_list_mutex);
+static FIBRIL_CONDVAR_INITIALIZE(services_list_cv);
+static FIBRIL_MUTEX_INITIALIZE(servers_list_mutex);
+static FIBRIL_MUTEX_INITIALIZE(create_id_mutex);
+static FIBRIL_MUTEX_INITIALIZE(null_services_mutex);
+
+static service_id_t last_id = 0;
+static loc_service_t *null_services[NULL_SERVICES];
+
+/*
+ * Dummy list for null services. This is necessary so that null services can
+ * be used just as any other services, e.g. in loc_service_unregister_core().
+ */
+static LIST_INITIALIZE(dummy_null_services);
+
+/** Service directory ogranized by categories (yellow pages) */
+static categ_dir_t cdir;
+
+static FIBRIL_MUTEX_INITIALIZE(callback_sess_mutex);
+static LIST_INITIALIZE(callback_sess_list);
+
+service_id_t loc_create_id(void)
+{
+	/* TODO: allow reusing old ids after their unregistration
+	 * and implement some version of LRU algorithm, avoid overflow
+	 */
+	
+	fibril_mutex_lock(&create_id_mutex);
+	last_id++;
+	fibril_mutex_unlock(&create_id_mutex);
+	
+	return last_id;
+}
+
+/** Convert fully qualified service name to namespace and service name.
+ *
+ * A fully qualified service name can be either a plain service name
+ * (then the namespace is considered to be an empty string) or consist
+ * of two components separated by a slash. No more than one slash
+ * is allowed.
+ *
+ */
+static bool loc_fqsn_split(const char *fqsn, char **ns_name, char **name)
+{
+	size_t cnt = 0;
+	size_t slash_offset = 0;
+	size_t slash_after = 0;
+	
+	size_t offset = 0;
+	size_t offset_prev = 0;
+	wchar_t c;
+	
+	while ((c = str_decode(fqsn, &offset, STR_NO_LIMIT)) != 0) {
+		if (c == '/') {
+			cnt++;
+			slash_offset = offset_prev;
+			slash_after = offset;
+		}
+		offset_prev = offset;
+	}
+	
+	/* More than one slash */
+	if (cnt > 1)
+		return false;
+	
+	/* No slash -> namespace is empty */
+	if (cnt == 0) {
+		*ns_name = str_dup("");
+		if (*ns_name == NULL)
+			return false;
+		
+		*name = str_dup(fqsn);
+		if (*name == NULL) {
+			free(*ns_name);
+			return false;
+		}
+		
+		if (str_cmp(*name, "") == 0) {
+			free(*name);
+			free(*ns_name);
+			return false;
+		}
+		
+		return true;
+	}
+	
+	/* Exactly one slash */
+	*ns_name = str_ndup(fqsn, slash_offset);
+	if (*ns_name == NULL)
+		return false;
+	
+	*name = str_dup(fqsn + slash_after);
+	if (*name == NULL) {
+		free(*ns_name);
+		return false;
+	}
+	
+	if (str_cmp(*name, "") == 0) {
+		free(*name);
+		free(*ns_name);
+		return false;
+	}
+	
+	return true;
+}
+
+/** Find namespace with given name. */
+static loc_namespace_t *loc_namespace_find_name(const char *name)
+{
+	assert(fibril_mutex_is_locked(&services_list_mutex));
+	
+	list_foreach(namespaces_list, item) {
+		loc_namespace_t *namespace =
+		    list_get_instance(item, loc_namespace_t, namespaces);
+		if (str_cmp(namespace->name, name) == 0)
+			return namespace;
+	}
+	
+	return NULL;
+}
+
+/** Find namespace with given ID.
+ *
+ * @todo: use hash table
+ *
+ */
+static loc_namespace_t *loc_namespace_find_id(service_id_t id)
+{
+	assert(fibril_mutex_is_locked(&services_list_mutex));
+	
+	list_foreach(namespaces_list, item) {
+		loc_namespace_t *namespace =
+		    list_get_instance(item, loc_namespace_t, namespaces);
+		if (namespace->id == id)
+			return namespace;
+	}
+	
+	return NULL;
+}
+
+/** Find service with given name. */
+static loc_service_t *loc_service_find_name(const char *ns_name,
+    const char *name)
+{
+	assert(fibril_mutex_is_locked(&services_list_mutex));
+	
+	list_foreach(services_list, item) {
+		loc_service_t *service =
+		    list_get_instance(item, loc_service_t, services);
+		if ((str_cmp(service->namespace->name, ns_name) == 0)
+		    && (str_cmp(service->name, name) == 0))
+			return service;
+	}
+	
+	return NULL;
+}
+
+/** Find service with given ID.
+ *
+ * @todo: use hash table
+ *
+ */
+static loc_service_t *loc_service_find_id(service_id_t id)
+{
+	assert(fibril_mutex_is_locked(&services_list_mutex));
+	
+	list_foreach(services_list, item) {
+		loc_service_t *service =
+		    list_get_instance(item, loc_service_t, services);
+		if (service->id == id)
+			return service;
+	}
+	
+	return NULL;
+}
+
+/** Create a namespace (if not already present). */
+static loc_namespace_t *loc_namespace_create(const char *ns_name)
+{
+	loc_namespace_t *namespace;
+	
+	assert(fibril_mutex_is_locked(&services_list_mutex));
+	
+	namespace = loc_namespace_find_name(ns_name);
+	if (namespace != NULL)
+		return namespace;
+	
+	namespace = (loc_namespace_t *) malloc(sizeof(loc_namespace_t));
+	if (namespace == NULL)
+		return NULL;
+	
+	namespace->name = str_dup(ns_name);
+	if (namespace->name == NULL) {
+		free(namespace);
+		return NULL;
+	}
+	
+	namespace->id = loc_create_id();
+	namespace->refcnt = 0;
+	
+	/*
+	 * Insert new namespace into list of registered namespaces
+	 */
+	list_append(&(namespace->namespaces), &namespaces_list);
+	
+	return namespace;
+}
+
+/** Destroy a namespace (if it is no longer needed). */
+static void loc_namespace_destroy(loc_namespace_t *namespace)
+{
+	assert(fibril_mutex_is_locked(&services_list_mutex));
+
+	if (namespace->refcnt == 0) {
+		list_remove(&(namespace->namespaces));
+		
+		free(namespace->name);
+		free(namespace);
+	}
+}
+
+/** Increase namespace reference count by including service. */
+static void loc_namespace_addref(loc_namespace_t *namespace,
+    loc_service_t *service)
+{
+	assert(fibril_mutex_is_locked(&services_list_mutex));
+
+	service->namespace = namespace;
+	namespace->refcnt++;
+}
+
+/** Decrease namespace reference count. */
+static void loc_namespace_delref(loc_namespace_t *namespace)
+{
+	assert(fibril_mutex_is_locked(&services_list_mutex));
+
+	namespace->refcnt--;
+	loc_namespace_destroy(namespace);
+}
+
+/** Unregister service and free it. */
+static void loc_service_unregister_core(loc_service_t *service)
+{
+	assert(fibril_mutex_is_locked(&services_list_mutex));
+	assert(fibril_mutex_is_locked(&cdir.mutex));
+	
+	loc_namespace_delref(service->namespace);
+	list_remove(&(service->services));
+	list_remove(&(service->server_services));
+	
+	/* Remove service from all categories. */
+	while (!list_empty(&service->cat_memb)) {
+		link_t *link = list_first(&service->cat_memb);
+		svc_categ_t *memb = list_get_instance(link, svc_categ_t,
+		    svc_link);
+		fibril_mutex_lock(&memb->cat->mutex);
+		category_remove_service(memb);
+		fibril_mutex_unlock(&memb->cat->mutex);
+	}
+	
+	free(service->name);
+	free(service);
+}
+
+/**
+ * Read info about new server and add it into linked list of registered
+ * servers.
+ */
+static loc_server_t *loc_server_register(void)
+{
+	ipc_call_t icall;
+	ipc_callid_t iid = async_get_call(&icall);
+	
+	if (IPC_GET_IMETHOD(icall) != LOC_SERVER_REGISTER) {
+		async_answer_0(iid, EREFUSED);
+		return NULL;
+	}
+	
+	loc_server_t *server =
+	    (loc_server_t *) malloc(sizeof(loc_server_t));
+	if (server == NULL) {
+		async_answer_0(iid, ENOMEM);
+		return NULL;
+	}
+	
+	/*
+	 * Get server name
+	 */
+	int rc = async_data_write_accept((void **) &server->name, true, 0,
+	    LOC_NAME_MAXLEN, 0, NULL);
+	if (rc != EOK) {
+		free(server);
+		async_answer_0(iid, rc);
+		return NULL;
+	}
+	
+	/*
+	 * Create connection to the server
+	 */
+	server->sess = async_callback_receive(EXCHANGE_SERIALIZE);
+	if (!server->sess) {
+		free(server->name);
+		free(server);
+		async_answer_0(iid, ENOTSUP);
+		return NULL;
+	}
+	
+	/*
+	 * Initialize mutex for list of services
+	 * supplied by this server
+	 */
+	fibril_mutex_initialize(&server->services_mutex);
+	
+	/*
+	 * Initialize list of supplied services
+	 */
+	list_initialize(&server->services);
+
+	link_initialize(&server->servers);
+	
+	fibril_mutex_lock(&servers_list_mutex);
+	
+	/* TODO:
+	 * Check that no server with name equal to
+	 * server->name is registered
+	 */
+	
+	/*
+	 * Insert new server into list of registered servers
+	 */
+	list_append(&(server->servers), &servers_list);
+	fibril_mutex_unlock(&servers_list_mutex);
+	
+	async_answer_0(iid, EOK);
+	
+	return server;
+}
+
+/**
+ * Unregister server, unregister all its services and free server
+ * structure.
+ *
+ */
+static int loc_server_unregister(loc_server_t *server)
+{
+	if (server == NULL)
+		return EEXISTS;
+	
+	fibril_mutex_lock(&servers_list_mutex);
+	
+	if (server->sess)
+		async_hangup(server->sess);
+	
+	/* Remove it from list of servers */
+	list_remove(&(server->servers));
+	
+	/* Unregister all its services */
+	fibril_mutex_lock(&services_list_mutex);
+	fibril_mutex_lock(&server->services_mutex);
+	fibril_mutex_lock(&cdir.mutex);
+	
+	while (!list_empty(&server->services)) {
+		loc_service_t *service = list_get_instance(
+		    list_first(&server->services), loc_service_t,
+		    server_services);
+		loc_service_unregister_core(service);
+	}
+	
+	fibril_mutex_unlock(&cdir.mutex);
+	fibril_mutex_unlock(&server->services_mutex);
+	fibril_mutex_unlock(&services_list_mutex);
+	fibril_mutex_unlock(&servers_list_mutex);
+	
+	/* Free name and server */
+	if (server->name != NULL)
+		free(server->name);
+	
+	free(server);
+	
+	return EOK;
+}
+
+/** Register service
+ *
+ */
+static void loc_service_register(ipc_callid_t iid, ipc_call_t *icall,
+    loc_server_t *server)
+{
+	if (server == NULL) {
+		async_answer_0(iid, EREFUSED);
+		return;
+	}
+	
+	/* Create new service entry */
+	loc_service_t *service =
+	    (loc_service_t *) malloc(sizeof(loc_service_t));
+	if (service == NULL) {
+		async_answer_0(iid, ENOMEM);
+		return;
+	}
+	
+	/* Set the interface, if any. */
+	service->forward_interface = IPC_GET_ARG1(*icall);
+
+	/* Get fqsn */
+	char *fqsn;
+	int rc = async_data_write_accept((void **) &fqsn, true, 0,
+	    LOC_NAME_MAXLEN, 0, NULL);
+	if (rc != EOK) {
+		free(service);
+		async_answer_0(iid, rc);
+		return;
+	}
+	
+	char *ns_name;
+	if (!loc_fqsn_split(fqsn, &ns_name, &service->name)) {
+		free(fqsn);
+		free(service);
+		async_answer_0(iid, EINVAL);
+		return;
+	}
+	
+	free(fqsn);
+	
+	fibril_mutex_lock(&services_list_mutex);
+	
+	loc_namespace_t *namespace = loc_namespace_create(ns_name);
+	free(ns_name);
+	if (namespace == NULL) {
+		fibril_mutex_unlock(&services_list_mutex);
+		free(service->name);
+		free(service);
+		async_answer_0(iid, ENOMEM);
+		return;
+	}
+	
+	link_initialize(&service->services);
+	link_initialize(&service->server_services);
+	list_initialize(&service->cat_memb);
+	
+	/* Check that service is not already registered */
+	if (loc_service_find_name(namespace->name, service->name) != NULL) {
+		printf("%s: Service '%s/%s' already registered\n", NAME,
+		    namespace->name, service->name);
+		loc_namespace_destroy(namespace);
+		fibril_mutex_unlock(&services_list_mutex);
+		free(service->name);
+		free(service);
+		async_answer_0(iid, EEXISTS);
+		return;
+	}
+	
+	/* Get unique service ID */
+	service->id = loc_create_id();
+
+	loc_namespace_addref(namespace, service);
+	service->server = server;
+	
+	/* Insert service into list of all services  */
+	list_append(&service->services, &services_list);
+	
+	/* Insert service into list of services supplied by one server */
+	fibril_mutex_lock(&service->server->services_mutex);
+	
+	list_append(&service->server_services, &service->server->services);
+	
+	fibril_mutex_unlock(&service->server->services_mutex);
+	fibril_condvar_broadcast(&services_list_cv);
+	fibril_mutex_unlock(&services_list_mutex);
+	
+	async_answer_1(iid, EOK, service->id);
+}
+
+/**
+ *
+ */
+static void loc_service_unregister(ipc_callid_t iid, ipc_call_t *icall, 
+    loc_server_t *server)
+{
+	loc_service_t *svc;
+	
+	fibril_mutex_lock(&services_list_mutex);
+	svc = loc_service_find_id(IPC_GET_ARG1(*icall));
+	if (svc == NULL) {
+		fibril_mutex_unlock(&services_list_mutex);
+		async_answer_0(iid, ENOENT);
+		return;
+	}
+	
+	fibril_mutex_lock(&cdir.mutex);
+	loc_service_unregister_core(svc);
+	fibril_mutex_unlock(&cdir.mutex);
+	fibril_mutex_unlock(&services_list_mutex);
+	async_answer_0(iid, EOK);
+}
+
+static void loc_category_get_name(ipc_callid_t iid, ipc_call_t *icall)
+{
+	ipc_callid_t callid;
+	size_t size;
+	size_t act_size;
+	category_t *cat;
+	
+	if (!async_data_read_receive(&callid, &size)) {
+		async_answer_0(callid, EREFUSED);
+		async_answer_0(iid, EREFUSED);
+		return;
+	}
+	
+	fibril_mutex_lock(&cdir.mutex);
+	
+	cat = category_get(&cdir, IPC_GET_ARG1(*icall));
+	if (cat == NULL) {
+		fibril_mutex_unlock(&cdir.mutex);
+		async_answer_0(callid, ENOENT);
+		async_answer_0(iid, ENOENT);
+		return;
+	}
+	
+	act_size = str_size(cat->name);
+	if (act_size > size) {
+		fibril_mutex_unlock(&cdir.mutex);
+		async_answer_0(callid, EOVERFLOW);
+		async_answer_0(iid, EOVERFLOW);
+		return;
+	}
+	
+	sysarg_t retval = async_data_read_finalize(callid, cat->name,
+	    min(size, act_size));
+	
+	fibril_mutex_unlock(&cdir.mutex);
+	
+	async_answer_0(iid, retval);
+}
+
+static void loc_service_get_name(ipc_callid_t iid, ipc_call_t *icall)
+{
+	ipc_callid_t callid;
+	size_t size;
+	size_t act_size;
+	loc_service_t *svc;
+	char *fqn;
+	
+	if (!async_data_read_receive(&callid, &size)) {
+		async_answer_0(callid, EREFUSED);
+		async_answer_0(iid, EREFUSED);
+		return;
+	}
+	
+	fibril_mutex_lock(&services_list_mutex);
+	
+	svc = loc_service_find_id(IPC_GET_ARG1(*icall));
+	if (svc == NULL) {
+		fibril_mutex_unlock(&services_list_mutex);
+		async_answer_0(callid, ENOENT);
+		async_answer_0(iid, ENOENT);
+		return;
+	}
+	
+	if (asprintf(&fqn, "%s/%s", svc->namespace->name, svc->name) < 0) {
+		fibril_mutex_unlock(&services_list_mutex);
+		async_answer_0(callid, ENOMEM);
+		async_answer_0(iid, ENOMEM);
+		return;
+	}
+	
+	act_size = str_size(fqn);
+	if (act_size > size) {
+		free(fqn);
+		fibril_mutex_unlock(&services_list_mutex);
+		async_answer_0(callid, EOVERFLOW);
+		async_answer_0(iid, EOVERFLOW);
+		return;
+	}
+	
+	sysarg_t retval = async_data_read_finalize(callid, fqn,
+	    min(size, act_size));
+	free(fqn);
+	
+	fibril_mutex_unlock(&services_list_mutex);
+	
+	async_answer_0(iid, retval);
+}
+
+/** Connect client to the service.
+ *
+ * Find server supplying requested service and forward
+ * the message to it.
+ *
+ */
+static void loc_forward(ipc_callid_t callid, ipc_call_t *call)
+{
+	fibril_mutex_lock(&services_list_mutex);
+	
+	/*
+	 * Get ID from request
+	 */
+	service_id_t id = IPC_GET_ARG2(*call);
+	loc_service_t *svc = loc_service_find_id(id);
+	
+	if ((svc == NULL) || (svc->server == NULL) || (!svc->server->sess)) {
+		fibril_mutex_unlock(&services_list_mutex);
+		async_answer_0(callid, ENOENT);
+		return;
+	}
+	
+	async_exch_t *exch = async_exchange_begin(svc->server->sess);
+	
+	if (svc->forward_interface == 0)
+		async_forward_fast(callid, exch, svc->id, 0, 0, IPC_FF_NONE);
+	else
+		async_forward_fast(callid, exch, svc->forward_interface,
+		    svc->id, 0, IPC_FF_NONE);
+	
+	async_exchange_end(exch);
+	
+	fibril_mutex_unlock(&services_list_mutex);
+}
+
+/** Find ID for service identified by name.
+ *
+ * In answer will be send EOK and service ID in arg1 or a error
+ * code from errno.h.
+ *
+ */
+static void loc_service_get_id(ipc_callid_t iid, ipc_call_t *icall)
+{
+	char *fqsn;
+	
+	/* Get fqsn */
+	int rc = async_data_write_accept((void **) &fqsn, true, 0,
+	    LOC_NAME_MAXLEN, 0, NULL);
+	if (rc != EOK) {
+		async_answer_0(iid, rc);
+		return;
+	}
+	
+	char *ns_name;
+	char *name;
+	if (!loc_fqsn_split(fqsn, &ns_name, &name)) {
+		free(fqsn);
+		async_answer_0(iid, EINVAL);
+		return;
+	}
+	
+	free(fqsn);
+	
+	fibril_mutex_lock(&services_list_mutex);
+	const loc_service_t *svc;
+	
+recheck:
+	
+	/*
+	 * Find service name in the list of known services.
+	 */
+	svc = loc_service_find_name(ns_name, name);
+	
+	/*
+	 * Device was not found.
+	 */
+	if (svc == NULL) {
+		if (IPC_GET_ARG1(*icall) & IPC_FLAG_BLOCKING) {
+			/* Blocking lookup */
+			fibril_condvar_wait(&services_list_cv,
+			    &services_list_mutex);
+			goto recheck;
+		}
+		
+		async_answer_0(iid, ENOENT);
+		free(ns_name);
+		free(name);
+		fibril_mutex_unlock(&services_list_mutex);
+		return;
+	}
+	
+	async_answer_1(iid, EOK, svc->id);
+	
+	fibril_mutex_unlock(&services_list_mutex);
+	free(ns_name);
+	free(name);
+}
+
+/** Find ID for namespace identified by name.
+ *
+ * In answer will be send EOK and service ID in arg1 or a error
+ * code from errno.h.
+ *
+ */
+static void loc_namespace_get_id(ipc_callid_t iid, ipc_call_t *icall)
+{
+	char *name;
+	
+	/* Get service name */
+	int rc = async_data_write_accept((void **) &name, true, 0,
+	    LOC_NAME_MAXLEN, 0, NULL);
+	if (rc != EOK) {
+		async_answer_0(iid, rc);
+		return;
+	}
+	
+	fibril_mutex_lock(&services_list_mutex);
+	const loc_namespace_t *namespace;
+	
+recheck:
+	
+	/*
+	 * Find namespace name in the list of known namespaces.
+	 */
+	namespace = loc_namespace_find_name(name);
+	
+	/*
+	 * Namespace was not found.
+	 */
+	if (namespace == NULL) {
+		if (IPC_GET_ARG1(*icall) & IPC_FLAG_BLOCKING) {
+			/* Blocking lookup */
+			fibril_condvar_wait(&services_list_cv,
+			    &services_list_mutex);
+			goto recheck;
+		}
+		
+		async_answer_0(iid, ENOENT);
+		free(name);
+		fibril_mutex_unlock(&services_list_mutex);
+		return;
+	}
+	
+	async_answer_1(iid, EOK, namespace->id);
+	
+	fibril_mutex_unlock(&services_list_mutex);
+	free(name);
+}
+
+/** Create callback connection.
+ *
+ * Create callback connection which will be used to send category change
+ * events.
+ *
+ * On success, answer will contain EOK int retval.
+ * On failure, error code will be sent in retval.
+ *
+ */
+static void loc_callback_create(ipc_callid_t iid, ipc_call_t *icall)
+{
+	cb_sess_t *cb_sess = calloc(1, sizeof(cb_sess_t));
+	if (cb_sess == NULL) {
+		async_answer_0(iid, ENOMEM);
+		return;
+	}
+	
+	async_sess_t *sess = async_callback_receive(EXCHANGE_SERIALIZE);
+	if (sess == NULL) {
+		free(cb_sess);
+		async_answer_0(iid, ENOMEM);
+		return;
+	}
+	
+	cb_sess->sess = sess;
+	link_initialize(&cb_sess->cb_sess_list);
+	
+	fibril_mutex_lock(&callback_sess_mutex);
+	list_append(&cb_sess->cb_sess_list, &callback_sess_list);
+	fibril_mutex_unlock(&callback_sess_mutex);
+	
+	async_answer_0(iid, EOK);
+}
+
+void loc_category_change_event(void)
+{
+	fibril_mutex_lock(&callback_sess_mutex);
+	
+	list_foreach(callback_sess_list, link) {
+		cb_sess_t *cb_sess;
+		
+		cb_sess = list_get_instance(link, cb_sess_t, cb_sess_list);
+		
+		async_exch_t *exch = async_exchange_begin(cb_sess->sess);
+		async_msg_0(exch, LOC_EVENT_CAT_CHANGE);
+		async_exchange_end(exch);
+	}
+	
+	fibril_mutex_unlock(&callback_sess_mutex);
+}
+
+/** Find ID for category specified by name.
+ *
+ * On success, answer will contain EOK int retval and service ID in arg1.
+ * On failure, error code will be sent in retval.
+ *
+ */
+static void loc_category_get_id(ipc_callid_t iid, ipc_call_t *icall)
+{
+	char *name;
+	category_t *cat;
+	
+	/* Get service name */
+	int rc = async_data_write_accept((void **) &name, true, 0,
+	    LOC_NAME_MAXLEN, 0, NULL);
+	if (rc != EOK) {
+		async_answer_0(iid, rc);
+		return;
+	}
+	
+	fibril_mutex_lock(&cdir.mutex);
+
+	cat = category_find_by_name(&cdir, name);
+	if (cat == NULL) {
+		/* Category not found */
+		async_answer_0(iid, ENOENT);
+		goto cleanup;
+	}
+	
+	async_answer_1(iid, EOK, cat->id);
+cleanup:
+	fibril_mutex_unlock(&cdir.mutex);
+	free(name);
+}
+
+static void loc_id_probe(ipc_callid_t iid, ipc_call_t *icall)
+{
+	fibril_mutex_lock(&services_list_mutex);
+	
+	loc_namespace_t *namespace =
+	    loc_namespace_find_id(IPC_GET_ARG1(*icall));
+	if (namespace == NULL) {
+		loc_service_t *svc =
+		    loc_service_find_id(IPC_GET_ARG1(*icall));
+		if (svc == NULL)
+			async_answer_1(iid, EOK, LOC_OBJECT_NONE);
+		else
+			async_answer_1(iid, EOK, LOC_OBJECT_SERVICE);
+	} else
+		async_answer_1(iid, EOK, LOC_OBJECT_NAMESPACE);
+	
+	fibril_mutex_unlock(&services_list_mutex);
+}
+
+static void loc_get_namespace_count(ipc_callid_t iid, ipc_call_t *icall)
+{
+	fibril_mutex_lock(&services_list_mutex);
+	async_answer_1(iid, EOK, list_count(&namespaces_list));
+	fibril_mutex_unlock(&services_list_mutex);
+}
+
+static void loc_get_service_count(ipc_callid_t iid, ipc_call_t *icall)
+{
+	fibril_mutex_lock(&services_list_mutex);
+	
+	loc_namespace_t *namespace =
+	    loc_namespace_find_id(IPC_GET_ARG1(*icall));
+	if (namespace == NULL)
+		async_answer_0(iid, EEXISTS);
+	else
+		async_answer_1(iid, EOK, namespace->refcnt);
+	
+	fibril_mutex_unlock(&services_list_mutex);
+}
+
+static void loc_get_categories(ipc_callid_t iid, ipc_call_t *icall)
+{
+	ipc_callid_t callid;
+	size_t size;
+	size_t act_size;
+	int rc;
+	
+	if (!async_data_read_receive(&callid, &size)) {
+		async_answer_0(callid, EREFUSED);
+		async_answer_0(iid, EREFUSED);
+		return;
+	}
+	
+	category_id_t *id_buf = (category_id_t *) malloc(size);
+	if (id_buf == NULL) {
+		fibril_mutex_unlock(&cdir.mutex);
+		async_answer_0(callid, ENOMEM);
+		async_answer_0(iid, ENOMEM);
+		return;
+	}
+	
+	fibril_mutex_lock(&cdir.mutex);
+	
+	rc = categ_dir_get_categories(&cdir, id_buf, size, &act_size);
+	if (rc != EOK) {
+		fibril_mutex_unlock(&cdir.mutex);
+		async_answer_0(callid, rc);
+		async_answer_0(iid, rc);
+		return;
+	}
+	
+	fibril_mutex_unlock(&cdir.mutex);
+	
+	sysarg_t retval = async_data_read_finalize(callid, id_buf, size);
+	free(id_buf);
+	
+	async_answer_1(iid, retval, act_size);
+}
+
+static void loc_get_namespaces(ipc_callid_t iid, ipc_call_t *icall)
+{
+	ipc_callid_t callid;
+	size_t size;
+	if (!async_data_read_receive(&callid, &size)) {
+		async_answer_0(callid, EREFUSED);
+		async_answer_0(iid, EREFUSED);
+		return;
+	}
+	
+	if ((size % sizeof(loc_sdesc_t)) != 0) {
+		async_answer_0(callid, EINVAL);
+		async_answer_0(iid, EINVAL);
+		return;
+	}
+	
+	fibril_mutex_lock(&services_list_mutex);
+	
+	size_t count = size / sizeof(loc_sdesc_t);
+	if (count != list_count(&namespaces_list)) {
+		fibril_mutex_unlock(&services_list_mutex);
+		async_answer_0(callid, EOVERFLOW);
+		async_answer_0(iid, EOVERFLOW);
+		return;
+	}
+	
+	loc_sdesc_t *desc = (loc_sdesc_t *) malloc(size);
+	if (desc == NULL) {
+		fibril_mutex_unlock(&services_list_mutex);
+		async_answer_0(callid, ENOMEM);
+		async_answer_0(iid, ENOMEM);
+		return;
+	}
+	
+	size_t pos = 0;
+	list_foreach(namespaces_list, item) {
+		loc_namespace_t *namespace =
+		    list_get_instance(item, loc_namespace_t, namespaces);
+		
+		desc[pos].id = namespace->id;
+		str_cpy(desc[pos].name, LOC_NAME_MAXLEN, namespace->name);
+		pos++;
+	}
+	
+	sysarg_t retval = async_data_read_finalize(callid, desc, size);
+	
+	free(desc);
+	fibril_mutex_unlock(&services_list_mutex);
+	
+	async_answer_0(iid, retval);
+}
+
+static void loc_get_services(ipc_callid_t iid, ipc_call_t *icall)
+{
+	/* FIXME: Use faster algorithm which can make better use
+	   of namespaces */
+	
+	ipc_callid_t callid;
+	size_t size;
+	if (!async_data_read_receive(&callid, &size)) {
+		async_answer_0(callid, EREFUSED);
+		async_answer_0(iid, EREFUSED);
+		return;
+	}
+	
+	if ((size % sizeof(loc_sdesc_t)) != 0) {
+		async_answer_0(callid, EINVAL);
+		async_answer_0(iid, EINVAL);
+		return;
+	}
+	
+	fibril_mutex_lock(&services_list_mutex);
+	
+	loc_namespace_t *namespace =
+	    loc_namespace_find_id(IPC_GET_ARG1(*icall));
+	if (namespace == NULL) {
+		fibril_mutex_unlock(&services_list_mutex);
+		async_answer_0(callid, ENOENT);
+		async_answer_0(iid, ENOENT);
+		return;
+	}
+	
+	size_t count = size / sizeof(loc_sdesc_t);
+	if (count != namespace->refcnt) {
+		fibril_mutex_unlock(&services_list_mutex);
+		async_answer_0(callid, EOVERFLOW);
+		async_answer_0(iid, EOVERFLOW);
+		return;
+	}
+	
+	loc_sdesc_t *desc = (loc_sdesc_t *) malloc(size);
+	if (desc == NULL) {
+		fibril_mutex_unlock(&services_list_mutex);
+		async_answer_0(callid, ENOMEM);
+		async_answer_0(iid, EREFUSED);
+		return;
+	}
+	
+	size_t pos = 0;
+	list_foreach(services_list, item) {
+		loc_service_t *service =
+		    list_get_instance(item, loc_service_t, services);
+		
+		if (service->namespace == namespace) {
+			desc[pos].id = service->id;
+			str_cpy(desc[pos].name, LOC_NAME_MAXLEN, service->name);
+			pos++;
+		}
+	}
+	
+	sysarg_t retval = async_data_read_finalize(callid, desc, size);
+	
+	free(desc);
+	fibril_mutex_unlock(&services_list_mutex);
+	
+	async_answer_0(iid, retval);
+}
+
+static void loc_category_get_svcs(ipc_callid_t iid, ipc_call_t *icall)
+{
+	ipc_callid_t callid;
+	size_t size;
+	size_t act_size;
+	int rc;
+	
+	if (!async_data_read_receive(&callid, &size)) {
+		async_answer_0(callid, EREFUSED);
+		async_answer_0(iid, EREFUSED);
+		return;
+	}
+	
+	fibril_mutex_lock(&cdir.mutex);
+	
+	category_t *cat = category_get(&cdir, IPC_GET_ARG1(*icall));
+	if (cat == NULL) {
+		fibril_mutex_unlock(&cdir.mutex);
+		async_answer_0(callid, ENOENT);
+		async_answer_0(iid, ENOENT);
+		return;
+	}
+	
+	category_id_t *id_buf = (category_id_t *) malloc(size);
+	if (id_buf == NULL) {
+		fibril_mutex_unlock(&cdir.mutex);
+		async_answer_0(callid, ENOMEM);
+		async_answer_0(iid, ENOMEM);
+		return;
+	}
+	
+	fibril_mutex_lock(&cat->mutex);
+	
+	rc = category_get_services(cat, id_buf, size, &act_size);
+	if (rc != EOK) {
+		fibril_mutex_unlock(&cat->mutex);
+		fibril_mutex_unlock(&cdir.mutex);
+		async_answer_0(callid, rc);
+		async_answer_0(iid, rc);
+		return;
+	}
+	
+	fibril_mutex_unlock(&cat->mutex);
+	fibril_mutex_unlock(&cdir.mutex);
+	
+	sysarg_t retval = async_data_read_finalize(callid, id_buf, size);
+	free(id_buf);
+	
+	async_answer_1(iid, retval, act_size);
+}
+
+
+static void loc_null_create(ipc_callid_t iid, ipc_call_t *icall)
+{
+	fibril_mutex_lock(&null_services_mutex);
+	
+	unsigned int i;
+	bool fnd = false;
+	
+	for (i = 0; i < NULL_SERVICES; i++) {
+		if (null_services[i] == NULL) {
+			fnd = true;
+			break;
+		}
+	}
+	
+	if (!fnd) {
+		fibril_mutex_unlock(&null_services_mutex);
+		async_answer_0(iid, ENOMEM);
+		return;
+	}
+	
+	char null[LOC_NAME_MAXLEN];
+	snprintf(null, LOC_NAME_MAXLEN, "%u", i);
+	
+	char *dev_name = str_dup(null);
+	if (dev_name == NULL) {
+		fibril_mutex_unlock(&null_services_mutex);
+		async_answer_0(iid, ENOMEM);
+		return;
+	}
+	
+	loc_service_t *service =
+	    (loc_service_t *) malloc(sizeof(loc_service_t));
+	if (service == NULL) {
+		fibril_mutex_unlock(&null_services_mutex);
+		async_answer_0(iid, ENOMEM);
+		return;
+	}
+	
+	fibril_mutex_lock(&services_list_mutex);
+	
+	loc_namespace_t *namespace = loc_namespace_create("null");
+	if (namespace == NULL) {
+		fibril_mutex_lock(&services_list_mutex);
+		fibril_mutex_unlock(&null_services_mutex);
+		async_answer_0(iid, ENOMEM);
+		return;
+	}
+	
+	link_initialize(&service->services);
+	link_initialize(&service->server_services);
+	list_initialize(&service->cat_memb);
+	
+	/* Get unique service ID */
+	service->id = loc_create_id();
+	service->server = NULL;
+	
+	loc_namespace_addref(namespace, service);
+	service->name = dev_name;
+	
+	/*
+	 * Insert service into list of all services and into null services array.
+	 * Insert service into a dummy list of null server's services so that it
+	 * can be safely removed later.
+	 */
+	list_append(&service->services, &services_list);
+	list_append(&service->server_services, &dummy_null_services);
+	null_services[i] = service;
+	
+	fibril_mutex_unlock(&services_list_mutex);
+	fibril_mutex_unlock(&null_services_mutex);
+	
+	async_answer_1(iid, EOK, (sysarg_t) i);
+}
+
+static void loc_null_destroy(ipc_callid_t iid, ipc_call_t *icall)
+{
+	sysarg_t i = IPC_GET_ARG1(*icall);
+	if (i >= NULL_SERVICES) {
+		async_answer_0(iid, ELIMIT);
+		return;
+	}
+	
+	fibril_mutex_lock(&null_services_mutex);
+	
+	if (null_services[i] == NULL) {
+		fibril_mutex_unlock(&null_services_mutex);
+		async_answer_0(iid, ENOENT);
+		return;
+	}
+	
+	fibril_mutex_lock(&services_list_mutex);
+	fibril_mutex_lock(&cdir.mutex);
+	loc_service_unregister_core(null_services[i]);
+	fibril_mutex_unlock(&cdir.mutex);
+	fibril_mutex_unlock(&services_list_mutex);
+	
+	null_services[i] = NULL;
+	
+	fibril_mutex_unlock(&null_services_mutex);
+	async_answer_0(iid, EOK);
+}
+
+static void loc_service_add_to_cat(ipc_callid_t iid, ipc_call_t *icall)
+{
+	category_t *cat;
+	loc_service_t *svc;
+	catid_t cat_id;
+	service_id_t svc_id;
+	sysarg_t retval;
+	
+	svc_id = IPC_GET_ARG1(*icall);
+	cat_id = IPC_GET_ARG2(*icall);
+	
+	fibril_mutex_lock(&services_list_mutex);
+	fibril_mutex_lock(&cdir.mutex);
+	
+	cat = category_get(&cdir, cat_id);
+	svc = loc_service_find_id(svc_id);
+	
+	if (cat == NULL || svc == NULL) {
+		fibril_mutex_unlock(&cdir.mutex);
+		fibril_mutex_unlock(&services_list_mutex);
+		async_answer_0(iid, ENOENT);
+		return;
+	}
+	
+	fibril_mutex_lock(&cat->mutex);
+	retval = category_add_service(cat, svc);
+
+	fibril_mutex_unlock(&cat->mutex);
+	fibril_mutex_unlock(&cdir.mutex);
+	fibril_mutex_unlock(&services_list_mutex);
+
+	async_answer_0(iid, retval);
+
+	loc_category_change_event();
+}
+
+
+/** Initialize location service.
+ *
+ *
+ */
+static bool loc_init(void)
+{
+	unsigned int i;
+	category_t *cat;
+
+	for (i = 0; i < NULL_SERVICES; i++)
+		null_services[i] = NULL;
+	
+	categ_dir_init(&cdir);
+
+	cat = category_new("bd");
+	categ_dir_add_cat(&cdir, cat);
+
+	cat = category_new("iplink");
+	categ_dir_add_cat(&cdir, cat);
+
+	cat = category_new("keyboard");
+	categ_dir_add_cat(&cdir, cat);
+
+	cat = category_new("mouse");
+	categ_dir_add_cat(&cdir, cat);
+
+	cat = category_new("serial");
+	categ_dir_add_cat(&cdir, cat);
+
+	cat = category_new("test3");
+	categ_dir_add_cat(&cdir, cat);
+
+	cat = category_new("usbhc");
+	categ_dir_add_cat(&cdir, cat);
+
+	cat = category_new("virt-null");
+	categ_dir_add_cat(&cdir, cat);
+
+	cat = category_new("virtual");
+	categ_dir_add_cat(&cdir, cat);
+	
+	cat = category_new("nic");
+	categ_dir_add_cat(&cdir, cat);
+	
+	return true;
+}
+
+/** Handle connection on supplier port.
+ *
+ */
+static void loc_connection_supplier(ipc_callid_t iid, ipc_call_t *icall)
+{
+	/* Accept connection */
+	async_answer_0(iid, EOK);
+	
+	loc_server_t *server = loc_server_register();
+	if (server == NULL)
+		return;
+	
+	while (true) {
+		ipc_call_t call;
+		ipc_callid_t callid = async_get_call(&call);
+		
+		if (!IPC_GET_IMETHOD(call))
+			break;
+		
+		switch (IPC_GET_IMETHOD(call)) {
+		case LOC_SERVER_UNREGISTER:
+			if (server == NULL)
+				async_answer_0(callid, ENOENT);
+			else
+				async_answer_0(callid, EOK);
+			break;
+		case LOC_SERVICE_ADD_TO_CAT:
+			/* Add service to category */
+			loc_service_add_to_cat(callid, &call);
+			break;
+		case LOC_SERVICE_REGISTER:
+			/* Register one service */
+			loc_service_register(callid, &call, server);
+			break;
+		case LOC_SERVICE_UNREGISTER:
+			/* Remove one service */
+			loc_service_unregister(callid, &call, server);
+			break;
+		case LOC_SERVICE_GET_ID:
+			loc_service_get_id(callid, &call);
+			break;
+		case LOC_NAMESPACE_GET_ID:
+			loc_namespace_get_id(callid, &call);
+			break;
+		default:
+			async_answer_0(callid, ENOENT);
+		}
+	}
+	
+	if (server != NULL) {
+		/*
+		 * Unregister the server and all its services.
+		 */
+		loc_server_unregister(server);
+		server = NULL;
+	}
+}
+
+/** Handle connection on consumer port.
+ *
+ */
+static void loc_connection_consumer(ipc_callid_t iid, ipc_call_t *icall)
+{
+	/* Accept connection */
+	async_answer_0(iid, EOK);
+	
+	while (true) {
+		ipc_call_t call;
+		ipc_callid_t callid = async_get_call(&call);
+		
+		if (!IPC_GET_IMETHOD(call))
+			break;
+		
+		switch (IPC_GET_IMETHOD(call)) {
+		case LOC_SERVICE_GET_ID:
+			loc_service_get_id(callid, &call);
+			break;
+		case LOC_SERVICE_GET_NAME:
+			loc_service_get_name(callid, &call);
+			break;
+		case LOC_NAMESPACE_GET_ID:
+			loc_namespace_get_id(callid, &call);
+			break;
+		case LOC_CALLBACK_CREATE:
+			loc_callback_create(callid, &call);
+			break;
+		case LOC_CATEGORY_GET_ID:
+			loc_category_get_id(callid, &call);
+			break;
+		case LOC_CATEGORY_GET_NAME:
+			loc_category_get_name(callid, &call);
+			break;
+		case LOC_CATEGORY_GET_SVCS:
+			loc_category_get_svcs(callid, &call);
+			break;
+		case LOC_ID_PROBE:
+			loc_id_probe(callid, &call);
+			break;
+		case LOC_NULL_CREATE:
+			loc_null_create(callid, &call);
+			break;
+		case LOC_NULL_DESTROY:
+			loc_null_destroy(callid, &call);
+			break;
+		case LOC_GET_NAMESPACE_COUNT:
+			loc_get_namespace_count(callid, &call);
+			break;
+		case LOC_GET_SERVICE_COUNT:
+			loc_get_service_count(callid, &call);
+			break;
+		case LOC_GET_CATEGORIES:
+			loc_get_categories(callid, &call);
+			break;
+		case LOC_GET_NAMESPACES:
+			loc_get_namespaces(callid, &call);
+			break;
+		case LOC_GET_SERVICES:
+			loc_get_services(callid, &call);
+			break;
+		default:
+			async_answer_0(callid, ENOENT);
+		}
+	}
+}
+
+/** Function for handling connections to location service
+ *
+ */
+static void loc_connection(ipc_callid_t iid, ipc_call_t *icall, void *arg)
+{
+	/* Select interface */
+	switch ((sysarg_t) (IPC_GET_ARG1(*icall))) {
+	case LOC_PORT_SUPPLIER:
+		loc_connection_supplier(iid, icall);
+		break;
+	case LOC_PORT_CONSUMER:
+		loc_connection_consumer(iid, icall);
+		break;
+	case LOC_CONNECT_TO_SERVICE:
+		/* Connect client to selected service */
+		loc_forward(iid, icall);
+		break;
+	default:
+		/* No such interface */
+		async_answer_0(iid, ENOENT);
+	}
+}
+
+/**
+ *
+ */
+int main(int argc, char *argv[])
+{
+	printf("%s: HelenOS Location Service\n", NAME);
+	
+	if (!loc_init()) {
+		printf("%s: Error while initializing service\n", NAME);
+		return -1;
+	}
+	
+	/* Set a handler of incomming connections */
+	async_set_client_connection(loc_connection);
+	
+	/* Register location service at naming service */
+	if (service_register(SERVICE_LOC) != EOK)
+		return -1;
+	
+	printf("%s: Accepting connections\n", NAME);
+	async_manager();
+	
+	/* Never reached */
+	return 0;
+}
+
+/**
+ * @}
+ */
Index: uspace/srv/locsrv/locsrv.h
===================================================================
--- uspace/srv/locsrv/locsrv.h	(revision ce1836ed04784ea0bacc387ac80bfe852f36119b)
+++ uspace/srv/locsrv/locsrv.h	(revision ce1836ed04784ea0bacc387ac80bfe852f36119b)
@@ -0,0 +1,114 @@
+/*
+ * Copyright (c) 2011 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 loc
+ * @{
+ */
+/** @file HelenOS location service.
+ */
+
+#ifndef LOCSRV_H_
+#define LOCSRV_H_
+
+#include <ipc/loc.h>
+#include <async.h>
+#include <fibril_synch.h>
+#include <sys/types.h>
+
+/** Representation of server (supplier).
+ *
+ * Each server supplies a set of services.
+ *
+ */
+typedef struct {
+	/** Link to servers_list */
+	link_t servers;
+	
+	/** List of services supplied by this server */
+	list_t services;
+	
+	/** Session asociated with this server */
+	async_sess_t *sess;
+	
+	/** Server name */
+	char *name;
+	
+	/** Fibril mutex for list of services owned by this server */
+	fibril_mutex_t services_mutex;
+} loc_server_t;
+
+/** Info about registered namespaces
+ *
+ */
+typedef struct {
+	/** Link to namespaces_list */
+	link_t namespaces;
+	
+	/** Unique namespace identifier */
+	service_id_t id;
+	
+	/** Namespace name */
+	char *name;
+	
+	/** Reference count */
+	size_t refcnt;
+} loc_namespace_t;
+
+/** Info about registered service
+ *
+ */
+typedef struct {
+	/** Link to global list of services (services_list) */
+	link_t services;
+	/** Link to server list of services (loc_server_t.services) */
+	link_t server_services;
+	/** Link to list of services in category (category_t.services) */
+	link_t cat_services;
+	/** List of category memberships (svc_categ_t) */
+	list_t cat_memb;
+	/** Unique service identifier */
+	service_id_t id;
+	/** Service namespace */
+	loc_namespace_t *namespace;
+	/** Service name */
+	char *name;
+	/** Supplier of this service */
+	loc_server_t *server;
+	/** Use this interface when forwarding to server. */
+	sysarg_t forward_interface;
+} loc_service_t;
+
+extern fibril_mutex_t services_list_mutex;
+
+extern service_id_t loc_create_id(void);
+extern void loc_category_change_event(void);
+
+#endif
+
+/** @}
+ */
Index: pace/srv/net/inet/Makefile
===================================================================
--- uspace/srv/net/inet/Makefile	(revision 6df220289417a3a5f0a319426564833d6f527370)
+++ 	(revision )
@@ -1,44 +1,0 @@
-#
-# 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.
-#
-
-USPACE_PREFIX = ../../..
-BINARY = inet
-
-SOURCES = \
-	addrobj.c \
-	icmp.c \
-	inet.c \
-	inet_link.c \
-	inet_util.c \
-	inetcfg.c \
-	inetping.c \
-	pdu.c \
-	reass.c \
-	sroute.c
-
-include $(USPACE_PREFIX)/Makefile.common
Index: pace/srv/net/inet/addrobj.c
===================================================================
--- uspace/srv/net/inet/addrobj.c	(revision 6df220289417a3a5f0a319426564833d6f527370)
+++ 	(revision )
@@ -1,229 +1,0 @@
-/*
- * 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 <bitops.h>
-#include <errno.h>
-#include <fibril_synch.h>
-#include <io/log.h>
-#include <ipc/loc.h>
-#include <stdlib.h>
-#include <str.h>
-
-#include "addrobj.h"
-#include "inet.h"
-#include "inet_link.h"
-#include "inet_util.h"
-
-static FIBRIL_MUTEX_INITIALIZE(addr_list_lock);
-static LIST_INITIALIZE(addr_list);
-static sysarg_t addr_id = 0;
-
-inet_addrobj_t *inet_addrobj_new(void)
-{
-	inet_addrobj_t *addr = calloc(1, sizeof(inet_addrobj_t));
-
-	if (addr == NULL) {
-		log_msg(LVL_ERROR, "Failed allocating address object. "
-		    "Out of memory.");
-		return NULL;
-	}
-
-	link_initialize(&addr->addr_list);
-	fibril_mutex_lock(&addr_list_lock);
-	addr->id = ++addr_id;
-	fibril_mutex_unlock(&addr_list_lock);
-
-	return addr;
-}
-
-void inet_addrobj_delete(inet_addrobj_t *addr)
-{
-	if (addr->name != NULL)
-		free(addr->name);
-	free(addr);
-}
-
-void inet_addrobj_add(inet_addrobj_t *addr)
-{
-	fibril_mutex_lock(&addr_list_lock);
-	list_append(&addr->addr_list, &addr_list);
-	fibril_mutex_unlock(&addr_list_lock);
-}
-
-void inet_addrobj_remove(inet_addrobj_t *addr)
-{
-	fibril_mutex_lock(&addr_list_lock);
-	list_remove(&addr->addr_list);
-	fibril_mutex_unlock(&addr_list_lock);
-}
-
-/** Find address object matching address @a addr.
- *
- * @param addr	Address
- * @oaram find	iaf_net to find network (using mask),
- *		iaf_addr to find local address (exact match)
- */
-inet_addrobj_t *inet_addrobj_find(inet_addr_t *addr, inet_addrobj_find_t find)
-{
-	uint32_t mask;
-
-	log_msg(LVL_DEBUG, "inet_addrobj_find(%x)", (unsigned)addr->ipv4);
-
-	fibril_mutex_lock(&addr_list_lock);
-
-	list_foreach(addr_list, link) {
-		inet_addrobj_t *naddr = list_get_instance(link,
-		    inet_addrobj_t, addr_list);
-
-		mask = inet_netmask(naddr->naddr.bits);
-		if ((naddr->naddr.ipv4 & mask) == (addr->ipv4 & mask)) {
-			fibril_mutex_unlock(&addr_list_lock);
-			log_msg(LVL_DEBUG, "inet_addrobj_find: found %p",
-			    naddr);
-			return naddr;
-		}
-	}
-
-	log_msg(LVL_DEBUG, "inet_addrobj_find: Not found");
-	fibril_mutex_unlock(&addr_list_lock);
-
-	return NULL;
-}
-
-/** Find address object on a link, with a specific name.
- *
- * @param name	Address object name
- * @param ilink	Inet link
- * @return	Address object
- */
-inet_addrobj_t *inet_addrobj_find_by_name(const char *name, inet_link_t *ilink)
-{
-	log_msg(LVL_DEBUG, "inet_addrobj_find_by_name('%s', '%s')",
-	    name, ilink->svc_name);
-
-	fibril_mutex_lock(&addr_list_lock);
-
-	list_foreach(addr_list, link) {
-		inet_addrobj_t *naddr = list_get_instance(link,
-		    inet_addrobj_t, addr_list);
-
-		if (naddr->ilink == ilink && str_cmp(naddr->name, name) == 0) {
-			fibril_mutex_unlock(&addr_list_lock);
-			log_msg(LVL_DEBUG, "inet_addrobj_find_by_name: found %p",
-			    naddr);
-			return naddr;
-		}
-	}
-
-	log_msg(LVL_DEBUG, "inet_addrobj_find_by_name: Not found");
-	fibril_mutex_unlock(&addr_list_lock);
-
-	return NULL;
-}
-
-/** Find address object with the given ID.
- *
- * @param id	Address object ID
- * @return	Address object
- */
-inet_addrobj_t *inet_addrobj_get_by_id(sysarg_t id)
-{
-	log_msg(LVL_DEBUG, "inet_addrobj_get_by_id(%zu)", (size_t)id);
-
-	fibril_mutex_lock(&addr_list_lock);
-
-	list_foreach(addr_list, link) {
-		inet_addrobj_t *naddr = list_get_instance(link,
-		    inet_addrobj_t, addr_list);
-
-		if (naddr->id == id) {
-			fibril_mutex_unlock(&addr_list_lock);
-			return naddr;
-		}
-	}
-
-	fibril_mutex_unlock(&addr_list_lock);
-
-	return NULL;
-}
-
-/** Send datagram from address object */
-int inet_addrobj_send_dgram(inet_addrobj_t *addr, inet_addr_t *ldest,
-    inet_dgram_t *dgram, uint8_t proto, uint8_t ttl, int df)
-{
-	inet_addr_t lsrc_addr;
-	inet_addr_t *ldest_addr;
-
-	lsrc_addr.ipv4 = addr->naddr.ipv4;
-	ldest_addr = &dgram->dest;
-
-	return inet_link_send_dgram(addr->ilink, &lsrc_addr, ldest_addr, dgram,
-	    proto, ttl, df);
-}
-
-/** Get IDs of all address objects. */
-int inet_addrobj_get_id_list(sysarg_t **rid_list, size_t *rcount)
-{
-	sysarg_t *id_list;
-	size_t count, i;
-
-	fibril_mutex_lock(&addr_list_lock);
-	count = list_count(&addr_list);
-
-	id_list = calloc(count, sizeof(sysarg_t));
-	if (id_list == NULL) {
-		fibril_mutex_unlock(&addr_list_lock);
-		return ENOMEM;
-	}
-
-	i = 0;
-	list_foreach(addr_list, link) {
-		inet_addrobj_t *addr = list_get_instance(link,
-		    inet_addrobj_t, addr_list);
-
-		id_list[i++] = addr->id;
-	}
-
-	fibril_mutex_unlock(&addr_list_lock);
-
-	*rid_list = id_list;
-	*rcount = count;
-
-	return EOK;
-}
-
-/** @}
- */
Index: pace/srv/net/inet/addrobj.h
===================================================================
--- uspace/srv/net/inet/addrobj.h	(revision 6df220289417a3a5f0a319426564833d6f527370)
+++ 	(revision )
@@ -1,65 +1,0 @@
-/*
- * 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 INET_ADDROBJ_H_
-#define INET_ADDROBJ_H_
-
-#include <sys/types.h>
-#include "inet.h"
-
-typedef enum {
-	/* Find matching network address (using mask) */
-	iaf_net,
-	/* Find exact local address (not using mask) */
-	iaf_addr
-} inet_addrobj_find_t;
-
-extern inet_addrobj_t *inet_addrobj_new(void);
-extern void inet_addrobj_delete(inet_addrobj_t *);
-extern void inet_addrobj_add(inet_addrobj_t *);
-extern void inet_addrobj_remove(inet_addrobj_t *);
-extern inet_addrobj_t *inet_addrobj_find(inet_addr_t *, inet_addrobj_find_t);
-extern inet_addrobj_t *inet_addrobj_find_by_name(const char *, inet_link_t *);
-extern inet_addrobj_t *inet_addrobj_get_by_id(sysarg_t);
-extern int inet_addrobj_send_dgram(inet_addrobj_t *, inet_addr_t *,
-    inet_dgram_t *, uint8_t, uint8_t, int);
-extern int inet_addrobj_get_id_list(sysarg_t **, size_t *);
-
-
-#endif
-
-/** @}
- */
Index: pace/srv/net/inet/icmp.c
===================================================================
--- uspace/srv/net/inet/icmp.c	(revision 6df220289417a3a5f0a319426564833d6f527370)
+++ 	(revision )
@@ -1,183 +1,0 @@
-/*
- * 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 <byteorder.h>
-#include <errno.h>
-#include <io/log.h>
-#include <mem.h>
-#include <stdlib.h>
-
-#include "icmp.h"
-#include "icmp_std.h"
-#include "inet.h"
-#include "inetping.h"
-#include "pdu.h"
-
-/* XXX */
-#define INET_TTL_MAX 255
-
-static int icmp_recv_echo_request(inet_dgram_t *);
-static int icmp_recv_echo_reply(inet_dgram_t *);
-
-int icmp_recv(inet_dgram_t *dgram)
-{
-	uint8_t type;
-
-	log_msg(LVL_DEBUG, "icmp_recv()");
-
-	if (dgram->size < 1)
-		return EINVAL;
-
-	type = *(uint8_t *)dgram->data;
-
-	switch (type) {
-	case ICMP_ECHO_REQUEST:
-		return icmp_recv_echo_request(dgram);
-	case ICMP_ECHO_REPLY:
-		return icmp_recv_echo_reply(dgram);
-	default:
-		break;
-	}
-
-	return EINVAL;
-}
-
-static int icmp_recv_echo_request(inet_dgram_t *dgram)
-{
-	icmp_echo_t *request, *reply;
-	uint16_t checksum;
-	size_t size;
-	inet_dgram_t rdgram;
-	int rc;
-
-	log_msg(LVL_DEBUG, "icmp_recv_echo_request()");
-
-	if (dgram->size < sizeof(icmp_echo_t))
-		return EINVAL;
-
-	request = (icmp_echo_t *)dgram->data;
-	size = dgram->size;
-
-	reply = calloc(size, 1);
-	if (reply == NULL)
-		return ENOMEM;
-
-	memcpy(reply, request, size);
-
-	reply->type = ICMP_ECHO_REPLY;
-	reply->code = 0;
-	reply->checksum = 0;
-
-	checksum = inet_checksum_calc(INET_CHECKSUM_INIT, reply, size);
-	reply->checksum = host2uint16_t_be(checksum);
-
-	rdgram.src = dgram->dest;
-	rdgram.dest = dgram->src;
-	rdgram.tos = ICMP_TOS;
-	rdgram.data = reply;
-	rdgram.size = size;
-
-	rc = inet_route_packet(&rdgram, IP_PROTO_ICMP, INET_TTL_MAX, 0);
-
-	free(reply);
-
-	return rc;
-}
-
-static int icmp_recv_echo_reply(inet_dgram_t *dgram)
-{
-	icmp_echo_t *reply;
-	inetping_sdu_t sdu;
-	uint16_t ident;
-
-	log_msg(LVL_DEBUG, "icmp_recv_echo_reply()");
-
-	if (dgram->size < sizeof(icmp_echo_t))
-		return EINVAL;
-
-	reply = (icmp_echo_t *)dgram->data;
-
-	sdu.src = dgram->src;
-	sdu.dest = dgram->dest;
-	sdu.seq_no = uint16_t_be2host(reply->seq_no);
-	sdu.data = reply + sizeof(icmp_echo_t);
-	sdu.size = dgram->size - sizeof(icmp_echo_t);
-	ident = uint16_t_be2host(reply->ident);
-
-	return inetping_recv(ident, &sdu);
-}
-
-int icmp_ping_send(uint16_t ident, inetping_sdu_t *sdu)
-{
-	inet_dgram_t dgram;
-	icmp_echo_t *request;
-	void *rdata;
-	size_t rsize;
-	uint16_t checksum;
-	int rc;
-
-	rsize = sizeof(icmp_echo_t) + sdu->size;
-	rdata = calloc(rsize, 1);
-	if (rdata == NULL)
-		return ENOMEM;
-
-	request = (icmp_echo_t *)rdata;
-
-	request->type = ICMP_ECHO_REQUEST;
-	request->code = 0;
-	request->checksum = 0;
-	request->ident = host2uint16_t_be(ident);
-	request->seq_no = host2uint16_t_be(sdu->seq_no);
-
-	memcpy(rdata + sizeof(icmp_echo_t), sdu->data, sdu->size);
-
-	checksum = inet_checksum_calc(INET_CHECKSUM_INIT, rdata, rsize);
-	request->checksum = host2uint16_t_be(checksum);
-
-	dgram.src = sdu->src;
-	dgram.dest = sdu->dest;
-	dgram.tos = ICMP_TOS;
-	dgram.data = rdata;
-	dgram.size = rsize;
-
-	rc = inet_route_packet(&dgram, IP_PROTO_ICMP, INET_TTL_MAX, 0);
-
-	free(rdata);
-	return rc;
-}
-
-/** @}
- */
Index: pace/srv/net/inet/icmp.h
===================================================================
--- uspace/srv/net/inet/icmp.h	(revision 6df220289417a3a5f0a319426564833d6f527370)
+++ 	(revision )
@@ -1,48 +1,0 @@
-/*
- * 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 ICMP_H_
-#define ICMP_H_
-
-#include "inet.h"
-
-extern int icmp_recv(inet_dgram_t *);
-extern int icmp_ping_send(uint16_t, inetping_sdu_t *);
-
-#endif
-
-/** @}
- */
Index: pace/srv/net/inet/icmp_std.h
===================================================================
--- uspace/srv/net/inet/icmp_std.h	(revision 6df220289417a3a5f0a319426564833d6f527370)
+++ 	(revision )
@@ -1,70 +1,0 @@
-/*
- * 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 ICMP standard definitions
- *
- */
-
-#ifndef ICMP_STD_H_
-#define ICMP_STD_H_
-
-#include <sys/types.h>
-
-#define IP_PROTO_ICMP 1
-
-/** Type of service used for ICMP */
-#define ICMP_TOS	0
-
-/** ICMP message type */
-enum icmp_type {
-	ICMP_ECHO_REPLY   = 0,
-	ICMP_ECHO_REQUEST = 8
-};
-
-/** ICMP Echo Request or Reply message header */
-typedef struct {
-	/** ICMP message type */
-	uint8_t type;
-	/** Code (0) */
-	uint8_t code;
-	/** Internet checksum of the ICMP message */
-	uint16_t checksum;
-	/** Indentifier */
-	uint16_t ident;
-	/** Sequence number */
-	uint16_t seq_no;
-} icmp_echo_t;
-
-#endif
-
-/** @}
- */
Index: pace/srv/net/inet/inet.c
===================================================================
--- uspace/srv/net/inet/inet.c	(revision 6df220289417a3a5f0a319426564833d6f527370)
+++ 	(revision )
@@ -1,450 +1,0 @@
-/*
- * 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 Internet Protocol service
- */
-
-#include <adt/list.h>
-#include <async.h>
-#include <errno.h>
-#include <fibril_synch.h>
-#include <io/log.h>
-#include <ipc/inet.h>
-#include <ipc/services.h>
-#include <loc.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <sys/types.h>
-
-#include "addrobj.h"
-#include "icmp.h"
-#include "icmp_std.h"
-#include "inet.h"
-#include "inetcfg.h"
-#include "inetping.h"
-#include "inet_link.h"
-#include "reass.h"
-#include "sroute.h"
-
-#define NAME "inet"
-
-static void inet_client_conn(ipc_callid_t iid, ipc_call_t *icall, void *arg);
-
-static FIBRIL_MUTEX_INITIALIZE(client_list_lock);
-static LIST_INITIALIZE(client_list);
-
-static int inet_init(void)
-{
-	service_id_t sid;
-	int rc;
-
-	log_msg(LVL_DEBUG, "inet_init()");
-
-	async_set_client_connection(inet_client_conn);
-
-	rc = loc_server_register(NAME);
-	if (rc != EOK) {
-		log_msg(LVL_ERROR, "Failed registering server (%d).", rc);
-		return EEXIST;
-	}
-
-	rc = loc_service_register_with_iface(SERVICE_NAME_INET, &sid,
-	    INET_PORT_DEFAULT);
-	if (rc != EOK) {
-		log_msg(LVL_ERROR, "Failed registering service (%d).", rc);
-		return EEXIST;
-	}
-
-	rc = loc_service_register_with_iface(SERVICE_NAME_INETCFG, &sid,
-	    INET_PORT_CFG);
-	if (rc != EOK) {
-		log_msg(LVL_ERROR, "Failed registering service (%d).", rc);
-		return EEXIST;
-	}
-
-	rc = loc_service_register_with_iface(SERVICE_NAME_INETPING, &sid,
-	    INET_PORT_PING);
-	if (rc != EOK) {
-		log_msg(LVL_ERROR, "Failed registering service (%d).", rc);
-		return EEXIST;
-	}
-
-	rc = inet_link_discovery_start();
-	if (rc != EOK)
-		return EEXIST;
-
-	return EOK;
-}
-
-static void inet_callback_create_srv(inet_client_t *client, ipc_callid_t callid,
-    ipc_call_t *call)
-{
-	log_msg(LVL_DEBUG, "inet_callback_create_srv()");
-
-	async_sess_t *sess = async_callback_receive(EXCHANGE_SERIALIZE);
-	if (sess == NULL) {
-		async_answer_0(callid, ENOMEM);
-		return;
-	}
-
-	client->sess = sess;
-	async_answer_0(callid, EOK);
-}
-
-static int inet_find_dir(inet_addr_t *src, inet_addr_t *dest, uint8_t tos,
-    inet_dir_t *dir)
-{
-	inet_sroute_t *sr;
-
-	/* XXX Handle case where source address is specified */
-	(void) src;
-
-	dir->aobj = inet_addrobj_find(dest, iaf_net);
-	if (dir->aobj != NULL) {
-		dir->ldest = *dest;
-		dir->dtype = dt_direct;
-	} else {
-		/* No direct path, try using a static route */
-		sr = inet_sroute_find(dest);
-		if (sr != NULL) {
-			dir->aobj = inet_addrobj_find(&sr->router, iaf_net);
-			dir->ldest = sr->router;
-			dir->dtype = dt_router;
-		}
-	}
-
-	if (dir->aobj == NULL) {
-		log_msg(LVL_DEBUG, "inet_send: No route to destination.");
-		return ENOENT;
-	}
-
-	return EOK;
-}
-
-int inet_route_packet(inet_dgram_t *dgram, uint8_t proto, uint8_t ttl,
-    int df)
-{
-	inet_dir_t dir;
-	int rc;
-
-	rc = inet_find_dir(&dgram->src, &dgram->dest, dgram->tos, &dir);
-	if (rc != EOK)
-		return rc;
-
-	return inet_addrobj_send_dgram(dir.aobj, &dir.ldest, dgram,
-	    proto, ttl, df);
-}
-
-static int inet_send(inet_client_t *client, inet_dgram_t *dgram,
-    uint8_t proto, uint8_t ttl, int df)
-{
-	return inet_route_packet(dgram, proto, ttl, df);
-}
-
-int inet_get_srcaddr(inet_addr_t *remote, uint8_t tos, inet_addr_t *local)
-{
-	inet_dir_t dir;
-	int rc;
-
-	rc = inet_find_dir(NULL, remote, tos, &dir);
-	if (rc != EOK)
-		return rc;
-
-	/* XXX dt_local? */
-
-	/* Take source address from the address object */
-	local->ipv4 = dir.aobj->naddr.ipv4;
-	return EOK;
-}
-
-static void inet_get_srcaddr_srv(inet_client_t *client, ipc_callid_t callid,
-    ipc_call_t *call)
-{
-	inet_addr_t remote;
-	uint8_t tos;
-	inet_addr_t local;
-	int rc;
-
-	log_msg(LVL_DEBUG, "inet_get_srcaddr_srv()");
-
-	remote.ipv4 = IPC_GET_ARG1(*call);
-	tos = IPC_GET_ARG2(*call);
-	local.ipv4 = 0;
-
-	rc = inet_get_srcaddr(&remote, tos, &local);
-	async_answer_1(callid, rc, local.ipv4);
-}
-
-static void inet_send_srv(inet_client_t *client, ipc_callid_t callid,
-    ipc_call_t *call)
-{
-	inet_dgram_t dgram;
-	uint8_t ttl;
-	int df;
-	int rc;
-
-	log_msg(LVL_DEBUG, "inet_send_srv()");
-
-	dgram.src.ipv4 = IPC_GET_ARG1(*call);
-	dgram.dest.ipv4 = IPC_GET_ARG2(*call);
-	dgram.tos = IPC_GET_ARG3(*call);
-	ttl = IPC_GET_ARG4(*call);
-	df = IPC_GET_ARG5(*call);
-
-	rc = async_data_write_accept(&dgram.data, false, 0, 0, 0, &dgram.size);
-	if (rc != EOK) {
-		async_answer_0(callid, rc);
-		return;
-	}
-
-	rc = inet_send(client, &dgram, client->protocol, ttl, df);
-
-	free(dgram.data);
-	async_answer_0(callid, rc);
-}
-
-static void inet_set_proto_srv(inet_client_t *client, ipc_callid_t callid,
-    ipc_call_t *call)
-{
-	sysarg_t proto;
-
-	proto = IPC_GET_ARG1(*call);
-	log_msg(LVL_DEBUG, "inet_set_proto_srv(%lu)", (unsigned long) proto);
-
-	if (proto > UINT8_MAX) {
-		async_answer_0(callid, EINVAL);
-		return;
-	}
-
-	client->protocol = proto;
-	async_answer_0(callid, EOK);
-}
-
-static void inet_client_init(inet_client_t *client)
-{
-	client->sess = NULL;
-
-	fibril_mutex_lock(&client_list_lock);
-	list_append(&client->client_list, &client_list);
-	fibril_mutex_unlock(&client_list_lock);
-}
-
-static void inet_client_fini(inet_client_t *client)
-{
-	async_hangup(client->sess);
-	client->sess = NULL;
-
-	fibril_mutex_lock(&client_list_lock);
-	list_remove(&client->client_list);
-	fibril_mutex_unlock(&client_list_lock);
-}
-
-static void inet_default_conn(ipc_callid_t iid, ipc_call_t *icall, void *arg)
-{
-	inet_client_t client;
-
-	log_msg(LVL_DEBUG, "inet_default_conn()");
-
-	/* Accept the connection */
-	async_answer_0(iid, EOK);
-
-	inet_client_init(&client);
-
-	while (true) {
-		ipc_call_t call;
-		ipc_callid_t callid = async_get_call(&call);
-		sysarg_t method = IPC_GET_IMETHOD(call);
-
-		if (!method) {
-			/* The other side has hung up */
-			async_answer_0(callid, EOK);
-			return;
-		}
-
-		switch (method) {
-		case INET_CALLBACK_CREATE:
-			inet_callback_create_srv(&client, callid, &call);
-			break;
-		case INET_GET_SRCADDR:
-			inet_get_srcaddr_srv(&client, callid, &call);
-			break;
-		case INET_SEND:
-			inet_send_srv(&client, callid, &call);
-			break;
-		case INET_SET_PROTO:
-			inet_set_proto_srv(&client, callid, &call);
-			break;
-		default:
-			async_answer_0(callid, EINVAL);
-		}
-	}
-
-	inet_client_fini(&client);
-}
-
-static void inet_client_conn(ipc_callid_t iid, ipc_call_t *icall, void *arg)
-{
-	sysarg_t port;
-
-	port = IPC_GET_ARG1(*icall);
-
-	switch (port) {
-	case INET_PORT_DEFAULT:
-		inet_default_conn(iid, icall, arg);
-		break;
-	case INET_PORT_CFG:
-		inet_cfg_conn(iid, icall, arg);
-		break;
-	case INET_PORT_PING:
-		inetping_conn(iid, icall, arg);
-		break;
-	default:
-		async_answer_0(iid, ENOTSUP);
-		break;
-	}
-}
-
-static inet_client_t *inet_client_find(uint8_t proto)
-{
-	fibril_mutex_lock(&client_list_lock);
-
-	list_foreach(client_list, link) {
-		inet_client_t *client = list_get_instance(link, inet_client_t,
-		    client_list);
-
-		if (client->protocol == proto) {
-			fibril_mutex_unlock(&client_list_lock);
-			return client;
-		}
-	}
-
-	fibril_mutex_unlock(&client_list_lock);
-	return NULL;
-}
-
-int inet_ev_recv(inet_client_t *client, inet_dgram_t *dgram)
-{
-	async_exch_t *exch = async_exchange_begin(client->sess);
-
-	ipc_call_t answer;
-	aid_t req = async_send_3(exch, INET_EV_RECV, dgram->src.ipv4,
-	    dgram->dest.ipv4, dgram->tos, &answer);
-	int 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);
-	if (retval != EOK)
-		return retval;
-
-	return EOK;
-}
-
-int inet_recv_dgram_local(inet_dgram_t *dgram, uint8_t proto)
-{
-	inet_client_t *client;
-
-	log_msg(LVL_DEBUG, "inet_recv_dgram_local()");
-
-	/* ICMP messages are handled internally */
-	if (proto == IP_PROTO_ICMP)
-		return icmp_recv(dgram);
-
-	client = inet_client_find(proto);
-	if (client == NULL) {
-		log_msg(LVL_DEBUG, "No client found for protocol 0x%" PRIx8,
-		    proto);
-		return ENOENT;
-	}
-
-	return inet_ev_recv(client, dgram);
-}
-
-int inet_recv_packet(inet_packet_t *packet)
-{
-	inet_addrobj_t *addr;
-	inet_dgram_t dgram;
-
-	addr = inet_addrobj_find(&packet->dest, iaf_addr);
-	if (addr != NULL) {
-		/* Destined for one of the local addresses */
-
-		/* Check if packet is a complete datagram */
-		if (packet->offs == 0 && !packet->mf) {
-			/* It is complete deliver it immediately */
-			dgram.src = packet->src;
-			dgram.dest = packet->dest;
-			dgram.tos = packet->tos;
-			dgram.data = packet->data;
-			dgram.size = packet->size;
-
-			return inet_recv_dgram_local(&dgram, packet->proto);
-		} else {
-			/* It is a fragment, queue it for reassembly */
-			inet_reass_queue_packet(packet);
-		}
-	}
-
-	return ENOENT;
-}
-
-int main(int argc, char *argv[])
-{
-	int rc;
-
-	printf(NAME ": HelenOS Internet Protocol service\n");
-
-	if (log_init(NAME, LVL_WARN) != EOK) {
-		printf(NAME ": Failed to initialize logging.\n");
-		return 1;
-	}
-
-	rc = inet_init();
-	if (rc != EOK)
-		return 1;
-
-	printf(NAME ": Accepting connections.\n");
-	task_retval(0);
-	async_manager();
-
-	/* Not reached */
-	return 0;
-}
-
-/** @}
- */
Index: pace/srv/net/inet/inet.h
===================================================================
--- uspace/srv/net/inet/inet.h	(revision 6df220289417a3a5f0a319426564833d6f527370)
+++ 	(revision )
@@ -1,202 +1,0 @@
-/*
- * 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 INET_H_
-#define INET_H_
-
-#include <adt/list.h>
-#include <bool.h>
-#include <inet/iplink.h>
-#include <ipc/loc.h>
-#include <sys/types.h>
-#include <async.h>
-
-/** Inet Client */
-typedef struct {
-	async_sess_t *sess;
-	uint8_t protocol;
-	link_t client_list;
-} inet_client_t;
-
-/** Inetping Client */
-typedef struct {
-	/** Callback session */
-	async_sess_t *sess;
-	/** Session identifier */
-	uint16_t ident;
-	/** Link to client list */
-	link_t client_list;
-} inetping_client_t;
-
-/** Host address */
-typedef struct {
-	uint32_t ipv4;
-} inet_addr_t;
-
-/** Network address */
-typedef struct {
-	/** Address */
-	uint32_t ipv4;
-	/** Number of valid bits in @c ipv4 */
-	int bits;
-} inet_naddr_t;
-
-/** Address object info */
-typedef struct {
-	/** Network address */
-	inet_naddr_t naddr;
-	/** Link service ID */
-	sysarg_t ilink;
-	/** Address object name */
-	char *name;
-} inet_addr_info_t;
-
-/** IP link info */
-typedef struct {
-	/** Link service name */
-	char *name;
-	/** Default MTU */
-	size_t def_mtu;
-} inet_link_info_t;
-
-/** Static route info */
-typedef struct {
-	/** Destination network address */
-	inet_naddr_t dest;
-	/** Router address */
-	inet_addr_t router;
-	/** Static route name */
-	char *name;
-} inet_sroute_info_t;
-
-typedef struct {
-	/** Source address */
-	inet_addr_t src;
-	/** Destination address */
-	inet_addr_t dest;
-	/** Type of service */
-	uint8_t tos;
-	/** Protocol */
-	uint8_t proto;
-	/** Time to live */
-	uint8_t ttl;
-	/** Identifier */
-	uint16_t ident;
-	/** Do not fragment */
-	bool df;
-	/** More fragments */
-	bool mf;
-	/** Offset of fragment into datagram, in bytes */
-	size_t offs;
-	/** Packet data */
-	void *data;
-	/** Packet data size in bytes */
-	size_t size;
-} inet_packet_t;
-
-typedef struct {
-	inet_addr_t src;
-	inet_addr_t dest;
-	uint8_t tos;
-	void *data;
-	size_t size;
-} inet_dgram_t;
-
-typedef struct {
-	link_t link_list;
-	service_id_t svc_id;
-	char *svc_name;
-	async_sess_t *sess;
-	iplink_t *iplink;
-	size_t def_mtu;
-} inet_link_t;
-
-typedef struct {
-	link_t addr_list;
-	sysarg_t id;
-	inet_naddr_t naddr;
-	inet_link_t *ilink;
-	char *name;
-} inet_addrobj_t;
-
-/** Static route configuration */
-typedef struct {
-	link_t sroute_list;
-	sysarg_t id;
-	/** Destination network */
-	inet_naddr_t dest;
-	/** Router via which to route packets */
-	inet_addr_t router;
-	char *name;
-} inet_sroute_t;
-
-typedef enum {
-	/** Destination is on this network node */
-	dt_local,
-	/** Destination is directly reachable */
-	dt_direct,
-	/** Destination is behind a router */
-	dt_router
-} inet_dir_type_t;
-
-/** Direction (next hop) to a destination */
-typedef struct {
-	/** Route type */
-	inet_dir_type_t dtype;
-	/** Address object (direction) */
-	inet_addrobj_t *aobj;
-	/** Local destination address */
-	inet_addr_t ldest;
-} inet_dir_t;
-
-typedef struct {
-	inet_addr_t src;
-	inet_addr_t dest;
-	uint16_t seq_no;
-	void *data;
-	size_t size;
-} inetping_sdu_t;
-
-extern int inet_ev_recv(inet_client_t *, inet_dgram_t *);
-extern int inet_recv_packet(inet_packet_t *);
-extern int inet_route_packet(inet_dgram_t *, uint8_t, uint8_t, int);
-extern int inet_get_srcaddr(inet_addr_t *, uint8_t, inet_addr_t *);
-extern int inet_recv_dgram_local(inet_dgram_t *, uint8_t);
-
-#endif
-
-/** @}
- */
Index: pace/srv/net/inet/inet_link.c
===================================================================
--- uspace/srv/net/inet/inet_link.c	(revision 6df220289417a3a5f0a319426564833d6f527370)
+++ 	(revision )
@@ -1,309 +1,0 @@
-/*
- * 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 <bool.h>
-#include <errno.h>
-#include <fibril_synch.h>
-#include <inet/iplink.h>
-#include <io/log.h>
-#include <loc.h>
-#include <stdlib.h>
-#include <str.h>
-
-#include "addrobj.h"
-#include "inet.h"
-#include "inet_link.h"
-#include "pdu.h"
-
-static int inet_link_open(service_id_t sid);
-static int inet_iplink_recv(iplink_t *ilink, iplink_sdu_t *sdu);
-
-static iplink_ev_ops_t inet_iplink_ev_ops = {
-	.recv = inet_iplink_recv
-};
-
-static LIST_INITIALIZE(inet_link_list);
-static FIBRIL_MUTEX_INITIALIZE(inet_discovery_lock);
-
-static int inet_iplink_recv(iplink_t *iplink, iplink_sdu_t *sdu)
-{
-	inet_packet_t packet;
-	int rc;
-
-	log_msg(LVL_DEBUG, "inet_iplink_recv()");
-	rc = inet_pdu_decode(sdu->data, sdu->size, &packet);
-	if (rc != EOK) {
-		log_msg(LVL_DEBUG, "failed decoding PDU");
-		return rc;
-	}
-
-	log_msg(LVL_DEBUG, "call inet_recv_packet()");
-	rc = inet_recv_packet(&packet);
-	log_msg(LVL_DEBUG, "call inet_recv_packet -> %d", rc);
-	free(packet.data);
-
-	return rc;
-}
-
-static int inet_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(&inet_discovery_lock);
-
-	rc = loc_category_get_id("iplink", &iplink_cat, IPC_FLAG_BLOCKING);
-	if (rc != EOK) {
-		log_msg(LVL_ERROR, "Failed resolving category 'iplink'.");
-		fibril_mutex_unlock(&inet_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(&inet_discovery_lock);
-		return EIO;
-	}
-
-	for (i = 0; i < count; i++) {
-		already_known = false;
-
-		list_foreach(inet_link_list, ilink_link) {
-			inet_link_t *ilink = list_get_instance(ilink_link,
-			    inet_link_t, link_list);
-			if (ilink->svc_id == svcs[i]) {
-				already_known = true;
-				break;
-			}
-		}
-
-		if (!already_known) {
-			log_msg(LVL_DEBUG, "Found IP link '%lu'",
-			    (unsigned long) svcs[i]);
-			rc = inet_link_open(svcs[i]);
-			if (rc != EOK)
-				log_msg(LVL_ERROR, "Could not open IP link.");
-		}
-	}
-
-	fibril_mutex_unlock(&inet_discovery_lock);
-	return EOK;
-}
-
-static inet_link_t *inet_link_new(void)
-{
-	inet_link_t *ilink = calloc(1, sizeof(inet_link_t));
-
-	if (ilink == NULL) {
-		log_msg(LVL_ERROR, "Failed allocating link structure. "
-		    "Out of memory.");
-		return NULL;
-	}
-
-	link_initialize(&ilink->link_list);
-
-	return ilink;
-}
-
-static void inet_link_delete(inet_link_t *ilink)
-{
-	if (ilink->svc_name != NULL)
-		free(ilink->svc_name);
-	free(ilink);
-}
-
-static int inet_link_open(service_id_t sid)
-{
-	inet_link_t *ilink;
-	iplink_addr_t iaddr;
-	int rc;
-
-	log_msg(LVL_DEBUG, "inet_link_open()");
-	ilink = inet_link_new();
-	if (ilink == NULL)
-		return ENOMEM;
-
-	ilink->svc_id = sid;
-	ilink->iplink = NULL;
-
-	rc = loc_service_get_name(sid, &ilink->svc_name);
-	if (rc != EOK) {
-		log_msg(LVL_ERROR, "Failed getting service name.");
-		goto error;
-	}
-
-	ilink->sess = loc_service_connect(EXCHANGE_SERIALIZE, sid, 0);
-	if (ilink->sess == NULL) {
-		log_msg(LVL_ERROR, "Failed connecting '%s'", ilink->svc_name);
-		goto error;
-	}
-
-	rc = iplink_open(ilink->sess, &inet_iplink_ev_ops, &ilink->iplink);
-	if (rc != EOK) {
-		log_msg(LVL_ERROR, "Failed opening IP link '%s'",
-		    ilink->svc_name);
-		goto error;
-	}
-
-	rc = iplink_get_mtu(ilink->iplink, &ilink->def_mtu);
-	if (rc != EOK) {
-		log_msg(LVL_ERROR, "Failed determinning MTU of link '%s'",
-		    ilink->svc_name);
-		goto error;
-	}
-
-	log_msg(LVL_DEBUG, "Opened IP link '%s'", ilink->svc_name);
-	list_append(&ilink->link_list, &inet_link_list);
-
-	inet_addrobj_t *addr;
-
-	static int first = 1;
-	/* XXX For testing: set static IP address 192.168.0.4/24 */
-	addr = inet_addrobj_new();
-	if (first) {
-		addr->naddr.ipv4 = (127 << 24) + (0 << 16) + (0 << 8) + 1;
-		first = 0;
-	} else {
-		addr->naddr.ipv4 = (192 << 24) + (168 << 16) + (0 << 8) + 4;
-	}
-	addr->naddr.bits = 24;
-	addr->ilink = ilink;
-	addr->name = str_dup("v4a");
-	inet_addrobj_add(addr);
-
-	iaddr.ipv4 = addr->naddr.ipv4;
-	rc = iplink_addr_add(ilink->iplink, &iaddr);
-	if (rc != EOK) {
-		log_msg(LVL_ERROR, "Failed setting IP address on internet link.");
-		/* XXX Roll back */
-		return rc;
-	}
-
-	return EOK;
-
-error:
-	if (ilink->iplink != NULL)
-		iplink_close(ilink->iplink);
-	inet_link_delete(ilink);
-	return rc;
-}
-
-static void inet_link_cat_change_cb(void)
-{
-	(void) inet_link_check_new();
-}
-
-int inet_link_discovery_start(void)
-{
-	int rc;
-
-	rc = loc_register_cat_change_cb(inet_link_cat_change_cb);
-	if (rc != EOK) {
-		log_msg(LVL_ERROR, "Failed registering callback for IP link "
-		    "discovery (%d).", rc);
-		return rc;
-	}
-
-	return inet_link_check_new();
-}
-
-/** Send datagram over Internet link */
-int inet_link_send_dgram(inet_link_t *ilink, inet_addr_t *lsrc,
-    inet_addr_t *ldest, inet_dgram_t *dgram, uint8_t proto, uint8_t ttl, int df)
-{
-	iplink_sdu_t sdu;
-	inet_packet_t packet;
-	int rc;
-	size_t offs, roffs;
-
-	/*
-	 * Fill packet structure. Fragmentation is performed by
-	 * inet_pdu_encode().
-	 */
-	packet.src = dgram->src;
-	packet.dest = dgram->dest;
-	packet.tos = dgram->tos;
-	packet.proto = proto;
-	packet.ttl = ttl;
-	packet.df = df;
-	packet.data = dgram->data;
-	packet.size = dgram->size;
-
-	sdu.lsrc.ipv4 = lsrc->ipv4;
-	sdu.ldest.ipv4 = ldest->ipv4;
-
-	offs = 0;
-	do {
-		/* Encode one fragment */
-		rc = inet_pdu_encode(&packet, offs, ilink->def_mtu, &sdu.data,
-		    &sdu.size, &roffs);
-		if (rc != EOK)
-			return rc;
-
-		/* Send the PDU */
-		rc = iplink_send(ilink->iplink, &sdu);
-		free(sdu.data);
-
-		offs = roffs;
-	} while (offs < packet.size);
-
-	return rc;
-}
-
-inet_link_t *inet_link_get_by_id(sysarg_t link_id)
-{
-	fibril_mutex_lock(&inet_discovery_lock);
-
-	list_foreach(inet_link_list, elem) {
-		inet_link_t *ilink = list_get_instance(elem, inet_link_t,
-		    link_list);
-
-		if (ilink->svc_id == link_id) {
-			fibril_mutex_unlock(&inet_discovery_lock);
-			return ilink;
-		}
-	}
-
-	fibril_mutex_unlock(&inet_discovery_lock);
-	return NULL;
-}
-
-/** @}
- */
Index: pace/srv/net/inet/inet_link.h
===================================================================
--- uspace/srv/net/inet/inet_link.h	(revision 6df220289417a3a5f0a319426564833d6f527370)
+++ 	(revision )
@@ -1,51 +1,0 @@
-/*
- * 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 INET_LINK_H_
-#define INET_LINK_H_
-
-#include <sys/types.h>
-#include "inet.h"
-
-extern int inet_link_discovery_start(void);
-extern int inet_link_send_dgram(inet_link_t *, inet_addr_t *,
-    inet_addr_t *, inet_dgram_t *, uint8_t, uint8_t, int);
-extern inet_link_t *inet_link_get_by_id(sysarg_t);
-
-#endif
-
-/** @}
- */
Index: pace/srv/net/inet/inet_std.h
===================================================================
--- uspace/srv/net/inet/inet_std.h	(revision 6df220289417a3a5f0a319426564833d6f527370)
+++ 	(revision )
@@ -1,98 +1,0 @@
-/*
- * 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 IP header definitions
- *
- */
-
-#ifndef INET_STD_H_
-#define INET_STD_H_
-
-#include <sys/types.h>
-
-/** Internet Datagram header (fixed part) */
-typedef struct {
-	/** Version, Internet Header Length */
-	uint8_t ver_ihl;
-	/* Type of Service */
-	uint8_t tos;
-	/** Total Length */
-	uint16_t tot_len;
-	/** Identification */
-	uint16_t id;
-	/** Flags, Fragment Offset */
-	uint16_t flags_foff;
-	/** Time to Live */
-	uint8_t ttl;
-	/** Protocol */
-	uint8_t proto;
-	/** Header Checksum */
-	uint16_t chksum;
-	/** Source Address */
-	uint32_t src_addr;
-	/** Destination Address */
-	uint32_t dest_addr;
-} ip_header_t;
-
-/** Bits in ip_header_t.ver_ihl */
-enum ver_ihl_bits {
-	/** Version, highest bit */
-	VI_VERSION_h = 7,
-	/** Version, lowest bit */
-	VI_VERSION_l = 4,
-	/** Internet Header Length, highest bit */
-	VI_IHL_h     = 3,
-	/** Internet Header Length, lowest bit */
-	VI_IHL_l     = 0
-};
-
-/** Bits in ip_header_t.flags_foff */
-enum flags_foff_bits {
-	/** Reserved, must be zero */
-	FF_FLAG_RSVD = 15,
-	/** Don't Fragment */
-	FF_FLAG_DF = 14,
-	/** More Fragments */
-	FF_FLAG_MF = 13,
-	/** Fragment Offset, highest bit */
-	FF_FRAGOFF_h = 12,
-	/** Fragment Offset, lowest bit */
-	FF_FRAGOFF_l = 0
-};
-
-/** Fragment offset is expressed in units of 8 bytes */
-#define FRAG_OFFS_UNIT 8
-
-#endif
-
-/** @}
- */
Index: pace/srv/net/inet/inet_util.c
===================================================================
--- uspace/srv/net/inet/inet_util.c	(revision 6df220289417a3a5f0a319426564833d6f527370)
+++ 	(revision )
@@ -1,54 +1,0 @@
-/*
- * 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 <assert.h>
-#include <bitops.h>
-#include <sys/types.h>
-#include "inet_util.h"
-
-uint32_t inet_netmask(int bits)
-{
-	assert(bits >= 0);
-	assert(bits < 32);
-
-	if (bits == 0)
-		return 0;
-	else
-		return BIT_RANGE(uint32_t, 31, 31 - (bits - 1));
-}
-
-/** @}
- */
Index: pace/srv/net/inet/inet_util.h
===================================================================
--- uspace/srv/net/inet/inet_util.h	(revision 6df220289417a3a5f0a319426564833d6f527370)
+++ 	(revision )
@@ -1,47 +1,0 @@
-/*
- * 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 INET_UTIL_H_
-#define INET_UTIL_H_
-
-#include <sys/types.h>
-
-uint32_t inet_netmask(int bits);
-
-#endif
-
-/** @}
- */
Index: pace/srv/net/inet/inetcfg.c
===================================================================
--- uspace/srv/net/inet/inetcfg.c	(revision 6df220289417a3a5f0a319426564833d6f527370)
+++ 	(revision )
@@ -1,631 +1,0 @@
-/*
- * 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 <errno.h>
-#include <macros.h>
-#include <io/log.h>
-#include <ipc/inet.h>
-#include <loc.h>
-#include <stdlib.h>
-#include <str.h>
-#include <sys/types.h>
-
-#include "addrobj.h"
-#include "inet.h"
-#include "inet_link.h"
-#include "inetcfg.h"
-#include "sroute.h"
-
-static int inetcfg_addr_create_static(char *name, inet_naddr_t *naddr,
-    sysarg_t link_id, sysarg_t *addr_id)
-{
-	inet_link_t *ilink;
-	inet_addrobj_t *addr;
-	iplink_addr_t iaddr;
-	int rc;
-
-	ilink = inet_link_get_by_id(link_id);
-	if (ilink == NULL) {
-		log_msg(LVL_DEBUG, "Link %lu not found.",
-		    (unsigned long) link_id);
-		return ENOENT;
-	}
-
-	addr = inet_addrobj_new();
-	if (addr == NULL) {
-		*addr_id = 0;
-		return ENOMEM;
-	}
-
-	addr->naddr = *naddr;
-	addr->ilink = ilink;
-	addr->name = str_dup(name);
-	inet_addrobj_add(addr);
-
-	iaddr.ipv4 = addr->naddr.ipv4;
-	rc = iplink_addr_add(ilink->iplink, &iaddr);
-	if (rc != EOK) {
-		log_msg(LVL_ERROR, "Failed setting IP address on internet link.");
-		inet_addrobj_remove(addr);
-		inet_addrobj_delete(addr);
-		return rc;
-	}
-
-	return EOK;
-}
-
-static int inetcfg_addr_delete(sysarg_t addr_id)
-{
-	inet_addrobj_t *addr;
-
-	addr = inet_addrobj_get_by_id(addr_id);
-	if (addr == NULL)
-		return ENOENT;
-
-	inet_addrobj_remove(addr);
-	inet_addrobj_delete(addr);
-
-	return EOK;
-}
-
-static int inetcfg_addr_get(sysarg_t addr_id, inet_addr_info_t *ainfo)
-{
-	inet_addrobj_t *addr;
-
-	addr = inet_addrobj_get_by_id(addr_id);
-	if (addr == NULL)
-		return ENOENT;
-
-	ainfo->naddr = addr->naddr;
-	ainfo->ilink = addr->ilink->svc_id;
-	ainfo->name = str_dup(addr->name);
-
-	return EOK;
-}
-
-static int inetcfg_addr_get_id(char *name, sysarg_t link_id, sysarg_t *addr_id)
-{
-	inet_link_t *ilink;
-	inet_addrobj_t *addr;
-
-	ilink = inet_link_get_by_id(link_id);
-	if (ilink == NULL) {
-		log_msg(LVL_DEBUG, "Link %zu not found.", (size_t) link_id);
-		return ENOENT;
-	}
-
-	addr = inet_addrobj_find_by_name(name, ilink);
-	if (addr == NULL) {
-		log_msg(LVL_DEBUG, "Address '%s' not found.", name);
-		return ENOENT;
-	}
-
-	*addr_id = addr->id;
-	return EOK;
-}
-
-static int inetcfg_get_addr_list(sysarg_t **addrs, size_t *count)
-{
-	return inet_addrobj_get_id_list(addrs, count);
-}
-
-static int inetcfg_get_link_list(sysarg_t **addrs, size_t *count)
-{
-	return ENOTSUP;
-}
-
-static int inetcfg_get_sroute_list(sysarg_t **sroutes, size_t *count)
-{
-	return inet_sroute_get_id_list(sroutes, count);
-}
-
-static int inetcfg_link_get(sysarg_t link_id, inet_link_info_t *linfo)
-{
-	inet_link_t *ilink;
-
-	ilink = inet_link_get_by_id(link_id);
-	if (ilink == NULL) {
-		return ENOENT;
-	}
-
-	linfo->name = str_dup(ilink->svc_name);
-	linfo->def_mtu = ilink->def_mtu;
-	return EOK;
-}
-
-static int inetcfg_sroute_create(char *name, inet_naddr_t *dest,
-    inet_addr_t *router, sysarg_t *sroute_id)
-{
-	inet_sroute_t *sroute;
-
-	sroute = inet_sroute_new();
-	if (sroute == NULL) {
-		*sroute_id = 0;
-		return ENOMEM;
-	}
-
-	sroute->dest = *dest;
-	sroute->router = *router;
-	sroute->name = str_dup(name);
-	inet_sroute_add(sroute);
-
-	*sroute_id = sroute->id;
-	return EOK;
-}
-
-static int inetcfg_sroute_delete(sysarg_t sroute_id)
-{
-	inet_sroute_t *sroute;
-
-	sroute = inet_sroute_get_by_id(sroute_id);
-	if (sroute == NULL)
-		return ENOENT;
-
-	inet_sroute_remove(sroute);
-	inet_sroute_delete(sroute);
-
-	return EOK;
-}
-
-static int inetcfg_sroute_get(sysarg_t sroute_id, inet_sroute_info_t *srinfo)
-{
-	inet_sroute_t *sroute;
-
-	sroute = inet_sroute_get_by_id(sroute_id);
-	if (sroute == NULL)
-		return ENOENT;
-
-	srinfo->dest = sroute->dest;
-	srinfo->router = sroute->router;
-	srinfo->name = str_dup(sroute->name);
-
-	return EOK;
-}
-
-static int inetcfg_sroute_get_id(char *name, sysarg_t *sroute_id)
-{
-	inet_sroute_t *sroute;
-
-	sroute = inet_sroute_find_by_name(name);
-	if (sroute == NULL) {
-		log_msg(LVL_DEBUG, "Static route '%s' not found.", name);
-		return ENOENT;
-	}
-
-	*sroute_id = sroute->id;
-	return EOK;
-}
-
-static void inetcfg_addr_create_static_srv(ipc_callid_t callid,
-    ipc_call_t *call)
-{
-	char *name;
-	inet_naddr_t naddr;
-	sysarg_t link_id;
-	sysarg_t addr_id;
-	int rc;
-
-	log_msg(LVL_DEBUG, "inetcfg_addr_create_static_srv()");
-
-	rc = async_data_write_accept((void **) &name, true, 0, LOC_NAME_MAXLEN,
-	    0, NULL);
-	if (rc != EOK) {
-		async_answer_0(callid, rc);
-		return;
-	}
-
-	naddr.ipv4 = IPC_GET_ARG1(*call);
-	naddr.bits = IPC_GET_ARG2(*call);
-	link_id    = IPC_GET_ARG3(*call);
-
-	addr_id = 0;
-	rc = inetcfg_addr_create_static(name, &naddr, link_id, &addr_id);
-	free(name);
-	async_answer_1(callid, rc, addr_id);
-}
-
-static void inetcfg_addr_delete_srv(ipc_callid_t callid, ipc_call_t *call)
-{
-	sysarg_t addr_id;
-	int rc;
-
-	log_msg(LVL_DEBUG, "inetcfg_addr_delete_srv()");
-
-	addr_id = IPC_GET_ARG1(*call);
-
-	rc = inetcfg_addr_delete(addr_id);
-	async_answer_0(callid, rc);
-}
-
-static void inetcfg_addr_get_srv(ipc_callid_t callid, ipc_call_t *call)
-{
-	ipc_callid_t rcallid;
-	size_t max_size;
-
-	sysarg_t addr_id;
-	inet_addr_info_t ainfo;
-	int rc;
-
-	addr_id = IPC_GET_ARG1(*call);
-	log_msg(LVL_DEBUG, "inetcfg_addr_get_srv()");
-
-	ainfo.naddr.ipv4 = 0;
-	ainfo.naddr.bits = 0;
-	ainfo.ilink = 0;
-	ainfo.name = NULL;
-
-	if (!async_data_read_receive(&rcallid, &max_size)) {
-		async_answer_0(rcallid, EREFUSED);
-		async_answer_0(callid, EREFUSED);
-		return;
-	}
-
-	rc = inetcfg_addr_get(addr_id, &ainfo);
-	if (rc != EOK) {
-		async_answer_0(callid, rc);
-		return;
-	}
-
-	sysarg_t retval = async_data_read_finalize(rcallid, ainfo.name,
-	    min(max_size, str_size(ainfo.name)));
-	free(ainfo.name);
-
-	async_answer_3(callid, retval, ainfo.naddr.ipv4, ainfo.naddr.bits,
-	    ainfo.ilink);
-}
-
-static void inetcfg_addr_get_id_srv(ipc_callid_t callid, ipc_call_t *call)
-{
-	char *name;
-	sysarg_t link_id;
-	sysarg_t addr_id;
-	int rc;
-
-	log_msg(LVL_DEBUG, "inetcfg_addr_get_id_srv()");
-
-	link_id = IPC_GET_ARG1(*call);
-
-	rc = async_data_write_accept((void **) &name, true, 0, LOC_NAME_MAXLEN,
-	    0, NULL);
-	if (rc != EOK) {
-		async_answer_0(callid, rc);
-		return;
-	}
-
-	addr_id = 0;
-	rc = inetcfg_addr_get_id(name, link_id, &addr_id);
-	free(name);
-	async_answer_1(callid, rc, addr_id);
-}
-
-static void inetcfg_get_addr_list_srv(ipc_callid_t callid, ipc_call_t *call)
-{
-	ipc_callid_t rcallid;
-	size_t count;
-	size_t max_size;
-	size_t act_size;
-	size_t size;
-	sysarg_t *id_buf;
-	int rc;
-
-	log_msg(LVL_DEBUG, "inetcfg_get_addr_list_srv()");
-
-	if (!async_data_read_receive(&rcallid, &max_size)) {
-		async_answer_0(rcallid, EREFUSED);
-		async_answer_0(callid, EREFUSED);
-		return;
-	}
-
-	rc = inetcfg_get_addr_list(&id_buf, &count);
-	if (rc != EOK) {
-		async_answer_0(rcallid, rc);
-		async_answer_0(callid, rc);
-		return;
-	}
-
-	act_size = count * sizeof(sysarg_t);
-	size = min(act_size, max_size);
-
-	sysarg_t retval = async_data_read_finalize(rcallid, id_buf, size);
-	free(id_buf);
-
-	async_answer_1(callid, retval, act_size);
-}
-
-
-static void inetcfg_get_link_list_srv(ipc_callid_t callid, ipc_call_t *call)
-{
-	ipc_callid_t rcallid;
-	size_t max_size;
-	size_t act_size;
-	size_t size;
-	sysarg_t *id_buf;
-	int rc;
-
-	log_msg(LVL_DEBUG, "inetcfg_get_link_list_srv()");
-
-	if (!async_data_read_receive(&rcallid, &max_size)) {
-		async_answer_0(rcallid, EREFUSED);
-		async_answer_0(callid, EREFUSED);
-		return;
-	}
-
-	rc = inetcfg_get_link_list(&id_buf, &act_size);
-	if (rc != EOK) {
-		async_answer_0(rcallid, rc);
-		async_answer_0(callid, rc);
-		return;
-	}
-
-	size = min(act_size, max_size);
-
-	sysarg_t retval = async_data_read_finalize(rcallid, id_buf, size);
-	free(id_buf);
-
-	async_answer_1(callid, retval, act_size);
-}
-
-static void inetcfg_get_sroute_list_srv(ipc_callid_t callid, ipc_call_t *call)
-{
-	ipc_callid_t rcallid;
-	size_t count;
-	size_t max_size;
-	size_t act_size;
-	size_t size;
-	sysarg_t *id_buf;
-	int rc;
-
-	log_msg(LVL_DEBUG, "inetcfg_get_sroute_list_srv()");
-
-	if (!async_data_read_receive(&rcallid, &max_size)) {
-		async_answer_0(rcallid, EREFUSED);
-		async_answer_0(callid, EREFUSED);
-		return;
-	}
-
-	rc = inetcfg_get_sroute_list(&id_buf, &count);
-	if (rc != EOK) {
-		async_answer_0(rcallid, rc);
-		async_answer_0(callid, rc);
-		return;
-	}
-
-	act_size = count * sizeof(sysarg_t);
-	size = min(act_size, max_size);
-
-	sysarg_t retval = async_data_read_finalize(rcallid, id_buf, size);
-	free(id_buf);
-
-	async_answer_1(callid, retval, act_size);
-}
-
-static void inetcfg_link_get_srv(ipc_callid_t callid, ipc_call_t *call)
-{
-	ipc_callid_t rcallid;
-	size_t max_size;
-
-	sysarg_t link_id;
-	inet_link_info_t linfo;
-	int rc;
-
-	link_id = IPC_GET_ARG1(*call);
-	log_msg(LVL_DEBUG, "inetcfg_link_get_srv()");
-
-	linfo.name = NULL;
-
-	if (!async_data_read_receive(&rcallid, &max_size)) {
-		async_answer_0(rcallid, EREFUSED);
-		async_answer_0(callid, EREFUSED);
-		return;
-	}
-
-	rc = inetcfg_link_get(link_id, &linfo);
-	if (rc != EOK) {
-		async_answer_0(rcallid, rc);
-		async_answer_0(callid, rc);
-		return;
-	}
-
-	sysarg_t retval = async_data_read_finalize(rcallid, linfo.name,
-	    min(max_size, str_size(linfo.name)));
-	free(linfo.name);
-
-	async_answer_1(callid, retval, linfo.def_mtu);
-}
-
-static void inetcfg_sroute_create_srv(ipc_callid_t callid,
-    ipc_call_t *call)
-{
-	char *name;
-	inet_naddr_t dest;
-	inet_addr_t router;
-	sysarg_t sroute_id;
-	int rc;
-
-	log_msg(LVL_DEBUG, "inetcfg_sroute_create_srv()");
-
-	rc = async_data_write_accept((void **) &name, true, 0, LOC_NAME_MAXLEN,
-	    0, NULL);
-	if (rc != EOK) {
-		async_answer_0(callid, rc);
-		return;
-	}
-
-	dest.ipv4   = IPC_GET_ARG1(*call);
-	dest.bits   = IPC_GET_ARG2(*call);
-	router.ipv4 = IPC_GET_ARG3(*call);
-
-	sroute_id = 0;
-	rc = inetcfg_sroute_create(name, &dest, &router, &sroute_id);
-	free(name);
-	async_answer_1(callid, rc, sroute_id);
-}
-
-static void inetcfg_sroute_delete_srv(ipc_callid_t callid, ipc_call_t *call)
-{
-	sysarg_t sroute_id;
-	int rc;
-
-	log_msg(LVL_DEBUG, "inetcfg_sroute_delete_srv()");
-
-	sroute_id = IPC_GET_ARG1(*call);
-
-	rc = inetcfg_sroute_delete(sroute_id);
-	async_answer_0(callid, rc);
-}
-
-static void inetcfg_sroute_get_srv(ipc_callid_t callid, ipc_call_t *call)
-{
-	ipc_callid_t rcallid;
-	size_t max_size;
-
-	sysarg_t sroute_id;
-	inet_sroute_info_t srinfo;
-	int rc;
-
-	sroute_id = IPC_GET_ARG1(*call);
-	log_msg(LVL_DEBUG, "inetcfg_sroute_get_srv()");
-
-	srinfo.dest.ipv4 = 0;
-	srinfo.dest.bits = 0;
-	srinfo.router.ipv4 = 0;
-	srinfo.name = NULL;
-
-	if (!async_data_read_receive(&rcallid, &max_size)) {
-		async_answer_0(rcallid, EREFUSED);
-		async_answer_0(callid, EREFUSED);
-		return;
-	}
-
-	rc = inetcfg_sroute_get(sroute_id, &srinfo);
-	if (rc != EOK) {
-		async_answer_0(callid, rc);
-		return;
-	}
-
-	sysarg_t retval = async_data_read_finalize(rcallid, srinfo.name,
-	    min(max_size, str_size(srinfo.name)));
-	free(srinfo.name);
-
-	async_answer_3(callid, retval, srinfo.dest.ipv4, srinfo.dest.bits,
-	    srinfo.router.ipv4);
-}
-
-static void inetcfg_sroute_get_id_srv(ipc_callid_t callid, ipc_call_t *call)
-{
-	char *name;
-	sysarg_t sroute_id;
-	int rc;
-
-	log_msg(LVL_DEBUG, "inetcfg_sroute_get_id_srv()");
-
-	rc = async_data_write_accept((void **) &name, true, 0, LOC_NAME_MAXLEN,
-	    0, NULL);
-	if (rc != EOK) {
-		async_answer_0(callid, rc);
-		return;
-	}
-
-	sroute_id = 0;
-	rc = inetcfg_sroute_get_id(name, &sroute_id);
-	free(name);
-	async_answer_1(callid, rc, sroute_id);
-}
-
-void inet_cfg_conn(ipc_callid_t iid, ipc_call_t *icall, void *arg)
-{
-	log_msg(LVL_DEBUG, "inet_cfg_conn()");
-
-	/* Accept the connection */
-	async_answer_0(iid, EOK);
-
-	while (true) {
-		ipc_call_t call;
-		ipc_callid_t callid = async_get_call(&call);
-		sysarg_t method = IPC_GET_IMETHOD(call);
-
-		if (!method) {
-			/* The other side has hung up */
-			async_answer_0(callid, EOK);
-			return;
-		}
-
-		switch (method) {
-		case INETCFG_ADDR_CREATE_STATIC:
-			inetcfg_addr_create_static_srv(callid, &call);
-			break;
-		case INETCFG_ADDR_DELETE:
-			inetcfg_addr_delete_srv(callid, &call);
-			break;
-		case INETCFG_ADDR_GET:
-			inetcfg_addr_get_srv(callid, &call);
-			break;
-		case INETCFG_ADDR_GET_ID:
-			inetcfg_addr_get_id_srv(callid, &call);
-			break;
-		case INETCFG_GET_ADDR_LIST:
-			inetcfg_get_addr_list_srv(callid, &call);
-			break;
-		case INETCFG_GET_LINK_LIST:
-			inetcfg_get_link_list_srv(callid, &call);
-			break;
-		case INETCFG_GET_SROUTE_LIST:
-			inetcfg_get_sroute_list_srv(callid, &call);
-			break;
-		case INETCFG_LINK_GET:
-			inetcfg_link_get_srv(callid, &call);
-			break;
-		case INETCFG_SROUTE_CREATE:
-			inetcfg_sroute_create_srv(callid, &call);
-			break;
-		case INETCFG_SROUTE_DELETE:
-			inetcfg_sroute_delete_srv(callid, &call);
-			break;
-		case INETCFG_SROUTE_GET:
-			inetcfg_sroute_get_srv(callid, &call);
-			break;
-		case INETCFG_SROUTE_GET_ID:
-			inetcfg_sroute_get_id_srv(callid, &call);
-			break;
-		default:
-			async_answer_0(callid, EINVAL);
-		}
-	}
-}
-
-/** @}
- */
Index: pace/srv/net/inet/inetcfg.h
===================================================================
--- uspace/srv/net/inet/inetcfg.h	(revision 6df220289417a3a5f0a319426564833d6f527370)
+++ 	(revision )
@@ -1,45 +1,0 @@
-/*
- * 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 INETCFG_H_
-#define INETCFG_H_
-
-extern void inet_cfg_conn(ipc_callid_t, ipc_call_t *, void *);
-
-#endif
-
-/** @}
- */
Index: pace/srv/net/inet/inetping.c
===================================================================
--- uspace/srv/net/inet/inetping.c	(revision 6df220289417a3a5f0a319426564833d6f527370)
+++ 	(revision )
@@ -1,230 +1,0 @@
-/*
- * 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 <errno.h>
-#include <fibril_synch.h>
-#include <io/log.h>
-#include <ipc/inet.h>
-#include <loc.h>
-#include <stdlib.h>
-#include <sys/types.h>
-
-#include "icmp.h"
-#include "icmp_std.h"
-#include "inet.h"
-#include "inetping.h"
-
-static FIBRIL_MUTEX_INITIALIZE(client_list_lock);
-static LIST_INITIALIZE(client_list);
-
-/** Last used session identifier. Protected by @c client_list_lock */
-static uint16_t inetping_ident = 0;
-
-static inetping_client_t *inetping_client_find(uint16_t);
-
-static int inetping_send(inetping_client_t *client, inetping_sdu_t *sdu)
-{
-	return icmp_ping_send(client->ident, sdu);
-}
-
-static int inetping_get_srcaddr(inetping_client_t *client, inet_addr_t *remote,
-    inet_addr_t *local)
-{
-	return inet_get_srcaddr(remote, ICMP_TOS, local);
-}
-
-int inetping_recv(uint16_t ident, inetping_sdu_t *sdu)
-{
-	inetping_client_t *client;
-	async_exch_t *exch;
-	ipc_call_t answer;
-
-	client = inetping_client_find(ident);
-	if (client == NULL) {
-		log_msg(LVL_DEBUG, "Unknown ICMP ident. Dropping.");
-		return ENOENT;
-	}
-
-	exch = async_exchange_begin(client->sess);
-
-	aid_t req = async_send_3(exch, INETPING_EV_RECV, sdu->src.ipv4,
-	    sdu->dest.ipv4, sdu->seq_no, &answer);
-	int rc = async_data_write_start(exch, sdu->data, sdu->size);
-	async_exchange_end(exch);
-
-	if (rc != EOK) {
-		async_forget(req);
-		return rc;
-	}
-
-	sysarg_t retval;
-	async_wait_for(req, &retval);
-	if (retval != EOK) {
-		return retval;
-	}
-
-	return EOK;
-}
-
-static void inetping_send_srv(inetping_client_t *client, ipc_callid_t callid,
-    ipc_call_t *call)
-{
-	inetping_sdu_t sdu;
-	int rc;
-
-	log_msg(LVL_DEBUG, "inetping_send_srv()");
-
-	rc = async_data_write_accept((void **) &sdu.data, false, 0, 0, 0,
-	    &sdu.size);
-	if (rc != EOK) {
-		async_answer_0(callid, rc);
-		return;
-	}
-
-	sdu.src.ipv4 = IPC_GET_ARG1(*call);
-	sdu.dest.ipv4 = IPC_GET_ARG2(*call);
-	sdu.seq_no = IPC_GET_ARG3(*call);
-
-	rc = inetping_send(client, &sdu);
-	free(sdu.data);
-
-	async_answer_0(callid, rc);
-}
-
-static void inetping_get_srcaddr_srv(inetping_client_t *client,
-    ipc_callid_t callid, ipc_call_t *call)
-{
-	inet_addr_t remote;
-	inet_addr_t local;
-	int rc;
-
-	log_msg(LVL_DEBUG, "inetping_get_srcaddr_srv()");
-
-	remote.ipv4 = IPC_GET_ARG1(*call);
-	local.ipv4 = 0;
-
-	rc = inetping_get_srcaddr(client, &remote, &local);
-	async_answer_1(callid, rc, local.ipv4);
-}
-
-static int inetping_client_init(inetping_client_t *client)
-{
-	async_sess_t *sess = async_callback_receive(EXCHANGE_SERIALIZE);
-	if (sess == NULL)
-		return ENOMEM;
-
-	client->sess = sess;
-	link_initialize(&client->client_list);
-
-	fibril_mutex_lock(&client_list_lock);
-	client->ident = ++inetping_ident;
-	list_append(&client->client_list, &client_list);
-	fibril_mutex_unlock(&client_list_lock);
-
-	return EOK;
-}
-
-static void inetping_client_fini(inetping_client_t *client)
-{
-	async_hangup(client->sess);
-	client->sess = NULL;
-
-	fibril_mutex_lock(&client_list_lock);
-	list_remove(&client->client_list);
-	fibril_mutex_unlock(&client_list_lock);
-}
-
-static inetping_client_t *inetping_client_find(uint16_t ident)
-{
-	fibril_mutex_lock(&client_list_lock);
-
-	list_foreach(client_list, link) {
-		inetping_client_t *client = list_get_instance(link,
-		    inetping_client_t, client_list);
-
-		if (client->ident == ident) {
-			fibril_mutex_unlock(&client_list_lock);
-			return client;
-		}
-	}
-
-	fibril_mutex_unlock(&client_list_lock);
-	return NULL;
-}
-
-void inetping_conn(ipc_callid_t iid, ipc_call_t *icall, void *arg)
-{
-	inetping_client_t client;
-	int rc;
-
-	log_msg(LVL_DEBUG, "inetping_conn()");
-
-	/* Accept the connection */
-	async_answer_0(iid, EOK);
-
-	rc = inetping_client_init(&client);
-	if (rc != EOK)
-		return;
-
-	while (true) {
-		ipc_call_t call;
-		ipc_callid_t callid = async_get_call(&call);
-		sysarg_t method = IPC_GET_IMETHOD(call);
-
-		if (!method) {
-			/* The other side has hung up */
-			async_answer_0(callid, EOK);
-			break;
-		}
-
-		switch (method) {
-		case INETPING_SEND:
-			inetping_send_srv(&client, callid, &call);
-			break;
-		case INETPING_GET_SRCADDR:
-			inetping_get_srcaddr_srv(&client, callid, &call);
-			break;
-		default:
-			async_answer_0(callid, EINVAL);
-		}
-	}
-
-	inetping_client_fini(&client);
-}
-
-/** @}
- */
Index: pace/srv/net/inet/inetping.h
===================================================================
--- uspace/srv/net/inet/inetping.h	(revision 6df220289417a3a5f0a319426564833d6f527370)
+++ 	(revision )
@@ -1,46 +1,0 @@
-/*
- * 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 INETPING_H_
-#define INETPING_H_
-
-extern void inetping_conn(ipc_callid_t, ipc_call_t *, void *);
-extern int inetping_recv(uint16_t, inetping_sdu_t *);
-
-#endif
-
-/** @}
- */
Index: pace/srv/net/inet/pdu.c
===================================================================
--- uspace/srv/net/inet/pdu.c	(revision 6df220289417a3a5f0a319426564833d6f527370)
+++ 	(revision )
@@ -1,268 +1,0 @@
-/*
- * 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 <align.h>
-#include <bitops.h>
-#include <byteorder.h>
-#include <errno.h>
-#include <fibril_synch.h>
-#include <io/log.h>
-#include <macros.h>
-#include <mem.h>
-#include <stdlib.h>
-
-#include "inet.h"
-#include "inet_std.h"
-#include "pdu.h"
-
-static FIBRIL_MUTEX_INITIALIZE(ip_ident_lock);
-static uint16_t ip_ident = 0;
-
-/** One's complement addition.
- *
- * Result is a + b + carry.
- */
-static uint16_t inet_ocadd16(uint16_t a, uint16_t b)
-{
-	uint32_t s;
-
-	s = (uint32_t)a + (uint32_t)b;
-	return (s & 0xffff) + (s >> 16);
-}
-
-uint16_t inet_checksum_calc(uint16_t ivalue, void *data, size_t size)
-{
-	uint16_t sum;
-	uint16_t w;
-	size_t words, i;
-	uint8_t *bdata;
-
-	sum = ~ivalue;
-	words = size / 2;
-	bdata = (uint8_t *)data;
-
-	for (i = 0; i < words; i++) {
-		w = ((uint16_t)bdata[2*i] << 8) | bdata[2*i + 1];
-		sum = inet_ocadd16(sum, w);
-	}
-
-	if (size % 2 != 0) {
-		w = ((uint16_t)bdata[2*words] << 8);
-		sum = inet_ocadd16(sum, w);
-	}
-
-	return ~sum;
-}
-
-/** Encode Internet PDU.
- *
- * Encode internet packet into PDU (serialized form). Will encode a
- * fragment of the payload starting at offset @a offs. The resulting
- * PDU will have at most @a mtu bytes. @a *roffs will be set to the offset
- * of remaining payload. If some data is remaining, the MF flag will
- * be set in the header, otherwise the offset will equal @a packet->size.
- *
- * @param packet	Packet to encode
- * @param offs		Offset into packet payload (in bytes)
- * @param mtu		MTU (Maximum Transmission Unit) in bytes
- * @param rdata		Place to store pointer to allocated data buffer
- * @param rsize		Place to store size of allocated data buffer
- * @param roffs		Place to store offset of remaning data
- */
-int inet_pdu_encode(inet_packet_t *packet, size_t offs, size_t mtu,
-    void **rdata, size_t *rsize, size_t *roffs)
-{
-	void *data;
-	size_t size;
-	ip_header_t *hdr;
-	size_t hdr_size;
-	size_t data_offs;
-	uint16_t chksum;
-	uint16_t ident;
-	uint16_t flags_foff;
-	uint16_t foff;
-	size_t fragoff_limit;
-	size_t xfer_size;
-	size_t spc_avail;
-	size_t rem_offs;
-
-	/* Upper bound for fragment offset field */
-	fragoff_limit = 1 << (FF_FRAGOFF_h - FF_FRAGOFF_l);
-
-	/* Verify that total size of datagram is within reasonable bounds */
-	if (offs + packet->size > FRAG_OFFS_UNIT * fragoff_limit)
-		return ELIMIT;
-
-	hdr_size = sizeof(ip_header_t);
-	data_offs = ROUND_UP(hdr_size, 4);
-
-	assert(offs % FRAG_OFFS_UNIT == 0);
-	assert(offs / FRAG_OFFS_UNIT < fragoff_limit);
-
-	/* Value for the fragment offset field */
-	foff = offs / FRAG_OFFS_UNIT;
-
-	if (hdr_size >= mtu)
-		return EINVAL;
-
-	/* Amount of space in the PDU available for payload */
-	spc_avail = mtu - hdr_size;
-	spc_avail -= (spc_avail % FRAG_OFFS_UNIT);
-
-	/* Amount of data (payload) to transfer */
-	xfer_size = min(packet->size - offs, spc_avail);
-
-	/* Total PDU size */
-	size = hdr_size + xfer_size;
-
-	/* Offset of remaining payload */
-	rem_offs = offs + xfer_size;
-
-	/* Flags */
-	flags_foff =
-	    (packet->df ? BIT_V(uint16_t, FF_FLAG_DF) : 0) +
-	    (rem_offs < packet->size ? BIT_V(uint16_t, FF_FLAG_MF) : 0) +
-	    (foff << FF_FRAGOFF_l);
-
-	data = calloc(size, 1);
-	if (data == NULL)
-		return ENOMEM;
-
-	/* Allocate identifier */
-	fibril_mutex_lock(&ip_ident_lock);
-	ident = ++ip_ident;
-	fibril_mutex_unlock(&ip_ident_lock);
-
-	/* Encode header fields */
-	hdr = (ip_header_t *)data;
-	hdr->ver_ihl = (4 << VI_VERSION_l) | (hdr_size / sizeof(uint32_t));
-	hdr->tos = packet->tos;
-	hdr->tot_len = host2uint16_t_be(size);
-	hdr->id = host2uint16_t_be(ident);
-	hdr->flags_foff = host2uint16_t_be(flags_foff);
-	hdr->ttl = packet->ttl;
-	hdr->proto = packet->proto;
-	hdr->chksum = 0;
-	hdr->src_addr = host2uint32_t_be(packet->src.ipv4);
-	hdr->dest_addr = host2uint32_t_be(packet->dest.ipv4);
-
-	/* Compute checksum */
-	chksum = inet_checksum_calc(INET_CHECKSUM_INIT, (void *)hdr, hdr_size);
-	hdr->chksum = host2uint16_t_be(chksum);
-
-	/* Copy payload */
-	memcpy((uint8_t *)data + data_offs, packet->data + offs, xfer_size);
-
-	*rdata = data;
-	*rsize = size;
-	*roffs = rem_offs;
-
-	return EOK;
-}
-
-int inet_pdu_decode(void *data, size_t size, inet_packet_t *packet)
-{
-	ip_header_t *hdr;
-	size_t tot_len;
-	size_t data_offs;
-	uint8_t version;
-	uint16_t ident;
-	uint16_t flags_foff;
-	uint16_t foff;
-
-	log_msg(LVL_DEBUG, "inet_pdu_decode()");
-
-	if (size < sizeof(ip_header_t)) {
-		log_msg(LVL_DEBUG, "PDU too short (%zu)", size);
-		return EINVAL;
-	}
-
-	hdr = (ip_header_t *)data;
-
-	version = BIT_RANGE_EXTRACT(uint8_t, VI_VERSION_h, VI_VERSION_l,
-	    hdr->ver_ihl);
-	if (version != 4) {
-		log_msg(LVL_DEBUG, "Version (%d) != 4", version);
-		return EINVAL;
-	}
-
-	tot_len = uint16_t_be2host(hdr->tot_len);
-	if (tot_len < sizeof(ip_header_t)) {
-		log_msg(LVL_DEBUG, "Total Length too small (%zu)", tot_len);
-		return EINVAL;
-	}
-
-	if (tot_len > size) {
-		log_msg(LVL_DEBUG, "Total Length = %zu > PDU size = %zu",
-			tot_len, size);
-		return EINVAL;
-	}
-
-	ident = uint16_t_be2host(hdr->id);
-	flags_foff = uint16_t_be2host(hdr->flags_foff);
-	foff = BIT_RANGE_EXTRACT(uint16_t, FF_FRAGOFF_h, FF_FRAGOFF_l,
-	    flags_foff);
-	/* XXX Checksum */
-
-	packet->src.ipv4 = uint32_t_be2host(hdr->src_addr);
-	packet->dest.ipv4 = uint32_t_be2host(hdr->dest_addr);
-	packet->tos = hdr->tos;
-	packet->proto = hdr->proto;
-	packet->ttl = hdr->ttl;
-	packet->ident = ident;
-
-	packet->df = (flags_foff & BIT_V(uint16_t, FF_FLAG_DF)) != 0;
-	packet->mf = (flags_foff & BIT_V(uint16_t, FF_FLAG_MF)) != 0;
-	packet->offs = foff * FRAG_OFFS_UNIT;
-
-	/* XXX IP options */
-	data_offs = sizeof(uint32_t) * BIT_RANGE_EXTRACT(uint8_t, VI_IHL_h,
-	    VI_IHL_l, hdr->ver_ihl);
-
-	packet->size = tot_len - data_offs;
-	packet->data = calloc(packet->size, 1);
-	if (packet->data == NULL) {
-		log_msg(LVL_WARN, "Out of memory.");
-		return ENOMEM;
-	}
-
-	memcpy(packet->data, (uint8_t *)data + data_offs, packet->size);
-
-	return EOK;
-}
-
-/** @}
- */
Index: pace/srv/net/inet/pdu.h
===================================================================
--- uspace/srv/net/inet/pdu.h	(revision 6df220289417a3a5f0a319426564833d6f527370)
+++ 	(revision )
@@ -1,54 +1,0 @@
-/*
- * 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 INET_PDU_H_
-#define INET_PDU_H_
-
-#include <sys/types.h>
-#include "inet.h"
-
-#define INET_CHECKSUM_INIT 0xffff
-
-extern uint16_t inet_checksum_calc(uint16_t, void *, size_t);
-
-extern int inet_pdu_encode(inet_packet_t *, size_t, size_t, void **,
-    size_t *, size_t *);
-extern int inet_pdu_decode(void *, size_t, inet_packet_t *);
-
-#endif
-
-/** @}
- */
Index: pace/srv/net/inet/reass.c
===================================================================
--- uspace/srv/net/inet/reass.c	(revision 6df220289417a3a5f0a319426564833d6f527370)
+++ 	(revision )
@@ -1,374 +1,0 @@
-/*
- * 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 Datagram reassembly.
- */
-
-#include <errno.h>
-#include <fibril_synch.h>
-#include <io/log.h>
-#include <macros.h>
-#include <mem.h>
-#include <stdlib.h>
-
-#include "inet.h"
-#include "inet_std.h"
-#include "reass.h"
-
-/** Datagram being reassembled.
- *
- * Uniquely identified by (source address, destination address, protocol,
- * identification) per RFC 791 sec. 2.3 / Fragmentation.
- */
-typedef struct {
-	link_t map_link;
-	/** List of fragments, @c reass_frag_t */
-	list_t frags;
-} reass_dgram_t;
-
-/** One datagram fragment */
-typedef struct {
-	link_t dgram_link;
-	inet_packet_t packet;
-} reass_frag_t;
-
-/** Datagram map, list of reass_dgram_t */
-static LIST_INITIALIZE(reass_dgram_map);
-/** Protects access to @c reass_dgram_map */
-static FIBRIL_MUTEX_INITIALIZE(reass_dgram_map_lock);
-
-static reass_dgram_t *reass_dgram_new(void);
-static reass_dgram_t *reass_dgram_get(inet_packet_t *);
-static int reass_dgram_insert_frag(reass_dgram_t *, inet_packet_t *);
-static bool reass_dgram_complete(reass_dgram_t *);
-static void reass_dgram_remove(reass_dgram_t *);
-static int reass_dgram_deliver(reass_dgram_t *);
-static void reass_dgram_destroy(reass_dgram_t *);
-
-/** Queue packet for datagram reassembly.
- *
- * @param packet	Packet
- * @return		EOK on success or ENOMEM.
- */
-int inet_reass_queue_packet(inet_packet_t *packet)
-{
-	reass_dgram_t *rdg;
-	int rc;
-
-	log_msg(LVL_DEBUG, "inet_reass_queue_packet()");
-
-	fibril_mutex_lock(&reass_dgram_map_lock);
-
-	/* Get existing or new datagram */
-	rdg = reass_dgram_get(packet);
-	if (rdg == NULL) {
-		/* Only happens when we are out of memory */
-		fibril_mutex_unlock(&reass_dgram_map_lock);
-		log_msg(LVL_DEBUG, "Allocation failed, packet dropped.");
-		return ENOMEM;
-	}
-
-	/* Insert fragment into the datagram */
-	rc = reass_dgram_insert_frag(rdg, packet);
-	if (rc != EOK)
-		return ENOMEM;
-
-	/* Check if datagram is complete */
-	if (reass_dgram_complete(rdg)) {
-		/* Remove it from the map */
-		reass_dgram_remove(rdg);
-		fibril_mutex_unlock(&reass_dgram_map_lock);
-
-		/* Deliver complete datagram */
-		rc = reass_dgram_deliver(rdg);
-		reass_dgram_destroy(rdg);
-		return rc;
-	}
-
-	fibril_mutex_unlock(&reass_dgram_map_lock);
-	return EOK;
-}
-
-/** Get datagram reassembly structure for packet.
- *
- * @param packet	Packet
- * @return		Datagram reassembly structure matching @a packet
- */
-static reass_dgram_t *reass_dgram_get(inet_packet_t *packet)
-{
-	assert(fibril_mutex_is_locked(&reass_dgram_map_lock));
-
-	list_foreach(reass_dgram_map, link) {
-		reass_dgram_t *rdg = list_get_instance(link, reass_dgram_t,
-		    map_link);
-
-		link_t *f1_link = list_first(&rdg->frags);
-		assert(f1_link != NULL);
-
-		reass_frag_t *f1 = list_get_instance(f1_link, reass_frag_t,
-		    dgram_link);
-
-		if (f1->packet.src.ipv4 == packet->src.ipv4 &&
-		    f1->packet.dest.ipv4 == packet->dest.ipv4 &&
-		    f1->packet.proto == packet->proto &&
-		    f1->packet.ident == packet->ident) {
-			/* Match */
-			return rdg;
-		}
-	}
-
-	/* No existing reassembly structure. Create a new one. */
-	return reass_dgram_new();
-}
-
-/** Create new datagram reassembly structure.
- *
- * @return New datagram reassembly structure.
- */
-static reass_dgram_t *reass_dgram_new(void)
-{
-	reass_dgram_t *rdg;
-
-	rdg = calloc(1, sizeof(reass_dgram_t));
-	if (rdg == NULL)
-		return NULL;
-
-	link_initialize(&rdg->map_link);
-	list_initialize(&rdg->frags);
-
-	return rdg;
-}
-
-static reass_frag_t *reass_frag_new(void)
-{
-	reass_frag_t *frag;
-
-	frag = calloc(1, sizeof(reass_frag_t));
-	if (frag == NULL)
-		return NULL;
-
-	link_initialize(&frag->dgram_link);
-
-	return frag;
-}
-
-static int reass_dgram_insert_frag(reass_dgram_t *rdg, inet_packet_t *packet)
-{
-	reass_frag_t *frag;
-	void *data_copy;
-	link_t *link;
-
-	assert(fibril_mutex_is_locked(&reass_dgram_map_lock));
-
-	frag = reass_frag_new();
-
-	/* Clone the packet */
-
-	data_copy = malloc(packet->size);
-	if (data_copy == NULL)
-		return ENOMEM;
-
-	frag->packet = *packet;
-	frag->packet.data = data_copy;
-
-	/*
-	 * XXX Make resource-consuming attacks harder, eliminate any duplicate
-	 * data immediately. Possibly eliminate redundant packet headers.
-	 */
-
-	/*
-	 * Insert into the list, which is sorted by offs member ascending.
-	 */
-
-	link = list_first(&rdg->frags);
-	while (link != NULL) {
-		reass_frag_t *qf = list_get_instance(link, reass_frag_t,
-		    dgram_link);
-
-		if (qf->packet.offs >= packet->offs)
-			break;
-
-		link = link->next;
-	}
-
-	if (link != NULL)
-		list_insert_after(&frag->dgram_link, link);
-	else
-		list_append(&frag->dgram_link, &rdg->frags);
-
-	return EOK;
-}
-
-/** Check if datagram is complete.
- *
- * @param rdg		Datagram reassembly structure
- * @return		@c true if complete, @c false if not
- */
-static bool reass_dgram_complete(reass_dgram_t *rdg)
-{
-	reass_frag_t *frag, *prev;
-	link_t *link;
-
-	assert(fibril_mutex_is_locked(&reass_dgram_map_lock));
-	assert(!list_empty(&rdg->frags));
-
-	/* First fragment must be at offset zero */
-	frag = list_get_instance(list_first(&rdg->frags), reass_frag_t,
-	    dgram_link);
-	if (frag->packet.offs != 0)
-		return false;
-
-	prev = frag;
-	while (true) {
-		link = frag->dgram_link.next;
-		if (link == NULL)
-			return false;
-
-		/* Each next fragment must follow immediately or overlap */
-		frag = list_get_instance(link, reass_frag_t, dgram_link);
-		if (frag->packet.offs > prev->packet.offs + prev->packet.size)
-			return false;
-
-		/* No more fragments - datagram is complete */
-		if (!frag->packet.mf)
-			return true;
-
-		prev = frag;
-	}
-
-	return false;
-}
-
-/** Remove datagram from reassembly map.
- *
- * @param rdg		Datagram reassembly structure
- */
-static void reass_dgram_remove(reass_dgram_t *rdg)
-{
-	assert(fibril_mutex_is_locked(&reass_dgram_map_lock));
-	list_remove(&rdg->map_link);
-}
-
-/** Deliver complete datagram.
- *
- * @param rdg		Datagram reassembly structure.
- */
-static int reass_dgram_deliver(reass_dgram_t *rdg)
-{
-	size_t dgram_size;
-	size_t fragoff_limit;
-	inet_dgram_t dgram;
-	reass_frag_t *frag;
-	uint8_t proto;
-
-	/*
-	 * Potentially there could be something beyond the first packet
-	 * that has !MF. Make sure we ignore that.
-	 */
-	frag = NULL;
-	list_foreach(rdg->frags, link) {
-		frag = list_get_instance(link, reass_frag_t, dgram_link);
-
-		if (!frag->packet.mf)
-			break;
-	}
-
-	assert(frag != NULL);
-	assert(!frag->packet.mf);
-
-	dgram_size = frag->packet.offs + frag->packet.size;
-
-	/* Upper bound for fragment offset field */
-	fragoff_limit = 1 << (FF_FRAGOFF_h - FF_FRAGOFF_l);
-
-	/* Verify that total size of datagram is within reasonable bounds */
-	if (dgram_size > FRAG_OFFS_UNIT * fragoff_limit)
-		return ELIMIT;
-
-	dgram.data = calloc(dgram_size, 1);
-	if (dgram.data == NULL)
-		return ENOMEM;
-
-	dgram.size = dgram_size;
-	dgram.src = frag->packet.src;
-	dgram.dest = frag->packet.dest;
-	dgram.tos = frag->packet.tos;
-	proto = frag->packet.proto;
-
-	/* Pull together data from individual fragments */
-
-	size_t doffs = 0;
-
-	frag = NULL;
-	list_foreach(rdg->frags, link) {
-		frag = list_get_instance(link, reass_frag_t, dgram_link);
-
-		size_t cb, ce;
-
-		cb = max(doffs, frag->packet.offs);
-		ce = min(dgram_size, frag->packet.offs + frag->packet.size);
-
-		if (ce > cb) {
-			memcpy(dgram.data + cb,
-			    frag->packet.data + cb - frag->packet.offs,
-			    ce - cb);
-		}
-
-		if (!frag->packet.mf)
-			break;
-	}
-
-	return inet_recv_dgram_local(&dgram, proto);
-}
-
-/** Destroy datagram reassembly structure.
- *
- * @param rdg		Datagram reassembly structure.
- */
-static void reass_dgram_destroy(reass_dgram_t *rdg)
-{
-	while (!list_empty(&rdg->frags)) {
-		link_t *flink = list_first(&rdg->frags);
-		reass_frag_t *frag = list_get_instance(flink, reass_frag_t,
-		    dgram_link);
-
-		list_remove(&frag->dgram_link);
-		free(frag->packet.data);
-		free(frag);
-	}
-
-	free(rdg);
-}
-
-/** @}
- */
Index: pace/srv/net/inet/reass.h
===================================================================
--- uspace/srv/net/inet/reass.h	(revision 6df220289417a3a5f0a319426564833d6f527370)
+++ 	(revision )
@@ -1,48 +1,0 @@
-/*
- * 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 INET_REASS_H_
-#define INET_REASS_H_
-
-#include <sys/types.h>
-#include "inet.h"
-
-extern int inet_reass_queue_packet(inet_packet_t *);
-
-#endif
-
-/** @}
- */
Index: pace/srv/net/inet/sroute.c
===================================================================
--- uspace/srv/net/inet/sroute.c	(revision 6df220289417a3a5f0a319426564833d6f527370)
+++ 	(revision )
@@ -1,219 +1,0 @@
-/*
- * 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 <bitops.h>
-#include <errno.h>
-#include <fibril_synch.h>
-#include <io/log.h>
-#include <ipc/loc.h>
-#include <stdlib.h>
-#include <str.h>
-
-#include "sroute.h"
-#include "inet.h"
-#include "inet_link.h"
-#include "inet_util.h"
-
-static FIBRIL_MUTEX_INITIALIZE(sroute_list_lock);
-static LIST_INITIALIZE(sroute_list);
-static sysarg_t sroute_id = 0;
-
-inet_sroute_t *inet_sroute_new(void)
-{
-	inet_sroute_t *sroute = calloc(1, sizeof(inet_sroute_t));
-
-	if (sroute == NULL) {
-		log_msg(LVL_ERROR, "Failed allocating static route object. "
-		    "Out of memory.");
-		return NULL;
-	}
-
-	link_initialize(&sroute->sroute_list);
-	fibril_mutex_lock(&sroute_list_lock);
-	sroute->id = ++sroute_id;
-	fibril_mutex_unlock(&sroute_list_lock);
-
-	return sroute;
-}
-
-void inet_sroute_delete(inet_sroute_t *sroute)
-{
-	if (sroute->name != NULL)
-		free(sroute->name);
-	free(sroute);
-}
-
-void inet_sroute_add(inet_sroute_t *sroute)
-{
-	fibril_mutex_lock(&sroute_list_lock);
-	list_append(&sroute->sroute_list, &sroute_list);
-	fibril_mutex_unlock(&sroute_list_lock);
-}
-
-void inet_sroute_remove(inet_sroute_t *sroute)
-{
-	fibril_mutex_lock(&sroute_list_lock);
-	list_remove(&sroute->sroute_list);
-	fibril_mutex_unlock(&sroute_list_lock);
-}
-
-/** Find address object matching address @a addr.
- *
- * @param addr	Address
- */
-inet_sroute_t *inet_sroute_find(inet_addr_t *addr)
-{
-	uint32_t mask;
-	inet_sroute_t *best;
-
-	log_msg(LVL_DEBUG, "inet_sroute_find(%x)", (unsigned)addr->ipv4);
-
-	fibril_mutex_lock(&sroute_list_lock);
-
-	best = NULL;
-
-	list_foreach(sroute_list, link) {
-		inet_sroute_t *sroute = list_get_instance(link,
-		    inet_sroute_t, sroute_list);
-
-		/* Look for the most specific route */
-		if (best != NULL && best->dest.bits >= sroute->dest.bits)
-			continue;
-
-		mask = inet_netmask(sroute->dest.bits);
-		if ((sroute->dest.ipv4 & mask) == (addr->ipv4 & mask)) {
-			fibril_mutex_unlock(&sroute_list_lock);
-			log_msg(LVL_DEBUG, "inet_sroute_find: found %p",
-			    sroute);
-			return sroute;
-		}
-	}
-
-	log_msg(LVL_DEBUG, "inet_sroute_find: Not found");
-	fibril_mutex_unlock(&sroute_list_lock);
-
-	return NULL;
-}
-
-/** Find static route with a specific name.
- *
- * @param name	Address object name
- * @return	Address object
- */
-inet_sroute_t *inet_sroute_find_by_name(const char *name)
-{
-	log_msg(LVL_DEBUG, "inet_sroute_find_by_name('%s')",
-	    name);
-
-	fibril_mutex_lock(&sroute_list_lock);
-
-	list_foreach(sroute_list, link) {
-		inet_sroute_t *sroute = list_get_instance(link,
-		    inet_sroute_t, sroute_list);
-
-		if (str_cmp(sroute->name, name) == 0) {
-			fibril_mutex_unlock(&sroute_list_lock);
-			log_msg(LVL_DEBUG, "inet_sroute_find_by_name: found %p",
-			    sroute);
-			return sroute;
-		}
-	}
-
-	log_msg(LVL_DEBUG, "inet_sroute_find_by_name: Not found");
-	fibril_mutex_unlock(&sroute_list_lock);
-
-	return NULL;
-}
-
-/** Find static route with the given ID.
- *
- * @param id	Address object ID
- * @return	Address object
- */
-inet_sroute_t *inet_sroute_get_by_id(sysarg_t id)
-{
-	log_msg(LVL_DEBUG, "inet_sroute_get_by_id(%zu)", (size_t)id);
-
-	fibril_mutex_lock(&sroute_list_lock);
-
-	list_foreach(sroute_list, link) {
-		inet_sroute_t *sroute = list_get_instance(link,
-		    inet_sroute_t, sroute_list);
-
-		if (sroute->id == id) {
-			fibril_mutex_unlock(&sroute_list_lock);
-			return sroute;
-		}
-	}
-
-	fibril_mutex_unlock(&sroute_list_lock);
-
-	return NULL;
-}
-
-/** Get IDs of all static routes. */
-int inet_sroute_get_id_list(sysarg_t **rid_list, size_t *rcount)
-{
-	sysarg_t *id_list;
-	size_t count, i;
-
-	fibril_mutex_lock(&sroute_list_lock);
-	count = list_count(&sroute_list);
-
-	id_list = calloc(count, sizeof(sysarg_t));
-	if (id_list == NULL) {
-		fibril_mutex_unlock(&sroute_list_lock);
-		return ENOMEM;
-	}
-
-	i = 0;
-	list_foreach(sroute_list, link) {
-		inet_sroute_t *sroute = list_get_instance(link,
-		    inet_sroute_t, sroute_list);
-
-		id_list[i++] = sroute->id;
-	}
-
-	fibril_mutex_unlock(&sroute_list_lock);
-
-	*rid_list = id_list;
-	*rcount = count;
-
-	return EOK;
-}
-
-/** @}
- */
Index: pace/srv/net/inet/sroute.h
===================================================================
--- uspace/srv/net/inet/sroute.h	(revision 6df220289417a3a5f0a319426564833d6f527370)
+++ 	(revision )
@@ -1,58 +1,0 @@
-/*
- * 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 INET_SROUTE_H_
-#define INET_SROUTE_H_
-
-#include <sys/types.h>
-#include "inet.h"
-
-extern inet_sroute_t *inet_sroute_new(void);
-extern void inet_sroute_delete(inet_sroute_t *);
-extern void inet_sroute_add(inet_sroute_t *);
-extern void inet_sroute_remove(inet_sroute_t *);
-extern inet_sroute_t *inet_sroute_find(inet_addr_t *);
-extern inet_sroute_t *inet_sroute_find_by_name(const char *);
-extern inet_sroute_t *inet_sroute_get_by_id(sysarg_t);
-extern int inet_sroute_send_dgram(inet_sroute_t *, inet_addr_t *,
-    inet_dgram_t *, uint8_t, uint8_t, int);
-extern int inet_sroute_get_id_list(sysarg_t **, size_t *);
-
-
-#endif
-
-/** @}
- */
Index: uspace/srv/net/inetsrv/Makefile
===================================================================
--- uspace/srv/net/inetsrv/Makefile	(revision ce1836ed04784ea0bacc387ac80bfe852f36119b)
+++ uspace/srv/net/inetsrv/Makefile	(revision ce1836ed04784ea0bacc387ac80bfe852f36119b)
@@ -0,0 +1,44 @@
+#
+# 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.
+#
+
+USPACE_PREFIX = ../../..
+BINARY = inetsrv
+
+SOURCES = \
+	addrobj.c \
+	icmp.c \
+	inetsrv.c \
+	inet_link.c \
+	inet_util.c \
+	inetcfg.c \
+	inetping.c \
+	pdu.c \
+	reass.c \
+	sroute.c
+
+include $(USPACE_PREFIX)/Makefile.common
Index: uspace/srv/net/inetsrv/addrobj.c
===================================================================
--- uspace/srv/net/inetsrv/addrobj.c	(revision ce1836ed04784ea0bacc387ac80bfe852f36119b)
+++ uspace/srv/net/inetsrv/addrobj.c	(revision ce1836ed04784ea0bacc387ac80bfe852f36119b)
@@ -0,0 +1,261 @@
+/*
+ * 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 <bitops.h>
+#include <errno.h>
+#include <fibril_synch.h>
+#include <io/log.h>
+#include <ipc/loc.h>
+#include <stdlib.h>
+#include <str.h>
+
+#include "addrobj.h"
+#include "inetsrv.h"
+#include "inet_link.h"
+#include "inet_util.h"
+
+static inet_addrobj_t *inet_addrobj_find_by_name_locked(const char *, inet_link_t *);
+
+static FIBRIL_MUTEX_INITIALIZE(addr_list_lock);
+static LIST_INITIALIZE(addr_list);
+static sysarg_t addr_id = 0;
+
+inet_addrobj_t *inet_addrobj_new(void)
+{
+	inet_addrobj_t *addr = calloc(1, sizeof(inet_addrobj_t));
+
+	if (addr == NULL) {
+		log_msg(LVL_ERROR, "Failed allocating address object. "
+		    "Out of memory.");
+		return NULL;
+	}
+
+	link_initialize(&addr->addr_list);
+	fibril_mutex_lock(&addr_list_lock);
+	addr->id = ++addr_id;
+	fibril_mutex_unlock(&addr_list_lock);
+
+	return addr;
+}
+
+void inet_addrobj_delete(inet_addrobj_t *addr)
+{
+	if (addr->name != NULL)
+		free(addr->name);
+	free(addr);
+}
+
+int inet_addrobj_add(inet_addrobj_t *addr)
+{
+	inet_addrobj_t *aobj;
+
+	fibril_mutex_lock(&addr_list_lock);
+	aobj = inet_addrobj_find_by_name_locked(addr->name, addr->ilink);
+	if (aobj != NULL) {
+		/* Duplicate address name */
+		fibril_mutex_unlock(&addr_list_lock);
+		return EEXISTS;
+	}
+
+	list_append(&addr->addr_list, &addr_list);
+	fibril_mutex_unlock(&addr_list_lock);
+
+	return EOK;
+}
+
+void inet_addrobj_remove(inet_addrobj_t *addr)
+{
+	fibril_mutex_lock(&addr_list_lock);
+	list_remove(&addr->addr_list);
+	fibril_mutex_unlock(&addr_list_lock);
+}
+
+/** Find address object matching address @a addr.
+ *
+ * @param addr	Address
+ * @oaram find	iaf_net to find network (using mask),
+ *		iaf_addr to find local address (exact match)
+ */
+inet_addrobj_t *inet_addrobj_find(inet_addr_t *addr, inet_addrobj_find_t find)
+{
+	uint32_t mask;
+
+	log_msg(LVL_DEBUG, "inet_addrobj_find(%x)", (unsigned)addr->ipv4);
+
+	fibril_mutex_lock(&addr_list_lock);
+
+	list_foreach(addr_list, link) {
+		inet_addrobj_t *naddr = list_get_instance(link,
+		    inet_addrobj_t, addr_list);
+
+		mask = inet_netmask(naddr->naddr.bits);
+		if ((naddr->naddr.ipv4 & mask) == (addr->ipv4 & mask)) {
+			fibril_mutex_unlock(&addr_list_lock);
+			log_msg(LVL_DEBUG, "inet_addrobj_find: found %p",
+			    naddr);
+			return naddr;
+		}
+	}
+
+	log_msg(LVL_DEBUG, "inet_addrobj_find: Not found");
+	fibril_mutex_unlock(&addr_list_lock);
+
+	return NULL;
+}
+
+/** Find address object on a link, with a specific name.
+ *
+ * @param name	Address object name
+ * @param ilink	Inet link
+ * @return	Address object
+ */
+static inet_addrobj_t *inet_addrobj_find_by_name_locked(const char *name, inet_link_t *ilink)
+{
+	assert(fibril_mutex_is_locked(&addr_list_lock));
+
+	log_msg(LVL_DEBUG, "inet_addrobj_find_by_name_locked('%s', '%s')",
+	    name, ilink->svc_name);
+
+	list_foreach(addr_list, link) {
+		inet_addrobj_t *naddr = list_get_instance(link,
+		    inet_addrobj_t, addr_list);
+
+		if (naddr->ilink == ilink && str_cmp(naddr->name, name) == 0) {
+			log_msg(LVL_DEBUG, "inet_addrobj_find_by_name_locked: found %p",
+			    naddr);
+			return naddr;
+		}
+	}
+
+	log_msg(LVL_DEBUG, "inet_addrobj_find_by_name_locked: Not found");
+
+	return NULL;
+}
+
+
+/** Find address object on a link, with a specific name.
+ *
+ * @param name	Address object name
+ * @param ilink	Inet link
+ * @return	Address object
+ */
+inet_addrobj_t *inet_addrobj_find_by_name(const char *name, inet_link_t *ilink)
+{
+	inet_addrobj_t *aobj;
+
+	log_msg(LVL_DEBUG, "inet_addrobj_find_by_name('%s', '%s')",
+	    name, ilink->svc_name);
+
+	fibril_mutex_lock(&addr_list_lock);
+	aobj = inet_addrobj_find_by_name_locked(name, ilink);
+	fibril_mutex_unlock(&addr_list_lock);
+
+	return aobj;
+}
+
+/** Find address object with the given ID.
+ *
+ * @param id	Address object ID
+ * @return	Address object
+ */
+inet_addrobj_t *inet_addrobj_get_by_id(sysarg_t id)
+{
+	log_msg(LVL_DEBUG, "inet_addrobj_get_by_id(%zu)", (size_t)id);
+
+	fibril_mutex_lock(&addr_list_lock);
+
+	list_foreach(addr_list, link) {
+		inet_addrobj_t *naddr = list_get_instance(link,
+		    inet_addrobj_t, addr_list);
+
+		if (naddr->id == id) {
+			fibril_mutex_unlock(&addr_list_lock);
+			return naddr;
+		}
+	}
+
+	fibril_mutex_unlock(&addr_list_lock);
+
+	return NULL;
+}
+
+/** Send datagram from address object */
+int inet_addrobj_send_dgram(inet_addrobj_t *addr, inet_addr_t *ldest,
+    inet_dgram_t *dgram, uint8_t proto, uint8_t ttl, int df)
+{
+	inet_addr_t lsrc_addr;
+	inet_addr_t *ldest_addr;
+
+	lsrc_addr.ipv4 = addr->naddr.ipv4;
+	ldest_addr = &dgram->dest;
+
+	return inet_link_send_dgram(addr->ilink, &lsrc_addr, ldest_addr, dgram,
+	    proto, ttl, df);
+}
+
+/** Get IDs of all address objects. */
+int inet_addrobj_get_id_list(sysarg_t **rid_list, size_t *rcount)
+{
+	sysarg_t *id_list;
+	size_t count, i;
+
+	fibril_mutex_lock(&addr_list_lock);
+	count = list_count(&addr_list);
+
+	id_list = calloc(count, sizeof(sysarg_t));
+	if (id_list == NULL) {
+		fibril_mutex_unlock(&addr_list_lock);
+		return ENOMEM;
+	}
+
+	i = 0;
+	list_foreach(addr_list, link) {
+		inet_addrobj_t *addr = list_get_instance(link,
+		    inet_addrobj_t, addr_list);
+
+		id_list[i++] = addr->id;
+	}
+
+	fibril_mutex_unlock(&addr_list_lock);
+
+	*rid_list = id_list;
+	*rcount = count;
+
+	return EOK;
+}
+
+/** @}
+ */
Index: uspace/srv/net/inetsrv/addrobj.h
===================================================================
--- uspace/srv/net/inetsrv/addrobj.h	(revision ce1836ed04784ea0bacc387ac80bfe852f36119b)
+++ uspace/srv/net/inetsrv/addrobj.h	(revision ce1836ed04784ea0bacc387ac80bfe852f36119b)
@@ -0,0 +1,65 @@
+/*
+ * 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 INET_ADDROBJ_H_
+#define INET_ADDROBJ_H_
+
+#include <sys/types.h>
+#include "inetsrv.h"
+
+typedef enum {
+	/* Find matching network address (using mask) */
+	iaf_net,
+	/* Find exact local address (not using mask) */
+	iaf_addr
+} inet_addrobj_find_t;
+
+extern inet_addrobj_t *inet_addrobj_new(void);
+extern void inet_addrobj_delete(inet_addrobj_t *);
+extern int inet_addrobj_add(inet_addrobj_t *);
+extern void inet_addrobj_remove(inet_addrobj_t *);
+extern inet_addrobj_t *inet_addrobj_find(inet_addr_t *, inet_addrobj_find_t);
+extern inet_addrobj_t *inet_addrobj_find_by_name(const char *, inet_link_t *);
+extern inet_addrobj_t *inet_addrobj_get_by_id(sysarg_t);
+extern int inet_addrobj_send_dgram(inet_addrobj_t *, inet_addr_t *,
+    inet_dgram_t *, uint8_t, uint8_t, int);
+extern int inet_addrobj_get_id_list(sysarg_t **, size_t *);
+
+
+#endif
+
+/** @}
+ */
Index: uspace/srv/net/inetsrv/icmp.c
===================================================================
--- uspace/srv/net/inetsrv/icmp.c	(revision ce1836ed04784ea0bacc387ac80bfe852f36119b)
+++ uspace/srv/net/inetsrv/icmp.c	(revision ce1836ed04784ea0bacc387ac80bfe852f36119b)
@@ -0,0 +1,183 @@
+/*
+ * 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 <byteorder.h>
+#include <errno.h>
+#include <io/log.h>
+#include <mem.h>
+#include <stdlib.h>
+
+#include "icmp.h"
+#include "icmp_std.h"
+#include "inetsrv.h"
+#include "inetping.h"
+#include "pdu.h"
+
+/* XXX */
+#define INET_TTL_MAX 255
+
+static int icmp_recv_echo_request(inet_dgram_t *);
+static int icmp_recv_echo_reply(inet_dgram_t *);
+
+int icmp_recv(inet_dgram_t *dgram)
+{
+	uint8_t type;
+
+	log_msg(LVL_DEBUG, "icmp_recv()");
+
+	if (dgram->size < 1)
+		return EINVAL;
+
+	type = *(uint8_t *)dgram->data;
+
+	switch (type) {
+	case ICMP_ECHO_REQUEST:
+		return icmp_recv_echo_request(dgram);
+	case ICMP_ECHO_REPLY:
+		return icmp_recv_echo_reply(dgram);
+	default:
+		break;
+	}
+
+	return EINVAL;
+}
+
+static int icmp_recv_echo_request(inet_dgram_t *dgram)
+{
+	icmp_echo_t *request, *reply;
+	uint16_t checksum;
+	size_t size;
+	inet_dgram_t rdgram;
+	int rc;
+
+	log_msg(LVL_DEBUG, "icmp_recv_echo_request()");
+
+	if (dgram->size < sizeof(icmp_echo_t))
+		return EINVAL;
+
+	request = (icmp_echo_t *)dgram->data;
+	size = dgram->size;
+
+	reply = calloc(size, 1);
+	if (reply == NULL)
+		return ENOMEM;
+
+	memcpy(reply, request, size);
+
+	reply->type = ICMP_ECHO_REPLY;
+	reply->code = 0;
+	reply->checksum = 0;
+
+	checksum = inet_checksum_calc(INET_CHECKSUM_INIT, reply, size);
+	reply->checksum = host2uint16_t_be(checksum);
+
+	rdgram.src = dgram->dest;
+	rdgram.dest = dgram->src;
+	rdgram.tos = ICMP_TOS;
+	rdgram.data = reply;
+	rdgram.size = size;
+
+	rc = inet_route_packet(&rdgram, IP_PROTO_ICMP, INET_TTL_MAX, 0);
+
+	free(reply);
+
+	return rc;
+}
+
+static int icmp_recv_echo_reply(inet_dgram_t *dgram)
+{
+	icmp_echo_t *reply;
+	inetping_sdu_t sdu;
+	uint16_t ident;
+
+	log_msg(LVL_DEBUG, "icmp_recv_echo_reply()");
+
+	if (dgram->size < sizeof(icmp_echo_t))
+		return EINVAL;
+
+	reply = (icmp_echo_t *)dgram->data;
+
+	sdu.src = dgram->src;
+	sdu.dest = dgram->dest;
+	sdu.seq_no = uint16_t_be2host(reply->seq_no);
+	sdu.data = reply + sizeof(icmp_echo_t);
+	sdu.size = dgram->size - sizeof(icmp_echo_t);
+	ident = uint16_t_be2host(reply->ident);
+
+	return inetping_recv(ident, &sdu);
+}
+
+int icmp_ping_send(uint16_t ident, inetping_sdu_t *sdu)
+{
+	inet_dgram_t dgram;
+	icmp_echo_t *request;
+	void *rdata;
+	size_t rsize;
+	uint16_t checksum;
+	int rc;
+
+	rsize = sizeof(icmp_echo_t) + sdu->size;
+	rdata = calloc(rsize, 1);
+	if (rdata == NULL)
+		return ENOMEM;
+
+	request = (icmp_echo_t *)rdata;
+
+	request->type = ICMP_ECHO_REQUEST;
+	request->code = 0;
+	request->checksum = 0;
+	request->ident = host2uint16_t_be(ident);
+	request->seq_no = host2uint16_t_be(sdu->seq_no);
+
+	memcpy(rdata + sizeof(icmp_echo_t), sdu->data, sdu->size);
+
+	checksum = inet_checksum_calc(INET_CHECKSUM_INIT, rdata, rsize);
+	request->checksum = host2uint16_t_be(checksum);
+
+	dgram.src = sdu->src;
+	dgram.dest = sdu->dest;
+	dgram.tos = ICMP_TOS;
+	dgram.data = rdata;
+	dgram.size = rsize;
+
+	rc = inet_route_packet(&dgram, IP_PROTO_ICMP, INET_TTL_MAX, 0);
+
+	free(rdata);
+	return rc;
+}
+
+/** @}
+ */
Index: uspace/srv/net/inetsrv/icmp.h
===================================================================
--- uspace/srv/net/inetsrv/icmp.h	(revision ce1836ed04784ea0bacc387ac80bfe852f36119b)
+++ uspace/srv/net/inetsrv/icmp.h	(revision ce1836ed04784ea0bacc387ac80bfe852f36119b)
@@ -0,0 +1,48 @@
+/*
+ * 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 ICMP_H_
+#define ICMP_H_
+
+#include "inetsrv.h"
+
+extern int icmp_recv(inet_dgram_t *);
+extern int icmp_ping_send(uint16_t, inetping_sdu_t *);
+
+#endif
+
+/** @}
+ */
Index: uspace/srv/net/inetsrv/icmp_std.h
===================================================================
--- uspace/srv/net/inetsrv/icmp_std.h	(revision ce1836ed04784ea0bacc387ac80bfe852f36119b)
+++ uspace/srv/net/inetsrv/icmp_std.h	(revision ce1836ed04784ea0bacc387ac80bfe852f36119b)
@@ -0,0 +1,70 @@
+/*
+ * 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 ICMP standard definitions
+ *
+ */
+
+#ifndef ICMP_STD_H_
+#define ICMP_STD_H_
+
+#include <sys/types.h>
+
+#define IP_PROTO_ICMP 1
+
+/** Type of service used for ICMP */
+#define ICMP_TOS	0
+
+/** ICMP message type */
+enum icmp_type {
+	ICMP_ECHO_REPLY   = 0,
+	ICMP_ECHO_REQUEST = 8
+};
+
+/** ICMP Echo Request or Reply message header */
+typedef struct {
+	/** ICMP message type */
+	uint8_t type;
+	/** Code (0) */
+	uint8_t code;
+	/** Internet checksum of the ICMP message */
+	uint16_t checksum;
+	/** Indentifier */
+	uint16_t ident;
+	/** Sequence number */
+	uint16_t seq_no;
+} icmp_echo_t;
+
+#endif
+
+/** @}
+ */
Index: uspace/srv/net/inetsrv/inet_link.c
===================================================================
--- uspace/srv/net/inetsrv/inet_link.c	(revision ce1836ed04784ea0bacc387ac80bfe852f36119b)
+++ uspace/srv/net/inetsrv/inet_link.c	(revision ce1836ed04784ea0bacc387ac80bfe852f36119b)
@@ -0,0 +1,317 @@
+/*
+ * 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 <bool.h>
+#include <errno.h>
+#include <fibril_synch.h>
+#include <inet/iplink.h>
+#include <io/log.h>
+#include <loc.h>
+#include <stdlib.h>
+#include <str.h>
+
+#include "addrobj.h"
+#include "inetsrv.h"
+#include "inet_link.h"
+#include "pdu.h"
+
+static int inet_link_open(service_id_t sid);
+static int inet_iplink_recv(iplink_t *ilink, iplink_sdu_t *sdu);
+
+static iplink_ev_ops_t inet_iplink_ev_ops = {
+	.recv = inet_iplink_recv
+};
+
+static LIST_INITIALIZE(inet_link_list);
+static FIBRIL_MUTEX_INITIALIZE(inet_discovery_lock);
+
+static int inet_iplink_recv(iplink_t *iplink, iplink_sdu_t *sdu)
+{
+	inet_packet_t packet;
+	int rc;
+
+	log_msg(LVL_DEBUG, "inet_iplink_recv()");
+	rc = inet_pdu_decode(sdu->data, sdu->size, &packet);
+	if (rc != EOK) {
+		log_msg(LVL_DEBUG, "failed decoding PDU");
+		return rc;
+	}
+
+	log_msg(LVL_DEBUG, "call inet_recv_packet()");
+	rc = inet_recv_packet(&packet);
+	log_msg(LVL_DEBUG, "call inet_recv_packet -> %d", rc);
+	free(packet.data);
+
+	return rc;
+}
+
+static int inet_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(&inet_discovery_lock);
+
+	rc = loc_category_get_id("iplink", &iplink_cat, IPC_FLAG_BLOCKING);
+	if (rc != EOK) {
+		log_msg(LVL_ERROR, "Failed resolving category 'iplink'.");
+		fibril_mutex_unlock(&inet_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(&inet_discovery_lock);
+		return EIO;
+	}
+
+	for (i = 0; i < count; i++) {
+		already_known = false;
+
+		list_foreach(inet_link_list, ilink_link) {
+			inet_link_t *ilink = list_get_instance(ilink_link,
+			    inet_link_t, link_list);
+			if (ilink->svc_id == svcs[i]) {
+				already_known = true;
+				break;
+			}
+		}
+
+		if (!already_known) {
+			log_msg(LVL_DEBUG, "Found IP link '%lu'",
+			    (unsigned long) svcs[i]);
+			rc = inet_link_open(svcs[i]);
+			if (rc != EOK)
+				log_msg(LVL_ERROR, "Could not open IP link.");
+		}
+	}
+
+	fibril_mutex_unlock(&inet_discovery_lock);
+	return EOK;
+}
+
+static inet_link_t *inet_link_new(void)
+{
+	inet_link_t *ilink = calloc(1, sizeof(inet_link_t));
+
+	if (ilink == NULL) {
+		log_msg(LVL_ERROR, "Failed allocating link structure. "
+		    "Out of memory.");
+		return NULL;
+	}
+
+	link_initialize(&ilink->link_list);
+
+	return ilink;
+}
+
+static void inet_link_delete(inet_link_t *ilink)
+{
+	if (ilink->svc_name != NULL)
+		free(ilink->svc_name);
+	free(ilink);
+}
+
+static int inet_link_open(service_id_t sid)
+{
+	inet_link_t *ilink;
+	iplink_addr_t iaddr;
+	int rc;
+
+	log_msg(LVL_DEBUG, "inet_link_open()");
+	ilink = inet_link_new();
+	if (ilink == NULL)
+		return ENOMEM;
+
+	ilink->svc_id = sid;
+	ilink->iplink = NULL;
+
+	rc = loc_service_get_name(sid, &ilink->svc_name);
+	if (rc != EOK) {
+		log_msg(LVL_ERROR, "Failed getting service name.");
+		goto error;
+	}
+
+	ilink->sess = loc_service_connect(EXCHANGE_SERIALIZE, sid, 0);
+	if (ilink->sess == NULL) {
+		log_msg(LVL_ERROR, "Failed connecting '%s'", ilink->svc_name);
+		goto error;
+	}
+
+	rc = iplink_open(ilink->sess, &inet_iplink_ev_ops, &ilink->iplink);
+	if (rc != EOK) {
+		log_msg(LVL_ERROR, "Failed opening IP link '%s'",
+		    ilink->svc_name);
+		goto error;
+	}
+
+	rc = iplink_get_mtu(ilink->iplink, &ilink->def_mtu);
+	if (rc != EOK) {
+		log_msg(LVL_ERROR, "Failed determinning MTU of link '%s'",
+		    ilink->svc_name);
+		goto error;
+	}
+
+	log_msg(LVL_DEBUG, "Opened IP link '%s'", ilink->svc_name);
+	list_append(&ilink->link_list, &inet_link_list);
+
+	inet_addrobj_t *addr;
+
+	static int first = 1;
+	/* XXX For testing: set static IP address 192.168.0.4/24 */
+	addr = inet_addrobj_new();
+	if (first) {
+		addr->naddr.ipv4 = (127 << 24) + (0 << 16) + (0 << 8) + 1;
+		first = 0;
+	} else {
+		addr->naddr.ipv4 = (192 << 24) + (168 << 16) + (0 << 8) + 4;
+	}
+	addr->naddr.bits = 24;
+	addr->ilink = ilink;
+	addr->name = str_dup("v4a");
+	rc = inet_addrobj_add(addr);
+	if (rc != EOK) {
+		log_msg(LVL_ERROR, "Failed setting IP address on internet link.");
+		inet_addrobj_delete(addr);
+		/* XXX Roll back */
+		return rc;
+	}
+
+	iaddr.ipv4 = addr->naddr.ipv4;
+	rc = iplink_addr_add(ilink->iplink, &iaddr);
+	if (rc != EOK) {
+		log_msg(LVL_ERROR, "Failed setting IP address on internet link.");
+		inet_addrobj_remove(addr);
+		inet_addrobj_delete(addr);
+		/* XXX Roll back */
+		return rc;
+	}
+
+	return EOK;
+
+error:
+	if (ilink->iplink != NULL)
+		iplink_close(ilink->iplink);
+	inet_link_delete(ilink);
+	return rc;
+}
+
+static void inet_link_cat_change_cb(void)
+{
+	(void) inet_link_check_new();
+}
+
+int inet_link_discovery_start(void)
+{
+	int rc;
+
+	rc = loc_register_cat_change_cb(inet_link_cat_change_cb);
+	if (rc != EOK) {
+		log_msg(LVL_ERROR, "Failed registering callback for IP link "
+		    "discovery (%d).", rc);
+		return rc;
+	}
+
+	return inet_link_check_new();
+}
+
+/** Send datagram over Internet link */
+int inet_link_send_dgram(inet_link_t *ilink, inet_addr_t *lsrc,
+    inet_addr_t *ldest, inet_dgram_t *dgram, uint8_t proto, uint8_t ttl, int df)
+{
+	iplink_sdu_t sdu;
+	inet_packet_t packet;
+	int rc;
+	size_t offs, roffs;
+
+	/*
+	 * Fill packet structure. Fragmentation is performed by
+	 * inet_pdu_encode().
+	 */
+	packet.src = dgram->src;
+	packet.dest = dgram->dest;
+	packet.tos = dgram->tos;
+	packet.proto = proto;
+	packet.ttl = ttl;
+	packet.df = df;
+	packet.data = dgram->data;
+	packet.size = dgram->size;
+
+	sdu.lsrc.ipv4 = lsrc->ipv4;
+	sdu.ldest.ipv4 = ldest->ipv4;
+
+	offs = 0;
+	do {
+		/* Encode one fragment */
+		rc = inet_pdu_encode(&packet, offs, ilink->def_mtu, &sdu.data,
+		    &sdu.size, &roffs);
+		if (rc != EOK)
+			return rc;
+
+		/* Send the PDU */
+		rc = iplink_send(ilink->iplink, &sdu);
+		free(sdu.data);
+
+		offs = roffs;
+	} while (offs < packet.size);
+
+	return rc;
+}
+
+inet_link_t *inet_link_get_by_id(sysarg_t link_id)
+{
+	fibril_mutex_lock(&inet_discovery_lock);
+
+	list_foreach(inet_link_list, elem) {
+		inet_link_t *ilink = list_get_instance(elem, inet_link_t,
+		    link_list);
+
+		if (ilink->svc_id == link_id) {
+			fibril_mutex_unlock(&inet_discovery_lock);
+			return ilink;
+		}
+	}
+
+	fibril_mutex_unlock(&inet_discovery_lock);
+	return NULL;
+}
+
+/** @}
+ */
Index: uspace/srv/net/inetsrv/inet_link.h
===================================================================
--- uspace/srv/net/inetsrv/inet_link.h	(revision ce1836ed04784ea0bacc387ac80bfe852f36119b)
+++ uspace/srv/net/inetsrv/inet_link.h	(revision ce1836ed04784ea0bacc387ac80bfe852f36119b)
@@ -0,0 +1,51 @@
+/*
+ * 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 INET_LINK_H_
+#define INET_LINK_H_
+
+#include <sys/types.h>
+#include "inetsrv.h"
+
+extern int inet_link_discovery_start(void);
+extern int inet_link_send_dgram(inet_link_t *, inet_addr_t *,
+    inet_addr_t *, inet_dgram_t *, uint8_t, uint8_t, int);
+extern inet_link_t *inet_link_get_by_id(sysarg_t);
+
+#endif
+
+/** @}
+ */
Index: uspace/srv/net/inetsrv/inet_std.h
===================================================================
--- uspace/srv/net/inetsrv/inet_std.h	(revision ce1836ed04784ea0bacc387ac80bfe852f36119b)
+++ uspace/srv/net/inetsrv/inet_std.h	(revision ce1836ed04784ea0bacc387ac80bfe852f36119b)
@@ -0,0 +1,98 @@
+/*
+ * 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 IP header definitions
+ *
+ */
+
+#ifndef INET_STD_H_
+#define INET_STD_H_
+
+#include <sys/types.h>
+
+/** Internet Datagram header (fixed part) */
+typedef struct {
+	/** Version, Internet Header Length */
+	uint8_t ver_ihl;
+	/* Type of Service */
+	uint8_t tos;
+	/** Total Length */
+	uint16_t tot_len;
+	/** Identification */
+	uint16_t id;
+	/** Flags, Fragment Offset */
+	uint16_t flags_foff;
+	/** Time to Live */
+	uint8_t ttl;
+	/** Protocol */
+	uint8_t proto;
+	/** Header Checksum */
+	uint16_t chksum;
+	/** Source Address */
+	uint32_t src_addr;
+	/** Destination Address */
+	uint32_t dest_addr;
+} ip_header_t;
+
+/** Bits in ip_header_t.ver_ihl */
+enum ver_ihl_bits {
+	/** Version, highest bit */
+	VI_VERSION_h = 7,
+	/** Version, lowest bit */
+	VI_VERSION_l = 4,
+	/** Internet Header Length, highest bit */
+	VI_IHL_h     = 3,
+	/** Internet Header Length, lowest bit */
+	VI_IHL_l     = 0
+};
+
+/** Bits in ip_header_t.flags_foff */
+enum flags_foff_bits {
+	/** Reserved, must be zero */
+	FF_FLAG_RSVD = 15,
+	/** Don't Fragment */
+	FF_FLAG_DF = 14,
+	/** More Fragments */
+	FF_FLAG_MF = 13,
+	/** Fragment Offset, highest bit */
+	FF_FRAGOFF_h = 12,
+	/** Fragment Offset, lowest bit */
+	FF_FRAGOFF_l = 0
+};
+
+/** Fragment offset is expressed in units of 8 bytes */
+#define FRAG_OFFS_UNIT 8
+
+#endif
+
+/** @}
+ */
Index: uspace/srv/net/inetsrv/inet_util.c
===================================================================
--- uspace/srv/net/inetsrv/inet_util.c	(revision ce1836ed04784ea0bacc387ac80bfe852f36119b)
+++ uspace/srv/net/inetsrv/inet_util.c	(revision ce1836ed04784ea0bacc387ac80bfe852f36119b)
@@ -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
+ */
+
+#include <assert.h>
+#include <bitops.h>
+#include <sys/types.h>
+#include "inet_util.h"
+
+uint32_t inet_netmask(int bits)
+{
+	assert(bits >= 0);
+	assert(bits < 32);
+
+	if (bits == 0)
+		return 0;
+	else
+		return BIT_RANGE(uint32_t, 31, 31 - (bits - 1));
+}
+
+/** @}
+ */
Index: uspace/srv/net/inetsrv/inet_util.h
===================================================================
--- uspace/srv/net/inetsrv/inet_util.h	(revision ce1836ed04784ea0bacc387ac80bfe852f36119b)
+++ uspace/srv/net/inetsrv/inet_util.h	(revision ce1836ed04784ea0bacc387ac80bfe852f36119b)
@@ -0,0 +1,47 @@
+/*
+ * 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 INET_UTIL_H_
+#define INET_UTIL_H_
+
+#include <sys/types.h>
+
+uint32_t inet_netmask(int bits);
+
+#endif
+
+/** @}
+ */
Index: uspace/srv/net/inetsrv/inetcfg.c
===================================================================
--- uspace/srv/net/inetsrv/inetcfg.c	(revision ce1836ed04784ea0bacc387ac80bfe852f36119b)
+++ uspace/srv/net/inetsrv/inetcfg.c	(revision ce1836ed04784ea0bacc387ac80bfe852f36119b)
@@ -0,0 +1,636 @@
+/*
+ * 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 <errno.h>
+#include <macros.h>
+#include <io/log.h>
+#include <ipc/inet.h>
+#include <loc.h>
+#include <stdlib.h>
+#include <str.h>
+#include <sys/types.h>
+
+#include "addrobj.h"
+#include "inetsrv.h"
+#include "inet_link.h"
+#include "inetcfg.h"
+#include "sroute.h"
+
+static int inetcfg_addr_create_static(char *name, inet_naddr_t *naddr,
+    sysarg_t link_id, sysarg_t *addr_id)
+{
+	inet_link_t *ilink;
+	inet_addrobj_t *addr;
+	iplink_addr_t iaddr;
+	int rc;
+
+	ilink = inet_link_get_by_id(link_id);
+	if (ilink == NULL) {
+		log_msg(LVL_DEBUG, "Link %lu not found.",
+		    (unsigned long) link_id);
+		return ENOENT;
+	}
+
+	addr = inet_addrobj_new();
+	if (addr == NULL) {
+		*addr_id = 0;
+		return ENOMEM;
+	}
+
+	addr->naddr = *naddr;
+	addr->ilink = ilink;
+	addr->name = str_dup(name);
+	rc = inet_addrobj_add(addr);
+	if (rc != EOK) {
+		log_msg(LVL_DEBUG, "Duplicate address name '%s'.", addr->name);
+		inet_addrobj_delete(addr);
+		return rc;
+	}
+
+	iaddr.ipv4 = addr->naddr.ipv4;
+	rc = iplink_addr_add(ilink->iplink, &iaddr);
+	if (rc != EOK) {
+		log_msg(LVL_ERROR, "Failed setting IP address on internet link.");
+		inet_addrobj_remove(addr);
+		inet_addrobj_delete(addr);
+		return rc;
+	}
+
+	return EOK;
+}
+
+static int inetcfg_addr_delete(sysarg_t addr_id)
+{
+	inet_addrobj_t *addr;
+
+	addr = inet_addrobj_get_by_id(addr_id);
+	if (addr == NULL)
+		return ENOENT;
+
+	inet_addrobj_remove(addr);
+	inet_addrobj_delete(addr);
+
+	return EOK;
+}
+
+static int inetcfg_addr_get(sysarg_t addr_id, inet_addr_info_t *ainfo)
+{
+	inet_addrobj_t *addr;
+
+	addr = inet_addrobj_get_by_id(addr_id);
+	if (addr == NULL)
+		return ENOENT;
+
+	ainfo->naddr = addr->naddr;
+	ainfo->ilink = addr->ilink->svc_id;
+	ainfo->name = str_dup(addr->name);
+
+	return EOK;
+}
+
+static int inetcfg_addr_get_id(char *name, sysarg_t link_id, sysarg_t *addr_id)
+{
+	inet_link_t *ilink;
+	inet_addrobj_t *addr;
+
+	ilink = inet_link_get_by_id(link_id);
+	if (ilink == NULL) {
+		log_msg(LVL_DEBUG, "Link %zu not found.", (size_t) link_id);
+		return ENOENT;
+	}
+
+	addr = inet_addrobj_find_by_name(name, ilink);
+	if (addr == NULL) {
+		log_msg(LVL_DEBUG, "Address '%s' not found.", name);
+		return ENOENT;
+	}
+
+	*addr_id = addr->id;
+	return EOK;
+}
+
+static int inetcfg_get_addr_list(sysarg_t **addrs, size_t *count)
+{
+	return inet_addrobj_get_id_list(addrs, count);
+}
+
+static int inetcfg_get_link_list(sysarg_t **addrs, size_t *count)
+{
+	return ENOTSUP;
+}
+
+static int inetcfg_get_sroute_list(sysarg_t **sroutes, size_t *count)
+{
+	return inet_sroute_get_id_list(sroutes, count);
+}
+
+static int inetcfg_link_get(sysarg_t link_id, inet_link_info_t *linfo)
+{
+	inet_link_t *ilink;
+
+	ilink = inet_link_get_by_id(link_id);
+	if (ilink == NULL) {
+		return ENOENT;
+	}
+
+	linfo->name = str_dup(ilink->svc_name);
+	linfo->def_mtu = ilink->def_mtu;
+	return EOK;
+}
+
+static int inetcfg_sroute_create(char *name, inet_naddr_t *dest,
+    inet_addr_t *router, sysarg_t *sroute_id)
+{
+	inet_sroute_t *sroute;
+
+	sroute = inet_sroute_new();
+	if (sroute == NULL) {
+		*sroute_id = 0;
+		return ENOMEM;
+	}
+
+	sroute->dest = *dest;
+	sroute->router = *router;
+	sroute->name = str_dup(name);
+	inet_sroute_add(sroute);
+
+	*sroute_id = sroute->id;
+	return EOK;
+}
+
+static int inetcfg_sroute_delete(sysarg_t sroute_id)
+{
+	inet_sroute_t *sroute;
+
+	sroute = inet_sroute_get_by_id(sroute_id);
+	if (sroute == NULL)
+		return ENOENT;
+
+	inet_sroute_remove(sroute);
+	inet_sroute_delete(sroute);
+
+	return EOK;
+}
+
+static int inetcfg_sroute_get(sysarg_t sroute_id, inet_sroute_info_t *srinfo)
+{
+	inet_sroute_t *sroute;
+
+	sroute = inet_sroute_get_by_id(sroute_id);
+	if (sroute == NULL)
+		return ENOENT;
+
+	srinfo->dest = sroute->dest;
+	srinfo->router = sroute->router;
+	srinfo->name = str_dup(sroute->name);
+
+	return EOK;
+}
+
+static int inetcfg_sroute_get_id(char *name, sysarg_t *sroute_id)
+{
+	inet_sroute_t *sroute;
+
+	sroute = inet_sroute_find_by_name(name);
+	if (sroute == NULL) {
+		log_msg(LVL_DEBUG, "Static route '%s' not found.", name);
+		return ENOENT;
+	}
+
+	*sroute_id = sroute->id;
+	return EOK;
+}
+
+static void inetcfg_addr_create_static_srv(ipc_callid_t callid,
+    ipc_call_t *call)
+{
+	char *name;
+	inet_naddr_t naddr;
+	sysarg_t link_id;
+	sysarg_t addr_id;
+	int rc;
+
+	log_msg(LVL_DEBUG, "inetcfg_addr_create_static_srv()");
+
+	rc = async_data_write_accept((void **) &name, true, 0, LOC_NAME_MAXLEN,
+	    0, NULL);
+	if (rc != EOK) {
+		async_answer_0(callid, rc);
+		return;
+	}
+
+	naddr.ipv4 = IPC_GET_ARG1(*call);
+	naddr.bits = IPC_GET_ARG2(*call);
+	link_id    = IPC_GET_ARG3(*call);
+
+	addr_id = 0;
+	rc = inetcfg_addr_create_static(name, &naddr, link_id, &addr_id);
+	free(name);
+	async_answer_1(callid, rc, addr_id);
+}
+
+static void inetcfg_addr_delete_srv(ipc_callid_t callid, ipc_call_t *call)
+{
+	sysarg_t addr_id;
+	int rc;
+
+	log_msg(LVL_DEBUG, "inetcfg_addr_delete_srv()");
+
+	addr_id = IPC_GET_ARG1(*call);
+
+	rc = inetcfg_addr_delete(addr_id);
+	async_answer_0(callid, rc);
+}
+
+static void inetcfg_addr_get_srv(ipc_callid_t callid, ipc_call_t *call)
+{
+	ipc_callid_t rcallid;
+	size_t max_size;
+
+	sysarg_t addr_id;
+	inet_addr_info_t ainfo;
+	int rc;
+
+	addr_id = IPC_GET_ARG1(*call);
+	log_msg(LVL_DEBUG, "inetcfg_addr_get_srv()");
+
+	ainfo.naddr.ipv4 = 0;
+	ainfo.naddr.bits = 0;
+	ainfo.ilink = 0;
+	ainfo.name = NULL;
+
+	if (!async_data_read_receive(&rcallid, &max_size)) {
+		async_answer_0(rcallid, EREFUSED);
+		async_answer_0(callid, EREFUSED);
+		return;
+	}
+
+	rc = inetcfg_addr_get(addr_id, &ainfo);
+	if (rc != EOK) {
+		async_answer_0(callid, rc);
+		return;
+	}
+
+	sysarg_t retval = async_data_read_finalize(rcallid, ainfo.name,
+	    min(max_size, str_size(ainfo.name)));
+	free(ainfo.name);
+
+	async_answer_3(callid, retval, ainfo.naddr.ipv4, ainfo.naddr.bits,
+	    ainfo.ilink);
+}
+
+static void inetcfg_addr_get_id_srv(ipc_callid_t callid, ipc_call_t *call)
+{
+	char *name;
+	sysarg_t link_id;
+	sysarg_t addr_id;
+	int rc;
+
+	log_msg(LVL_DEBUG, "inetcfg_addr_get_id_srv()");
+
+	link_id = IPC_GET_ARG1(*call);
+
+	rc = async_data_write_accept((void **) &name, true, 0, LOC_NAME_MAXLEN,
+	    0, NULL);
+	if (rc != EOK) {
+		async_answer_0(callid, rc);
+		return;
+	}
+
+	addr_id = 0;
+	rc = inetcfg_addr_get_id(name, link_id, &addr_id);
+	free(name);
+	async_answer_1(callid, rc, addr_id);
+}
+
+static void inetcfg_get_addr_list_srv(ipc_callid_t callid, ipc_call_t *call)
+{
+	ipc_callid_t rcallid;
+	size_t count;
+	size_t max_size;
+	size_t act_size;
+	size_t size;
+	sysarg_t *id_buf;
+	int rc;
+
+	log_msg(LVL_DEBUG, "inetcfg_get_addr_list_srv()");
+
+	if (!async_data_read_receive(&rcallid, &max_size)) {
+		async_answer_0(rcallid, EREFUSED);
+		async_answer_0(callid, EREFUSED);
+		return;
+	}
+
+	rc = inetcfg_get_addr_list(&id_buf, &count);
+	if (rc != EOK) {
+		async_answer_0(rcallid, rc);
+		async_answer_0(callid, rc);
+		return;
+	}
+
+	act_size = count * sizeof(sysarg_t);
+	size = min(act_size, max_size);
+
+	sysarg_t retval = async_data_read_finalize(rcallid, id_buf, size);
+	free(id_buf);
+
+	async_answer_1(callid, retval, act_size);
+}
+
+
+static void inetcfg_get_link_list_srv(ipc_callid_t callid, ipc_call_t *call)
+{
+	ipc_callid_t rcallid;
+	size_t max_size;
+	size_t act_size;
+	size_t size;
+	sysarg_t *id_buf;
+	int rc;
+
+	log_msg(LVL_DEBUG, "inetcfg_get_link_list_srv()");
+
+	if (!async_data_read_receive(&rcallid, &max_size)) {
+		async_answer_0(rcallid, EREFUSED);
+		async_answer_0(callid, EREFUSED);
+		return;
+	}
+
+	rc = inetcfg_get_link_list(&id_buf, &act_size);
+	if (rc != EOK) {
+		async_answer_0(rcallid, rc);
+		async_answer_0(callid, rc);
+		return;
+	}
+
+	size = min(act_size, max_size);
+
+	sysarg_t retval = async_data_read_finalize(rcallid, id_buf, size);
+	free(id_buf);
+
+	async_answer_1(callid, retval, act_size);
+}
+
+static void inetcfg_get_sroute_list_srv(ipc_callid_t callid, ipc_call_t *call)
+{
+	ipc_callid_t rcallid;
+	size_t count;
+	size_t max_size;
+	size_t act_size;
+	size_t size;
+	sysarg_t *id_buf;
+	int rc;
+
+	log_msg(LVL_DEBUG, "inetcfg_get_sroute_list_srv()");
+
+	if (!async_data_read_receive(&rcallid, &max_size)) {
+		async_answer_0(rcallid, EREFUSED);
+		async_answer_0(callid, EREFUSED);
+		return;
+	}
+
+	rc = inetcfg_get_sroute_list(&id_buf, &count);
+	if (rc != EOK) {
+		async_answer_0(rcallid, rc);
+		async_answer_0(callid, rc);
+		return;
+	}
+
+	act_size = count * sizeof(sysarg_t);
+	size = min(act_size, max_size);
+
+	sysarg_t retval = async_data_read_finalize(rcallid, id_buf, size);
+	free(id_buf);
+
+	async_answer_1(callid, retval, act_size);
+}
+
+static void inetcfg_link_get_srv(ipc_callid_t callid, ipc_call_t *call)
+{
+	ipc_callid_t rcallid;
+	size_t max_size;
+
+	sysarg_t link_id;
+	inet_link_info_t linfo;
+	int rc;
+
+	link_id = IPC_GET_ARG1(*call);
+	log_msg(LVL_DEBUG, "inetcfg_link_get_srv()");
+
+	linfo.name = NULL;
+
+	if (!async_data_read_receive(&rcallid, &max_size)) {
+		async_answer_0(rcallid, EREFUSED);
+		async_answer_0(callid, EREFUSED);
+		return;
+	}
+
+	rc = inetcfg_link_get(link_id, &linfo);
+	if (rc != EOK) {
+		async_answer_0(rcallid, rc);
+		async_answer_0(callid, rc);
+		return;
+	}
+
+	sysarg_t retval = async_data_read_finalize(rcallid, linfo.name,
+	    min(max_size, str_size(linfo.name)));
+	free(linfo.name);
+
+	async_answer_1(callid, retval, linfo.def_mtu);
+}
+
+static void inetcfg_sroute_create_srv(ipc_callid_t callid,
+    ipc_call_t *call)
+{
+	char *name;
+	inet_naddr_t dest;
+	inet_addr_t router;
+	sysarg_t sroute_id;
+	int rc;
+
+	log_msg(LVL_DEBUG, "inetcfg_sroute_create_srv()");
+
+	rc = async_data_write_accept((void **) &name, true, 0, LOC_NAME_MAXLEN,
+	    0, NULL);
+	if (rc != EOK) {
+		async_answer_0(callid, rc);
+		return;
+	}
+
+	dest.ipv4   = IPC_GET_ARG1(*call);
+	dest.bits   = IPC_GET_ARG2(*call);
+	router.ipv4 = IPC_GET_ARG3(*call);
+
+	sroute_id = 0;
+	rc = inetcfg_sroute_create(name, &dest, &router, &sroute_id);
+	free(name);
+	async_answer_1(callid, rc, sroute_id);
+}
+
+static void inetcfg_sroute_delete_srv(ipc_callid_t callid, ipc_call_t *call)
+{
+	sysarg_t sroute_id;
+	int rc;
+
+	log_msg(LVL_DEBUG, "inetcfg_sroute_delete_srv()");
+
+	sroute_id = IPC_GET_ARG1(*call);
+
+	rc = inetcfg_sroute_delete(sroute_id);
+	async_answer_0(callid, rc);
+}
+
+static void inetcfg_sroute_get_srv(ipc_callid_t callid, ipc_call_t *call)
+{
+	ipc_callid_t rcallid;
+	size_t max_size;
+
+	sysarg_t sroute_id;
+	inet_sroute_info_t srinfo;
+	int rc;
+
+	sroute_id = IPC_GET_ARG1(*call);
+	log_msg(LVL_DEBUG, "inetcfg_sroute_get_srv()");
+
+	srinfo.dest.ipv4 = 0;
+	srinfo.dest.bits = 0;
+	srinfo.router.ipv4 = 0;
+	srinfo.name = NULL;
+
+	if (!async_data_read_receive(&rcallid, &max_size)) {
+		async_answer_0(rcallid, EREFUSED);
+		async_answer_0(callid, EREFUSED);
+		return;
+	}
+
+	rc = inetcfg_sroute_get(sroute_id, &srinfo);
+	if (rc != EOK) {
+		async_answer_0(callid, rc);
+		return;
+	}
+
+	sysarg_t retval = async_data_read_finalize(rcallid, srinfo.name,
+	    min(max_size, str_size(srinfo.name)));
+	free(srinfo.name);
+
+	async_answer_3(callid, retval, srinfo.dest.ipv4, srinfo.dest.bits,
+	    srinfo.router.ipv4);
+}
+
+static void inetcfg_sroute_get_id_srv(ipc_callid_t callid, ipc_call_t *call)
+{
+	char *name;
+	sysarg_t sroute_id;
+	int rc;
+
+	log_msg(LVL_DEBUG, "inetcfg_sroute_get_id_srv()");
+
+	rc = async_data_write_accept((void **) &name, true, 0, LOC_NAME_MAXLEN,
+	    0, NULL);
+	if (rc != EOK) {
+		async_answer_0(callid, rc);
+		return;
+	}
+
+	sroute_id = 0;
+	rc = inetcfg_sroute_get_id(name, &sroute_id);
+	free(name);
+	async_answer_1(callid, rc, sroute_id);
+}
+
+void inet_cfg_conn(ipc_callid_t iid, ipc_call_t *icall, void *arg)
+{
+	log_msg(LVL_DEBUG, "inet_cfg_conn()");
+
+	/* Accept the connection */
+	async_answer_0(iid, EOK);
+
+	while (true) {
+		ipc_call_t call;
+		ipc_callid_t callid = async_get_call(&call);
+		sysarg_t method = IPC_GET_IMETHOD(call);
+
+		if (!method) {
+			/* The other side has hung up */
+			async_answer_0(callid, EOK);
+			return;
+		}
+
+		switch (method) {
+		case INETCFG_ADDR_CREATE_STATIC:
+			inetcfg_addr_create_static_srv(callid, &call);
+			break;
+		case INETCFG_ADDR_DELETE:
+			inetcfg_addr_delete_srv(callid, &call);
+			break;
+		case INETCFG_ADDR_GET:
+			inetcfg_addr_get_srv(callid, &call);
+			break;
+		case INETCFG_ADDR_GET_ID:
+			inetcfg_addr_get_id_srv(callid, &call);
+			break;
+		case INETCFG_GET_ADDR_LIST:
+			inetcfg_get_addr_list_srv(callid, &call);
+			break;
+		case INETCFG_GET_LINK_LIST:
+			inetcfg_get_link_list_srv(callid, &call);
+			break;
+		case INETCFG_GET_SROUTE_LIST:
+			inetcfg_get_sroute_list_srv(callid, &call);
+			break;
+		case INETCFG_LINK_GET:
+			inetcfg_link_get_srv(callid, &call);
+			break;
+		case INETCFG_SROUTE_CREATE:
+			inetcfg_sroute_create_srv(callid, &call);
+			break;
+		case INETCFG_SROUTE_DELETE:
+			inetcfg_sroute_delete_srv(callid, &call);
+			break;
+		case INETCFG_SROUTE_GET:
+			inetcfg_sroute_get_srv(callid, &call);
+			break;
+		case INETCFG_SROUTE_GET_ID:
+			inetcfg_sroute_get_id_srv(callid, &call);
+			break;
+		default:
+			async_answer_0(callid, EINVAL);
+		}
+	}
+}
+
+/** @}
+ */
Index: uspace/srv/net/inetsrv/inetcfg.h
===================================================================
--- uspace/srv/net/inetsrv/inetcfg.h	(revision ce1836ed04784ea0bacc387ac80bfe852f36119b)
+++ uspace/srv/net/inetsrv/inetcfg.h	(revision ce1836ed04784ea0bacc387ac80bfe852f36119b)
@@ -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 INETCFG_H_
+#define INETCFG_H_
+
+extern void inet_cfg_conn(ipc_callid_t, ipc_call_t *, void *);
+
+#endif
+
+/** @}
+ */
Index: uspace/srv/net/inetsrv/inetping.c
===================================================================
--- uspace/srv/net/inetsrv/inetping.c	(revision ce1836ed04784ea0bacc387ac80bfe852f36119b)
+++ uspace/srv/net/inetsrv/inetping.c	(revision ce1836ed04784ea0bacc387ac80bfe852f36119b)
@@ -0,0 +1,230 @@
+/*
+ * 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 <errno.h>
+#include <fibril_synch.h>
+#include <io/log.h>
+#include <ipc/inet.h>
+#include <loc.h>
+#include <stdlib.h>
+#include <sys/types.h>
+
+#include "icmp.h"
+#include "icmp_std.h"
+#include "inetsrv.h"
+#include "inetping.h"
+
+static FIBRIL_MUTEX_INITIALIZE(client_list_lock);
+static LIST_INITIALIZE(client_list);
+
+/** Last used session identifier. Protected by @c client_list_lock */
+static uint16_t inetping_ident = 0;
+
+static inetping_client_t *inetping_client_find(uint16_t);
+
+static int inetping_send(inetping_client_t *client, inetping_sdu_t *sdu)
+{
+	return icmp_ping_send(client->ident, sdu);
+}
+
+static int inetping_get_srcaddr(inetping_client_t *client, inet_addr_t *remote,
+    inet_addr_t *local)
+{
+	return inet_get_srcaddr(remote, ICMP_TOS, local);
+}
+
+int inetping_recv(uint16_t ident, inetping_sdu_t *sdu)
+{
+	inetping_client_t *client;
+	async_exch_t *exch;
+	ipc_call_t answer;
+
+	client = inetping_client_find(ident);
+	if (client == NULL) {
+		log_msg(LVL_DEBUG, "Unknown ICMP ident. Dropping.");
+		return ENOENT;
+	}
+
+	exch = async_exchange_begin(client->sess);
+
+	aid_t req = async_send_3(exch, INETPING_EV_RECV, sdu->src.ipv4,
+	    sdu->dest.ipv4, sdu->seq_no, &answer);
+	int rc = async_data_write_start(exch, sdu->data, sdu->size);
+	async_exchange_end(exch);
+
+	if (rc != EOK) {
+		async_forget(req);
+		return rc;
+	}
+
+	sysarg_t retval;
+	async_wait_for(req, &retval);
+	if (retval != EOK) {
+		return retval;
+	}
+
+	return EOK;
+}
+
+static void inetping_send_srv(inetping_client_t *client, ipc_callid_t callid,
+    ipc_call_t *call)
+{
+	inetping_sdu_t sdu;
+	int rc;
+
+	log_msg(LVL_DEBUG, "inetping_send_srv()");
+
+	rc = async_data_write_accept((void **) &sdu.data, false, 0, 0, 0,
+	    &sdu.size);
+	if (rc != EOK) {
+		async_answer_0(callid, rc);
+		return;
+	}
+
+	sdu.src.ipv4 = IPC_GET_ARG1(*call);
+	sdu.dest.ipv4 = IPC_GET_ARG2(*call);
+	sdu.seq_no = IPC_GET_ARG3(*call);
+
+	rc = inetping_send(client, &sdu);
+	free(sdu.data);
+
+	async_answer_0(callid, rc);
+}
+
+static void inetping_get_srcaddr_srv(inetping_client_t *client,
+    ipc_callid_t callid, ipc_call_t *call)
+{
+	inet_addr_t remote;
+	inet_addr_t local;
+	int rc;
+
+	log_msg(LVL_DEBUG, "inetping_get_srcaddr_srv()");
+
+	remote.ipv4 = IPC_GET_ARG1(*call);
+	local.ipv4 = 0;
+
+	rc = inetping_get_srcaddr(client, &remote, &local);
+	async_answer_1(callid, rc, local.ipv4);
+}
+
+static int inetping_client_init(inetping_client_t *client)
+{
+	async_sess_t *sess = async_callback_receive(EXCHANGE_SERIALIZE);
+	if (sess == NULL)
+		return ENOMEM;
+
+	client->sess = sess;
+	link_initialize(&client->client_list);
+
+	fibril_mutex_lock(&client_list_lock);
+	client->ident = ++inetping_ident;
+	list_append(&client->client_list, &client_list);
+	fibril_mutex_unlock(&client_list_lock);
+
+	return EOK;
+}
+
+static void inetping_client_fini(inetping_client_t *client)
+{
+	async_hangup(client->sess);
+	client->sess = NULL;
+
+	fibril_mutex_lock(&client_list_lock);
+	list_remove(&client->client_list);
+	fibril_mutex_unlock(&client_list_lock);
+}
+
+static inetping_client_t *inetping_client_find(uint16_t ident)
+{
+	fibril_mutex_lock(&client_list_lock);
+
+	list_foreach(client_list, link) {
+		inetping_client_t *client = list_get_instance(link,
+		    inetping_client_t, client_list);
+
+		if (client->ident == ident) {
+			fibril_mutex_unlock(&client_list_lock);
+			return client;
+		}
+	}
+
+	fibril_mutex_unlock(&client_list_lock);
+	return NULL;
+}
+
+void inetping_conn(ipc_callid_t iid, ipc_call_t *icall, void *arg)
+{
+	inetping_client_t client;
+	int rc;
+
+	log_msg(LVL_DEBUG, "inetping_conn()");
+
+	/* Accept the connection */
+	async_answer_0(iid, EOK);
+
+	rc = inetping_client_init(&client);
+	if (rc != EOK)
+		return;
+
+	while (true) {
+		ipc_call_t call;
+		ipc_callid_t callid = async_get_call(&call);
+		sysarg_t method = IPC_GET_IMETHOD(call);
+
+		if (!method) {
+			/* The other side has hung up */
+			async_answer_0(callid, EOK);
+			break;
+		}
+
+		switch (method) {
+		case INETPING_SEND:
+			inetping_send_srv(&client, callid, &call);
+			break;
+		case INETPING_GET_SRCADDR:
+			inetping_get_srcaddr_srv(&client, callid, &call);
+			break;
+		default:
+			async_answer_0(callid, EINVAL);
+		}
+	}
+
+	inetping_client_fini(&client);
+}
+
+/** @}
+ */
Index: uspace/srv/net/inetsrv/inetping.h
===================================================================
--- uspace/srv/net/inetsrv/inetping.h	(revision ce1836ed04784ea0bacc387ac80bfe852f36119b)
+++ uspace/srv/net/inetsrv/inetping.h	(revision ce1836ed04784ea0bacc387ac80bfe852f36119b)
@@ -0,0 +1,46 @@
+/*
+ * 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 INETPING_H_
+#define INETPING_H_
+
+extern void inetping_conn(ipc_callid_t, ipc_call_t *, void *);
+extern int inetping_recv(uint16_t, inetping_sdu_t *);
+
+#endif
+
+/** @}
+ */
Index: uspace/srv/net/inetsrv/inetsrv.c
===================================================================
--- uspace/srv/net/inetsrv/inetsrv.c	(revision ce1836ed04784ea0bacc387ac80bfe852f36119b)
+++ uspace/srv/net/inetsrv/inetsrv.c	(revision ce1836ed04784ea0bacc387ac80bfe852f36119b)
@@ -0,0 +1,450 @@
+/*
+ * 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 Internet Protocol service
+ */
+
+#include <adt/list.h>
+#include <async.h>
+#include <errno.h>
+#include <fibril_synch.h>
+#include <io/log.h>
+#include <ipc/inet.h>
+#include <ipc/services.h>
+#include <loc.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <sys/types.h>
+
+#include "addrobj.h"
+#include "icmp.h"
+#include "icmp_std.h"
+#include "inetsrv.h"
+#include "inetcfg.h"
+#include "inetping.h"
+#include "inet_link.h"
+#include "reass.h"
+#include "sroute.h"
+
+#define NAME "inetsrv"
+
+static void inet_client_conn(ipc_callid_t iid, ipc_call_t *icall, void *arg);
+
+static FIBRIL_MUTEX_INITIALIZE(client_list_lock);
+static LIST_INITIALIZE(client_list);
+
+static int inet_init(void)
+{
+	service_id_t sid;
+	int rc;
+
+	log_msg(LVL_DEBUG, "inet_init()");
+
+	async_set_client_connection(inet_client_conn);
+
+	rc = loc_server_register(NAME);
+	if (rc != EOK) {
+		log_msg(LVL_ERROR, "Failed registering server (%d).", rc);
+		return EEXIST;
+	}
+
+	rc = loc_service_register_with_iface(SERVICE_NAME_INET, &sid,
+	    INET_PORT_DEFAULT);
+	if (rc != EOK) {
+		log_msg(LVL_ERROR, "Failed registering service (%d).", rc);
+		return EEXIST;
+	}
+
+	rc = loc_service_register_with_iface(SERVICE_NAME_INETCFG, &sid,
+	    INET_PORT_CFG);
+	if (rc != EOK) {
+		log_msg(LVL_ERROR, "Failed registering service (%d).", rc);
+		return EEXIST;
+	}
+
+	rc = loc_service_register_with_iface(SERVICE_NAME_INETPING, &sid,
+	    INET_PORT_PING);
+	if (rc != EOK) {
+		log_msg(LVL_ERROR, "Failed registering service (%d).", rc);
+		return EEXIST;
+	}
+
+	rc = inet_link_discovery_start();
+	if (rc != EOK)
+		return EEXIST;
+
+	return EOK;
+}
+
+static void inet_callback_create_srv(inet_client_t *client, ipc_callid_t callid,
+    ipc_call_t *call)
+{
+	log_msg(LVL_DEBUG, "inet_callback_create_srv()");
+
+	async_sess_t *sess = async_callback_receive(EXCHANGE_SERIALIZE);
+	if (sess == NULL) {
+		async_answer_0(callid, ENOMEM);
+		return;
+	}
+
+	client->sess = sess;
+	async_answer_0(callid, EOK);
+}
+
+static int inet_find_dir(inet_addr_t *src, inet_addr_t *dest, uint8_t tos,
+    inet_dir_t *dir)
+{
+	inet_sroute_t *sr;
+
+	/* XXX Handle case where source address is specified */
+	(void) src;
+
+	dir->aobj = inet_addrobj_find(dest, iaf_net);
+	if (dir->aobj != NULL) {
+		dir->ldest = *dest;
+		dir->dtype = dt_direct;
+	} else {
+		/* No direct path, try using a static route */
+		sr = inet_sroute_find(dest);
+		if (sr != NULL) {
+			dir->aobj = inet_addrobj_find(&sr->router, iaf_net);
+			dir->ldest = sr->router;
+			dir->dtype = dt_router;
+		}
+	}
+
+	if (dir->aobj == NULL) {
+		log_msg(LVL_DEBUG, "inet_send: No route to destination.");
+		return ENOENT;
+	}
+
+	return EOK;
+}
+
+int inet_route_packet(inet_dgram_t *dgram, uint8_t proto, uint8_t ttl,
+    int df)
+{
+	inet_dir_t dir;
+	int rc;
+
+	rc = inet_find_dir(&dgram->src, &dgram->dest, dgram->tos, &dir);
+	if (rc != EOK)
+		return rc;
+
+	return inet_addrobj_send_dgram(dir.aobj, &dir.ldest, dgram,
+	    proto, ttl, df);
+}
+
+static int inet_send(inet_client_t *client, inet_dgram_t *dgram,
+    uint8_t proto, uint8_t ttl, int df)
+{
+	return inet_route_packet(dgram, proto, ttl, df);
+}
+
+int inet_get_srcaddr(inet_addr_t *remote, uint8_t tos, inet_addr_t *local)
+{
+	inet_dir_t dir;
+	int rc;
+
+	rc = inet_find_dir(NULL, remote, tos, &dir);
+	if (rc != EOK)
+		return rc;
+
+	/* XXX dt_local? */
+
+	/* Take source address from the address object */
+	local->ipv4 = dir.aobj->naddr.ipv4;
+	return EOK;
+}
+
+static void inet_get_srcaddr_srv(inet_client_t *client, ipc_callid_t callid,
+    ipc_call_t *call)
+{
+	inet_addr_t remote;
+	uint8_t tos;
+	inet_addr_t local;
+	int rc;
+
+	log_msg(LVL_DEBUG, "inet_get_srcaddr_srv()");
+
+	remote.ipv4 = IPC_GET_ARG1(*call);
+	tos = IPC_GET_ARG2(*call);
+	local.ipv4 = 0;
+
+	rc = inet_get_srcaddr(&remote, tos, &local);
+	async_answer_1(callid, rc, local.ipv4);
+}
+
+static void inet_send_srv(inet_client_t *client, ipc_callid_t callid,
+    ipc_call_t *call)
+{
+	inet_dgram_t dgram;
+	uint8_t ttl;
+	int df;
+	int rc;
+
+	log_msg(LVL_DEBUG, "inet_send_srv()");
+
+	dgram.src.ipv4 = IPC_GET_ARG1(*call);
+	dgram.dest.ipv4 = IPC_GET_ARG2(*call);
+	dgram.tos = IPC_GET_ARG3(*call);
+	ttl = IPC_GET_ARG4(*call);
+	df = IPC_GET_ARG5(*call);
+
+	rc = async_data_write_accept(&dgram.data, false, 0, 0, 0, &dgram.size);
+	if (rc != EOK) {
+		async_answer_0(callid, rc);
+		return;
+	}
+
+	rc = inet_send(client, &dgram, client->protocol, ttl, df);
+
+	free(dgram.data);
+	async_answer_0(callid, rc);
+}
+
+static void inet_set_proto_srv(inet_client_t *client, ipc_callid_t callid,
+    ipc_call_t *call)
+{
+	sysarg_t proto;
+
+	proto = IPC_GET_ARG1(*call);
+	log_msg(LVL_DEBUG, "inet_set_proto_srv(%lu)", (unsigned long) proto);
+
+	if (proto > UINT8_MAX) {
+		async_answer_0(callid, EINVAL);
+		return;
+	}
+
+	client->protocol = proto;
+	async_answer_0(callid, EOK);
+}
+
+static void inet_client_init(inet_client_t *client)
+{
+	client->sess = NULL;
+
+	fibril_mutex_lock(&client_list_lock);
+	list_append(&client->client_list, &client_list);
+	fibril_mutex_unlock(&client_list_lock);
+}
+
+static void inet_client_fini(inet_client_t *client)
+{
+	async_hangup(client->sess);
+	client->sess = NULL;
+
+	fibril_mutex_lock(&client_list_lock);
+	list_remove(&client->client_list);
+	fibril_mutex_unlock(&client_list_lock);
+}
+
+static void inet_default_conn(ipc_callid_t iid, ipc_call_t *icall, void *arg)
+{
+	inet_client_t client;
+
+	log_msg(LVL_DEBUG, "inet_default_conn()");
+
+	/* Accept the connection */
+	async_answer_0(iid, EOK);
+
+	inet_client_init(&client);
+
+	while (true) {
+		ipc_call_t call;
+		ipc_callid_t callid = async_get_call(&call);
+		sysarg_t method = IPC_GET_IMETHOD(call);
+
+		if (!method) {
+			/* The other side has hung up */
+			async_answer_0(callid, EOK);
+			return;
+		}
+
+		switch (method) {
+		case INET_CALLBACK_CREATE:
+			inet_callback_create_srv(&client, callid, &call);
+			break;
+		case INET_GET_SRCADDR:
+			inet_get_srcaddr_srv(&client, callid, &call);
+			break;
+		case INET_SEND:
+			inet_send_srv(&client, callid, &call);
+			break;
+		case INET_SET_PROTO:
+			inet_set_proto_srv(&client, callid, &call);
+			break;
+		default:
+			async_answer_0(callid, EINVAL);
+		}
+	}
+
+	inet_client_fini(&client);
+}
+
+static void inet_client_conn(ipc_callid_t iid, ipc_call_t *icall, void *arg)
+{
+	sysarg_t port;
+
+	port = IPC_GET_ARG1(*icall);
+
+	switch (port) {
+	case INET_PORT_DEFAULT:
+		inet_default_conn(iid, icall, arg);
+		break;
+	case INET_PORT_CFG:
+		inet_cfg_conn(iid, icall, arg);
+		break;
+	case INET_PORT_PING:
+		inetping_conn(iid, icall, arg);
+		break;
+	default:
+		async_answer_0(iid, ENOTSUP);
+		break;
+	}
+}
+
+static inet_client_t *inet_client_find(uint8_t proto)
+{
+	fibril_mutex_lock(&client_list_lock);
+
+	list_foreach(client_list, link) {
+		inet_client_t *client = list_get_instance(link, inet_client_t,
+		    client_list);
+
+		if (client->protocol == proto) {
+			fibril_mutex_unlock(&client_list_lock);
+			return client;
+		}
+	}
+
+	fibril_mutex_unlock(&client_list_lock);
+	return NULL;
+}
+
+int inet_ev_recv(inet_client_t *client, inet_dgram_t *dgram)
+{
+	async_exch_t *exch = async_exchange_begin(client->sess);
+
+	ipc_call_t answer;
+	aid_t req = async_send_3(exch, INET_EV_RECV, dgram->src.ipv4,
+	    dgram->dest.ipv4, dgram->tos, &answer);
+	int 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);
+	if (retval != EOK)
+		return retval;
+
+	return EOK;
+}
+
+int inet_recv_dgram_local(inet_dgram_t *dgram, uint8_t proto)
+{
+	inet_client_t *client;
+
+	log_msg(LVL_DEBUG, "inet_recv_dgram_local()");
+
+	/* ICMP messages are handled internally */
+	if (proto == IP_PROTO_ICMP)
+		return icmp_recv(dgram);
+
+	client = inet_client_find(proto);
+	if (client == NULL) {
+		log_msg(LVL_DEBUG, "No client found for protocol 0x%" PRIx8,
+		    proto);
+		return ENOENT;
+	}
+
+	return inet_ev_recv(client, dgram);
+}
+
+int inet_recv_packet(inet_packet_t *packet)
+{
+	inet_addrobj_t *addr;
+	inet_dgram_t dgram;
+
+	addr = inet_addrobj_find(&packet->dest, iaf_addr);
+	if (addr != NULL) {
+		/* Destined for one of the local addresses */
+
+		/* Check if packet is a complete datagram */
+		if (packet->offs == 0 && !packet->mf) {
+			/* It is complete deliver it immediately */
+			dgram.src = packet->src;
+			dgram.dest = packet->dest;
+			dgram.tos = packet->tos;
+			dgram.data = packet->data;
+			dgram.size = packet->size;
+
+			return inet_recv_dgram_local(&dgram, packet->proto);
+		} else {
+			/* It is a fragment, queue it for reassembly */
+			inet_reass_queue_packet(packet);
+		}
+	}
+
+	return ENOENT;
+}
+
+int main(int argc, char *argv[])
+{
+	int rc;
+
+	printf(NAME ": HelenOS Internet Protocol service\n");
+
+	if (log_init(NAME, LVL_WARN) != EOK) {
+		printf(NAME ": Failed to initialize logging.\n");
+		return 1;
+	}
+
+	rc = inet_init();
+	if (rc != EOK)
+		return 1;
+
+	printf(NAME ": Accepting connections.\n");
+	task_retval(0);
+	async_manager();
+
+	/* Not reached */
+	return 0;
+}
+
+/** @}
+ */
Index: uspace/srv/net/inetsrv/inetsrv.h
===================================================================
--- uspace/srv/net/inetsrv/inetsrv.h	(revision ce1836ed04784ea0bacc387ac80bfe852f36119b)
+++ uspace/srv/net/inetsrv/inetsrv.h	(revision ce1836ed04784ea0bacc387ac80bfe852f36119b)
@@ -0,0 +1,202 @@
+/*
+ * 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 inetsrv
+ * @{
+ */
+/**
+ * @file
+ * @brief
+ */
+
+#ifndef INETSRV_H_
+#define INETSRV_H_
+
+#include <adt/list.h>
+#include <bool.h>
+#include <inet/iplink.h>
+#include <ipc/loc.h>
+#include <sys/types.h>
+#include <async.h>
+
+/** Inet Client */
+typedef struct {
+	async_sess_t *sess;
+	uint8_t protocol;
+	link_t client_list;
+} inet_client_t;
+
+/** Inetping Client */
+typedef struct {
+	/** Callback session */
+	async_sess_t *sess;
+	/** Session identifier */
+	uint16_t ident;
+	/** Link to client list */
+	link_t client_list;
+} inetping_client_t;
+
+/** Host address */
+typedef struct {
+	uint32_t ipv4;
+} inet_addr_t;
+
+/** Network address */
+typedef struct {
+	/** Address */
+	uint32_t ipv4;
+	/** Number of valid bits in @c ipv4 */
+	int bits;
+} inet_naddr_t;
+
+/** Address object info */
+typedef struct {
+	/** Network address */
+	inet_naddr_t naddr;
+	/** Link service ID */
+	sysarg_t ilink;
+	/** Address object name */
+	char *name;
+} inet_addr_info_t;
+
+/** IP link info */
+typedef struct {
+	/** Link service name */
+	char *name;
+	/** Default MTU */
+	size_t def_mtu;
+} inet_link_info_t;
+
+/** Static route info */
+typedef struct {
+	/** Destination network address */
+	inet_naddr_t dest;
+	/** Router address */
+	inet_addr_t router;
+	/** Static route name */
+	char *name;
+} inet_sroute_info_t;
+
+typedef struct {
+	/** Source address */
+	inet_addr_t src;
+	/** Destination address */
+	inet_addr_t dest;
+	/** Type of service */
+	uint8_t tos;
+	/** Protocol */
+	uint8_t proto;
+	/** Time to live */
+	uint8_t ttl;
+	/** Identifier */
+	uint16_t ident;
+	/** Do not fragment */
+	bool df;
+	/** More fragments */
+	bool mf;
+	/** Offset of fragment into datagram, in bytes */
+	size_t offs;
+	/** Packet data */
+	void *data;
+	/** Packet data size in bytes */
+	size_t size;
+} inet_packet_t;
+
+typedef struct {
+	inet_addr_t src;
+	inet_addr_t dest;
+	uint8_t tos;
+	void *data;
+	size_t size;
+} inet_dgram_t;
+
+typedef struct {
+	link_t link_list;
+	service_id_t svc_id;
+	char *svc_name;
+	async_sess_t *sess;
+	iplink_t *iplink;
+	size_t def_mtu;
+} inet_link_t;
+
+typedef struct {
+	link_t addr_list;
+	sysarg_t id;
+	inet_naddr_t naddr;
+	inet_link_t *ilink;
+	char *name;
+} inet_addrobj_t;
+
+/** Static route configuration */
+typedef struct {
+	link_t sroute_list;
+	sysarg_t id;
+	/** Destination network */
+	inet_naddr_t dest;
+	/** Router via which to route packets */
+	inet_addr_t router;
+	char *name;
+} inet_sroute_t;
+
+typedef enum {
+	/** Destination is on this network node */
+	dt_local,
+	/** Destination is directly reachable */
+	dt_direct,
+	/** Destination is behind a router */
+	dt_router
+} inet_dir_type_t;
+
+/** Direction (next hop) to a destination */
+typedef struct {
+	/** Route type */
+	inet_dir_type_t dtype;
+	/** Address object (direction) */
+	inet_addrobj_t *aobj;
+	/** Local destination address */
+	inet_addr_t ldest;
+} inet_dir_t;
+
+typedef struct {
+	inet_addr_t src;
+	inet_addr_t dest;
+	uint16_t seq_no;
+	void *data;
+	size_t size;
+} inetping_sdu_t;
+
+extern int inet_ev_recv(inet_client_t *, inet_dgram_t *);
+extern int inet_recv_packet(inet_packet_t *);
+extern int inet_route_packet(inet_dgram_t *, uint8_t, uint8_t, int);
+extern int inet_get_srcaddr(inet_addr_t *, uint8_t, inet_addr_t *);
+extern int inet_recv_dgram_local(inet_dgram_t *, uint8_t);
+
+#endif
+
+/** @}
+ */
Index: uspace/srv/net/inetsrv/pdu.c
===================================================================
--- uspace/srv/net/inetsrv/pdu.c	(revision ce1836ed04784ea0bacc387ac80bfe852f36119b)
+++ uspace/srv/net/inetsrv/pdu.c	(revision ce1836ed04784ea0bacc387ac80bfe852f36119b)
@@ -0,0 +1,268 @@
+/*
+ * 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 <align.h>
+#include <bitops.h>
+#include <byteorder.h>
+#include <errno.h>
+#include <fibril_synch.h>
+#include <io/log.h>
+#include <macros.h>
+#include <mem.h>
+#include <stdlib.h>
+
+#include "inetsrv.h"
+#include "inet_std.h"
+#include "pdu.h"
+
+static FIBRIL_MUTEX_INITIALIZE(ip_ident_lock);
+static uint16_t ip_ident = 0;
+
+/** One's complement addition.
+ *
+ * Result is a + b + carry.
+ */
+static uint16_t inet_ocadd16(uint16_t a, uint16_t b)
+{
+	uint32_t s;
+
+	s = (uint32_t)a + (uint32_t)b;
+	return (s & 0xffff) + (s >> 16);
+}
+
+uint16_t inet_checksum_calc(uint16_t ivalue, void *data, size_t size)
+{
+	uint16_t sum;
+	uint16_t w;
+	size_t words, i;
+	uint8_t *bdata;
+
+	sum = ~ivalue;
+	words = size / 2;
+	bdata = (uint8_t *)data;
+
+	for (i = 0; i < words; i++) {
+		w = ((uint16_t)bdata[2*i] << 8) | bdata[2*i + 1];
+		sum = inet_ocadd16(sum, w);
+	}
+
+	if (size % 2 != 0) {
+		w = ((uint16_t)bdata[2*words] << 8);
+		sum = inet_ocadd16(sum, w);
+	}
+
+	return ~sum;
+}
+
+/** Encode Internet PDU.
+ *
+ * Encode internet packet into PDU (serialized form). Will encode a
+ * fragment of the payload starting at offset @a offs. The resulting
+ * PDU will have at most @a mtu bytes. @a *roffs will be set to the offset
+ * of remaining payload. If some data is remaining, the MF flag will
+ * be set in the header, otherwise the offset will equal @a packet->size.
+ *
+ * @param packet	Packet to encode
+ * @param offs		Offset into packet payload (in bytes)
+ * @param mtu		MTU (Maximum Transmission Unit) in bytes
+ * @param rdata		Place to store pointer to allocated data buffer
+ * @param rsize		Place to store size of allocated data buffer
+ * @param roffs		Place to store offset of remaning data
+ */
+int inet_pdu_encode(inet_packet_t *packet, size_t offs, size_t mtu,
+    void **rdata, size_t *rsize, size_t *roffs)
+{
+	void *data;
+	size_t size;
+	ip_header_t *hdr;
+	size_t hdr_size;
+	size_t data_offs;
+	uint16_t chksum;
+	uint16_t ident;
+	uint16_t flags_foff;
+	uint16_t foff;
+	size_t fragoff_limit;
+	size_t xfer_size;
+	size_t spc_avail;
+	size_t rem_offs;
+
+	/* Upper bound for fragment offset field */
+	fragoff_limit = 1 << (FF_FRAGOFF_h - FF_FRAGOFF_l);
+
+	/* Verify that total size of datagram is within reasonable bounds */
+	if (offs + packet->size > FRAG_OFFS_UNIT * fragoff_limit)
+		return ELIMIT;
+
+	hdr_size = sizeof(ip_header_t);
+	data_offs = ROUND_UP(hdr_size, 4);
+
+	assert(offs % FRAG_OFFS_UNIT == 0);
+	assert(offs / FRAG_OFFS_UNIT < fragoff_limit);
+
+	/* Value for the fragment offset field */
+	foff = offs / FRAG_OFFS_UNIT;
+
+	if (hdr_size >= mtu)
+		return EINVAL;
+
+	/* Amount of space in the PDU available for payload */
+	spc_avail = mtu - hdr_size;
+	spc_avail -= (spc_avail % FRAG_OFFS_UNIT);
+
+	/* Amount of data (payload) to transfer */
+	xfer_size = min(packet->size - offs, spc_avail);
+
+	/* Total PDU size */
+	size = hdr_size + xfer_size;
+
+	/* Offset of remaining payload */
+	rem_offs = offs + xfer_size;
+
+	/* Flags */
+	flags_foff =
+	    (packet->df ? BIT_V(uint16_t, FF_FLAG_DF) : 0) +
+	    (rem_offs < packet->size ? BIT_V(uint16_t, FF_FLAG_MF) : 0) +
+	    (foff << FF_FRAGOFF_l);
+
+	data = calloc(size, 1);
+	if (data == NULL)
+		return ENOMEM;
+
+	/* Allocate identifier */
+	fibril_mutex_lock(&ip_ident_lock);
+	ident = ++ip_ident;
+	fibril_mutex_unlock(&ip_ident_lock);
+
+	/* Encode header fields */
+	hdr = (ip_header_t *)data;
+	hdr->ver_ihl = (4 << VI_VERSION_l) | (hdr_size / sizeof(uint32_t));
+	hdr->tos = packet->tos;
+	hdr->tot_len = host2uint16_t_be(size);
+	hdr->id = host2uint16_t_be(ident);
+	hdr->flags_foff = host2uint16_t_be(flags_foff);
+	hdr->ttl = packet->ttl;
+	hdr->proto = packet->proto;
+	hdr->chksum = 0;
+	hdr->src_addr = host2uint32_t_be(packet->src.ipv4);
+	hdr->dest_addr = host2uint32_t_be(packet->dest.ipv4);
+
+	/* Compute checksum */
+	chksum = inet_checksum_calc(INET_CHECKSUM_INIT, (void *)hdr, hdr_size);
+	hdr->chksum = host2uint16_t_be(chksum);
+
+	/* Copy payload */
+	memcpy((uint8_t *)data + data_offs, packet->data + offs, xfer_size);
+
+	*rdata = data;
+	*rsize = size;
+	*roffs = rem_offs;
+
+	return EOK;
+}
+
+int inet_pdu_decode(void *data, size_t size, inet_packet_t *packet)
+{
+	ip_header_t *hdr;
+	size_t tot_len;
+	size_t data_offs;
+	uint8_t version;
+	uint16_t ident;
+	uint16_t flags_foff;
+	uint16_t foff;
+
+	log_msg(LVL_DEBUG, "inet_pdu_decode()");
+
+	if (size < sizeof(ip_header_t)) {
+		log_msg(LVL_DEBUG, "PDU too short (%zu)", size);
+		return EINVAL;
+	}
+
+	hdr = (ip_header_t *)data;
+
+	version = BIT_RANGE_EXTRACT(uint8_t, VI_VERSION_h, VI_VERSION_l,
+	    hdr->ver_ihl);
+	if (version != 4) {
+		log_msg(LVL_DEBUG, "Version (%d) != 4", version);
+		return EINVAL;
+	}
+
+	tot_len = uint16_t_be2host(hdr->tot_len);
+	if (tot_len < sizeof(ip_header_t)) {
+		log_msg(LVL_DEBUG, "Total Length too small (%zu)", tot_len);
+		return EINVAL;
+	}
+
+	if (tot_len > size) {
+		log_msg(LVL_DEBUG, "Total Length = %zu > PDU size = %zu",
+			tot_len, size);
+		return EINVAL;
+	}
+
+	ident = uint16_t_be2host(hdr->id);
+	flags_foff = uint16_t_be2host(hdr->flags_foff);
+	foff = BIT_RANGE_EXTRACT(uint16_t, FF_FRAGOFF_h, FF_FRAGOFF_l,
+	    flags_foff);
+	/* XXX Checksum */
+
+	packet->src.ipv4 = uint32_t_be2host(hdr->src_addr);
+	packet->dest.ipv4 = uint32_t_be2host(hdr->dest_addr);
+	packet->tos = hdr->tos;
+	packet->proto = hdr->proto;
+	packet->ttl = hdr->ttl;
+	packet->ident = ident;
+
+	packet->df = (flags_foff & BIT_V(uint16_t, FF_FLAG_DF)) != 0;
+	packet->mf = (flags_foff & BIT_V(uint16_t, FF_FLAG_MF)) != 0;
+	packet->offs = foff * FRAG_OFFS_UNIT;
+
+	/* XXX IP options */
+	data_offs = sizeof(uint32_t) * BIT_RANGE_EXTRACT(uint8_t, VI_IHL_h,
+	    VI_IHL_l, hdr->ver_ihl);
+
+	packet->size = tot_len - data_offs;
+	packet->data = calloc(packet->size, 1);
+	if (packet->data == NULL) {
+		log_msg(LVL_WARN, "Out of memory.");
+		return ENOMEM;
+	}
+
+	memcpy(packet->data, (uint8_t *)data + data_offs, packet->size);
+
+	return EOK;
+}
+
+/** @}
+ */
Index: uspace/srv/net/inetsrv/pdu.h
===================================================================
--- uspace/srv/net/inetsrv/pdu.h	(revision ce1836ed04784ea0bacc387ac80bfe852f36119b)
+++ uspace/srv/net/inetsrv/pdu.h	(revision ce1836ed04784ea0bacc387ac80bfe852f36119b)
@@ -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 INET_PDU_H_
+#define INET_PDU_H_
+
+#include <sys/types.h>
+#include "inetsrv.h"
+
+#define INET_CHECKSUM_INIT 0xffff
+
+extern uint16_t inet_checksum_calc(uint16_t, void *, size_t);
+
+extern int inet_pdu_encode(inet_packet_t *, size_t, size_t, void **,
+    size_t *, size_t *);
+extern int inet_pdu_decode(void *, size_t, inet_packet_t *);
+
+#endif
+
+/** @}
+ */
Index: uspace/srv/net/inetsrv/reass.c
===================================================================
--- uspace/srv/net/inetsrv/reass.c	(revision ce1836ed04784ea0bacc387ac80bfe852f36119b)
+++ uspace/srv/net/inetsrv/reass.c	(revision ce1836ed04784ea0bacc387ac80bfe852f36119b)
@@ -0,0 +1,374 @@
+/*
+ * 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 Datagram reassembly.
+ */
+
+#include <errno.h>
+#include <fibril_synch.h>
+#include <io/log.h>
+#include <macros.h>
+#include <mem.h>
+#include <stdlib.h>
+
+#include "inetsrv.h"
+#include "inet_std.h"
+#include "reass.h"
+
+/** Datagram being reassembled.
+ *
+ * Uniquely identified by (source address, destination address, protocol,
+ * identification) per RFC 791 sec. 2.3 / Fragmentation.
+ */
+typedef struct {
+	link_t map_link;
+	/** List of fragments, @c reass_frag_t */
+	list_t frags;
+} reass_dgram_t;
+
+/** One datagram fragment */
+typedef struct {
+	link_t dgram_link;
+	inet_packet_t packet;
+} reass_frag_t;
+
+/** Datagram map, list of reass_dgram_t */
+static LIST_INITIALIZE(reass_dgram_map);
+/** Protects access to @c reass_dgram_map */
+static FIBRIL_MUTEX_INITIALIZE(reass_dgram_map_lock);
+
+static reass_dgram_t *reass_dgram_new(void);
+static reass_dgram_t *reass_dgram_get(inet_packet_t *);
+static int reass_dgram_insert_frag(reass_dgram_t *, inet_packet_t *);
+static bool reass_dgram_complete(reass_dgram_t *);
+static void reass_dgram_remove(reass_dgram_t *);
+static int reass_dgram_deliver(reass_dgram_t *);
+static void reass_dgram_destroy(reass_dgram_t *);
+
+/** Queue packet for datagram reassembly.
+ *
+ * @param packet	Packet
+ * @return		EOK on success or ENOMEM.
+ */
+int inet_reass_queue_packet(inet_packet_t *packet)
+{
+	reass_dgram_t *rdg;
+	int rc;
+
+	log_msg(LVL_DEBUG, "inet_reass_queue_packet()");
+
+	fibril_mutex_lock(&reass_dgram_map_lock);
+
+	/* Get existing or new datagram */
+	rdg = reass_dgram_get(packet);
+	if (rdg == NULL) {
+		/* Only happens when we are out of memory */
+		fibril_mutex_unlock(&reass_dgram_map_lock);
+		log_msg(LVL_DEBUG, "Allocation failed, packet dropped.");
+		return ENOMEM;
+	}
+
+	/* Insert fragment into the datagram */
+	rc = reass_dgram_insert_frag(rdg, packet);
+	if (rc != EOK)
+		return ENOMEM;
+
+	/* Check if datagram is complete */
+	if (reass_dgram_complete(rdg)) {
+		/* Remove it from the map */
+		reass_dgram_remove(rdg);
+		fibril_mutex_unlock(&reass_dgram_map_lock);
+
+		/* Deliver complete datagram */
+		rc = reass_dgram_deliver(rdg);
+		reass_dgram_destroy(rdg);
+		return rc;
+	}
+
+	fibril_mutex_unlock(&reass_dgram_map_lock);
+	return EOK;
+}
+
+/** Get datagram reassembly structure for packet.
+ *
+ * @param packet	Packet
+ * @return		Datagram reassembly structure matching @a packet
+ */
+static reass_dgram_t *reass_dgram_get(inet_packet_t *packet)
+{
+	assert(fibril_mutex_is_locked(&reass_dgram_map_lock));
+
+	list_foreach(reass_dgram_map, link) {
+		reass_dgram_t *rdg = list_get_instance(link, reass_dgram_t,
+		    map_link);
+
+		link_t *f1_link = list_first(&rdg->frags);
+		assert(f1_link != NULL);
+
+		reass_frag_t *f1 = list_get_instance(f1_link, reass_frag_t,
+		    dgram_link);
+
+		if (f1->packet.src.ipv4 == packet->src.ipv4 &&
+		    f1->packet.dest.ipv4 == packet->dest.ipv4 &&
+		    f1->packet.proto == packet->proto &&
+		    f1->packet.ident == packet->ident) {
+			/* Match */
+			return rdg;
+		}
+	}
+
+	/* No existing reassembly structure. Create a new one. */
+	return reass_dgram_new();
+}
+
+/** Create new datagram reassembly structure.
+ *
+ * @return New datagram reassembly structure.
+ */
+static reass_dgram_t *reass_dgram_new(void)
+{
+	reass_dgram_t *rdg;
+
+	rdg = calloc(1, sizeof(reass_dgram_t));
+	if (rdg == NULL)
+		return NULL;
+
+	link_initialize(&rdg->map_link);
+	list_initialize(&rdg->frags);
+
+	return rdg;
+}
+
+static reass_frag_t *reass_frag_new(void)
+{
+	reass_frag_t *frag;
+
+	frag = calloc(1, sizeof(reass_frag_t));
+	if (frag == NULL)
+		return NULL;
+
+	link_initialize(&frag->dgram_link);
+
+	return frag;
+}
+
+static int reass_dgram_insert_frag(reass_dgram_t *rdg, inet_packet_t *packet)
+{
+	reass_frag_t *frag;
+	void *data_copy;
+	link_t *link;
+
+	assert(fibril_mutex_is_locked(&reass_dgram_map_lock));
+
+	frag = reass_frag_new();
+
+	/* Clone the packet */
+
+	data_copy = malloc(packet->size);
+	if (data_copy == NULL)
+		return ENOMEM;
+
+	frag->packet = *packet;
+	frag->packet.data = data_copy;
+
+	/*
+	 * XXX Make resource-consuming attacks harder, eliminate any duplicate
+	 * data immediately. Possibly eliminate redundant packet headers.
+	 */
+
+	/*
+	 * Insert into the list, which is sorted by offs member ascending.
+	 */
+
+	link = list_first(&rdg->frags);
+	while (link != NULL) {
+		reass_frag_t *qf = list_get_instance(link, reass_frag_t,
+		    dgram_link);
+
+		if (qf->packet.offs >= packet->offs)
+			break;
+
+		link = link->next;
+	}
+
+	if (link != NULL)
+		list_insert_after(&frag->dgram_link, link);
+	else
+		list_append(&frag->dgram_link, &rdg->frags);
+
+	return EOK;
+}
+
+/** Check if datagram is complete.
+ *
+ * @param rdg		Datagram reassembly structure
+ * @return		@c true if complete, @c false if not
+ */
+static bool reass_dgram_complete(reass_dgram_t *rdg)
+{
+	reass_frag_t *frag, *prev;
+	link_t *link;
+
+	assert(fibril_mutex_is_locked(&reass_dgram_map_lock));
+	assert(!list_empty(&rdg->frags));
+
+	/* First fragment must be at offset zero */
+	frag = list_get_instance(list_first(&rdg->frags), reass_frag_t,
+	    dgram_link);
+	if (frag->packet.offs != 0)
+		return false;
+
+	prev = frag;
+	while (true) {
+		link = frag->dgram_link.next;
+		if (link == NULL)
+			return false;
+
+		/* Each next fragment must follow immediately or overlap */
+		frag = list_get_instance(link, reass_frag_t, dgram_link);
+		if (frag->packet.offs > prev->packet.offs + prev->packet.size)
+			return false;
+
+		/* No more fragments - datagram is complete */
+		if (!frag->packet.mf)
+			return true;
+
+		prev = frag;
+	}
+
+	return false;
+}
+
+/** Remove datagram from reassembly map.
+ *
+ * @param rdg		Datagram reassembly structure
+ */
+static void reass_dgram_remove(reass_dgram_t *rdg)
+{
+	assert(fibril_mutex_is_locked(&reass_dgram_map_lock));
+	list_remove(&rdg->map_link);
+}
+
+/** Deliver complete datagram.
+ *
+ * @param rdg		Datagram reassembly structure.
+ */
+static int reass_dgram_deliver(reass_dgram_t *rdg)
+{
+	size_t dgram_size;
+	size_t fragoff_limit;
+	inet_dgram_t dgram;
+	reass_frag_t *frag;
+	uint8_t proto;
+
+	/*
+	 * Potentially there could be something beyond the first packet
+	 * that has !MF. Make sure we ignore that.
+	 */
+	frag = NULL;
+	list_foreach(rdg->frags, link) {
+		frag = list_get_instance(link, reass_frag_t, dgram_link);
+
+		if (!frag->packet.mf)
+			break;
+	}
+
+	assert(frag != NULL);
+	assert(!frag->packet.mf);
+
+	dgram_size = frag->packet.offs + frag->packet.size;
+
+	/* Upper bound for fragment offset field */
+	fragoff_limit = 1 << (FF_FRAGOFF_h - FF_FRAGOFF_l);
+
+	/* Verify that total size of datagram is within reasonable bounds */
+	if (dgram_size > FRAG_OFFS_UNIT * fragoff_limit)
+		return ELIMIT;
+
+	dgram.data = calloc(dgram_size, 1);
+	if (dgram.data == NULL)
+		return ENOMEM;
+
+	dgram.size = dgram_size;
+	dgram.src = frag->packet.src;
+	dgram.dest = frag->packet.dest;
+	dgram.tos = frag->packet.tos;
+	proto = frag->packet.proto;
+
+	/* Pull together data from individual fragments */
+
+	size_t doffs = 0;
+
+	frag = NULL;
+	list_foreach(rdg->frags, link) {
+		frag = list_get_instance(link, reass_frag_t, dgram_link);
+
+		size_t cb, ce;
+
+		cb = max(doffs, frag->packet.offs);
+		ce = min(dgram_size, frag->packet.offs + frag->packet.size);
+
+		if (ce > cb) {
+			memcpy(dgram.data + cb,
+			    frag->packet.data + cb - frag->packet.offs,
+			    ce - cb);
+		}
+
+		if (!frag->packet.mf)
+			break;
+	}
+
+	return inet_recv_dgram_local(&dgram, proto);
+}
+
+/** Destroy datagram reassembly structure.
+ *
+ * @param rdg		Datagram reassembly structure.
+ */
+static void reass_dgram_destroy(reass_dgram_t *rdg)
+{
+	while (!list_empty(&rdg->frags)) {
+		link_t *flink = list_first(&rdg->frags);
+		reass_frag_t *frag = list_get_instance(flink, reass_frag_t,
+		    dgram_link);
+
+		list_remove(&frag->dgram_link);
+		free(frag->packet.data);
+		free(frag);
+	}
+
+	free(rdg);
+}
+
+/** @}
+ */
Index: uspace/srv/net/inetsrv/reass.h
===================================================================
--- uspace/srv/net/inetsrv/reass.h	(revision ce1836ed04784ea0bacc387ac80bfe852f36119b)
+++ uspace/srv/net/inetsrv/reass.h	(revision ce1836ed04784ea0bacc387ac80bfe852f36119b)
@@ -0,0 +1,48 @@
+/*
+ * 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 INET_REASS_H_
+#define INET_REASS_H_
+
+#include <sys/types.h>
+#include "inetsrv.h"
+
+extern int inet_reass_queue_packet(inet_packet_t *);
+
+#endif
+
+/** @}
+ */
Index: uspace/srv/net/inetsrv/sroute.c
===================================================================
--- uspace/srv/net/inetsrv/sroute.c	(revision ce1836ed04784ea0bacc387ac80bfe852f36119b)
+++ uspace/srv/net/inetsrv/sroute.c	(revision ce1836ed04784ea0bacc387ac80bfe852f36119b)
@@ -0,0 +1,219 @@
+/*
+ * 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 <bitops.h>
+#include <errno.h>
+#include <fibril_synch.h>
+#include <io/log.h>
+#include <ipc/loc.h>
+#include <stdlib.h>
+#include <str.h>
+
+#include "sroute.h"
+#include "inetsrv.h"
+#include "inet_link.h"
+#include "inet_util.h"
+
+static FIBRIL_MUTEX_INITIALIZE(sroute_list_lock);
+static LIST_INITIALIZE(sroute_list);
+static sysarg_t sroute_id = 0;
+
+inet_sroute_t *inet_sroute_new(void)
+{
+	inet_sroute_t *sroute = calloc(1, sizeof(inet_sroute_t));
+
+	if (sroute == NULL) {
+		log_msg(LVL_ERROR, "Failed allocating static route object. "
+		    "Out of memory.");
+		return NULL;
+	}
+
+	link_initialize(&sroute->sroute_list);
+	fibril_mutex_lock(&sroute_list_lock);
+	sroute->id = ++sroute_id;
+	fibril_mutex_unlock(&sroute_list_lock);
+
+	return sroute;
+}
+
+void inet_sroute_delete(inet_sroute_t *sroute)
+{
+	if (sroute->name != NULL)
+		free(sroute->name);
+	free(sroute);
+}
+
+void inet_sroute_add(inet_sroute_t *sroute)
+{
+	fibril_mutex_lock(&sroute_list_lock);
+	list_append(&sroute->sroute_list, &sroute_list);
+	fibril_mutex_unlock(&sroute_list_lock);
+}
+
+void inet_sroute_remove(inet_sroute_t *sroute)
+{
+	fibril_mutex_lock(&sroute_list_lock);
+	list_remove(&sroute->sroute_list);
+	fibril_mutex_unlock(&sroute_list_lock);
+}
+
+/** Find address object matching address @a addr.
+ *
+ * @param addr	Address
+ */
+inet_sroute_t *inet_sroute_find(inet_addr_t *addr)
+{
+	uint32_t mask;
+	inet_sroute_t *best;
+
+	log_msg(LVL_DEBUG, "inet_sroute_find(%x)", (unsigned)addr->ipv4);
+
+	fibril_mutex_lock(&sroute_list_lock);
+
+	best = NULL;
+
+	list_foreach(sroute_list, link) {
+		inet_sroute_t *sroute = list_get_instance(link,
+		    inet_sroute_t, sroute_list);
+
+		/* Look for the most specific route */
+		if (best != NULL && best->dest.bits >= sroute->dest.bits)
+			continue;
+
+		mask = inet_netmask(sroute->dest.bits);
+		if ((sroute->dest.ipv4 & mask) == (addr->ipv4 & mask)) {
+			fibril_mutex_unlock(&sroute_list_lock);
+			log_msg(LVL_DEBUG, "inet_sroute_find: found %p",
+			    sroute);
+			return sroute;
+		}
+	}
+
+	log_msg(LVL_DEBUG, "inet_sroute_find: Not found");
+	fibril_mutex_unlock(&sroute_list_lock);
+
+	return NULL;
+}
+
+/** Find static route with a specific name.
+ *
+ * @param name	Address object name
+ * @return	Address object
+ */
+inet_sroute_t *inet_sroute_find_by_name(const char *name)
+{
+	log_msg(LVL_DEBUG, "inet_sroute_find_by_name('%s')",
+	    name);
+
+	fibril_mutex_lock(&sroute_list_lock);
+
+	list_foreach(sroute_list, link) {
+		inet_sroute_t *sroute = list_get_instance(link,
+		    inet_sroute_t, sroute_list);
+
+		if (str_cmp(sroute->name, name) == 0) {
+			fibril_mutex_unlock(&sroute_list_lock);
+			log_msg(LVL_DEBUG, "inet_sroute_find_by_name: found %p",
+			    sroute);
+			return sroute;
+		}
+	}
+
+	log_msg(LVL_DEBUG, "inet_sroute_find_by_name: Not found");
+	fibril_mutex_unlock(&sroute_list_lock);
+
+	return NULL;
+}
+
+/** Find static route with the given ID.
+ *
+ * @param id	Address object ID
+ * @return	Address object
+ */
+inet_sroute_t *inet_sroute_get_by_id(sysarg_t id)
+{
+	log_msg(LVL_DEBUG, "inet_sroute_get_by_id(%zu)", (size_t)id);
+
+	fibril_mutex_lock(&sroute_list_lock);
+
+	list_foreach(sroute_list, link) {
+		inet_sroute_t *sroute = list_get_instance(link,
+		    inet_sroute_t, sroute_list);
+
+		if (sroute->id == id) {
+			fibril_mutex_unlock(&sroute_list_lock);
+			return sroute;
+		}
+	}
+
+	fibril_mutex_unlock(&sroute_list_lock);
+
+	return NULL;
+}
+
+/** Get IDs of all static routes. */
+int inet_sroute_get_id_list(sysarg_t **rid_list, size_t *rcount)
+{
+	sysarg_t *id_list;
+	size_t count, i;
+
+	fibril_mutex_lock(&sroute_list_lock);
+	count = list_count(&sroute_list);
+
+	id_list = calloc(count, sizeof(sysarg_t));
+	if (id_list == NULL) {
+		fibril_mutex_unlock(&sroute_list_lock);
+		return ENOMEM;
+	}
+
+	i = 0;
+	list_foreach(sroute_list, link) {
+		inet_sroute_t *sroute = list_get_instance(link,
+		    inet_sroute_t, sroute_list);
+
+		id_list[i++] = sroute->id;
+	}
+
+	fibril_mutex_unlock(&sroute_list_lock);
+
+	*rid_list = id_list;
+	*rcount = count;
+
+	return EOK;
+}
+
+/** @}
+ */
Index: uspace/srv/net/inetsrv/sroute.h
===================================================================
--- uspace/srv/net/inetsrv/sroute.h	(revision ce1836ed04784ea0bacc387ac80bfe852f36119b)
+++ uspace/srv/net/inetsrv/sroute.h	(revision ce1836ed04784ea0bacc387ac80bfe852f36119b)
@@ -0,0 +1,58 @@
+/*
+ * 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 INET_SROUTE_H_
+#define INET_SROUTE_H_
+
+#include <sys/types.h>
+#include "inetsrv.h"
+
+extern inet_sroute_t *inet_sroute_new(void);
+extern void inet_sroute_delete(inet_sroute_t *);
+extern void inet_sroute_add(inet_sroute_t *);
+extern void inet_sroute_remove(inet_sroute_t *);
+extern inet_sroute_t *inet_sroute_find(inet_addr_t *);
+extern inet_sroute_t *inet_sroute_find_by_name(const char *);
+extern inet_sroute_t *inet_sroute_get_by_id(sysarg_t);
+extern int inet_sroute_send_dgram(inet_sroute_t *, inet_addr_t *,
+    inet_dgram_t *, uint8_t, uint8_t, int);
+extern int inet_sroute_get_id_list(sysarg_t **, size_t *);
+
+
+#endif
+
+/** @}
+ */
Index: uspace/srv/websrv/Makefile
===================================================================
--- uspace/srv/websrv/Makefile	(revision ce1836ed04784ea0bacc387ac80bfe852f36119b)
+++ uspace/srv/websrv/Makefile	(revision ce1836ed04784ea0bacc387ac80bfe852f36119b)
@@ -0,0 +1,37 @@
+#
+# Copyright (c) 2010 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.
+#
+
+USPACE_PREFIX = ../..
+LIBS =
+EXTRA_CFLAGS =
+BINARY = websrv
+
+SOURCES = \
+	websrv.c
+
+include $(USPACE_PREFIX)/Makefile.common
Index: uspace/srv/websrv/websrv.c
===================================================================
--- uspace/srv/websrv/websrv.c	(revision ce1836ed04784ea0bacc387ac80bfe852f36119b)
+++ uspace/srv/websrv/websrv.c	(revision ce1836ed04784ea0bacc387ac80bfe852f36119b)
@@ -0,0 +1,445 @@
+/*
+ * 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 websrv
+ * @{
+ */
+/**
+ * @file Skeletal web server.
+ */
+
+#include <bool.h>
+#include <errno.h>
+#include <stdio.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <stdlib.h>
+#include <fcntl.h>
+#include <task.h>
+
+#include <net/in.h>
+#include <net/inet.h>
+#include <net/socket.h>
+
+#include <arg_parse.h>
+#include <macros.h>
+#include <str.h>
+#include <str_error.h>
+
+#define NAME  "websrv"
+
+#define DEFAULT_PORT  8080
+#define BACKLOG_SIZE  3
+
+#define WEB_ROOT  "/data/web"
+
+/** Buffer for receiving the request. */
+#define BUFFER_SIZE  1024
+
+static uint16_t port = DEFAULT_PORT;
+
+static char rbuf[BUFFER_SIZE];
+static size_t rbuf_out;
+static size_t rbuf_in;
+
+static char lbuf[BUFFER_SIZE + 1];
+static size_t lbuf_used;
+
+static char fbuf[BUFFER_SIZE];
+
+static bool verbose = false;
+
+/** Responses to send to client. */
+
+static const char *msg_ok =
+    "HTTP/1.0 200 OK\r\n"
+    "\r\n";
+
+static const char *msg_bad_request =
+    "HTTP/1.0 400 Bad Request\r\n"
+    "\r\n"
+    "<!DOCTYPE HTML PUBLIC \"-//IETF//DTD HTML 2.0//EN\">\r\n"
+    "<html><head>\r\n"
+    "<title>400 Bad Request</title>\r\n"
+    "</head>\r\n"
+    "<body>\r\n"
+    "<h1>Bad Request</h1>\r\n"
+    "<p>The requested URL has bad syntax.</p>\r\n"
+    "</body>\r\n"
+    "</html>\r\n";
+
+static const char *msg_not_found =
+    "HTTP/1.0 404 Not Found\r\n"
+    "\r\n"
+    "<!DOCTYPE HTML PUBLIC \"-//IETF//DTD HTML 2.0//EN\">\r\n"
+    "<html><head>\r\n"
+    "<title>404 Not Found</title>\r\n"
+    "</head>\r\n"
+    "<body>\r\n"
+    "<h1>Not Found</h1>\r\n"
+    "<p>The requested URL was not found on this server.</p>\r\n"
+    "</body>\r\n"
+    "</html>\r\n";
+
+static const char *msg_not_implemented =
+    "HTTP/1.0 501 Not Implemented\r\n"
+    "\r\n"
+    "<!DOCTYPE HTML PUBLIC \"-//IETF//DTD HTML 2.0//EN\">\r\n"
+    "<html><head>\r\n"
+    "<title>501 Not Implemented</title>\r\n"
+    "</head>\r\n"
+    "<body>\r\n"
+    "<h1>Not Implemented</h1>\r\n"
+    "<p>The requested method is not implemented on this server.</p>\r\n"
+    "</body>\r\n"
+    "</html>\r\n";
+
+/** Receive one character (with buffering) */
+static int recv_char(int fd, char *c)
+{
+	if (rbuf_out == rbuf_in) {
+		rbuf_out = 0;
+		rbuf_in = 0;
+		
+		ssize_t rc = recv(fd, rbuf, BUFFER_SIZE, 0);
+		if (rc <= 0) {
+			fprintf(stderr, "recv() failed (%zd)\n", rc);
+			return rc;
+		}
+		
+		rbuf_in = rc;
+	}
+	
+	*c = rbuf[rbuf_out++];
+	return EOK;
+}
+
+/** Receive one line with length limit */
+static int recv_line(int fd)
+{
+	char *bp = lbuf;
+	char c = '\0';
+	
+	while (bp < lbuf + BUFFER_SIZE) {
+		char prev = c;
+		int rc = recv_char(fd, &c);
+		
+		if (rc != EOK)
+			return rc;
+		
+		*bp++ = c;
+		if ((prev == '\r') && (c == '\n'))
+			break;
+	}
+	
+	lbuf_used = bp - lbuf;
+	*bp = '\0';
+	
+	if (bp == lbuf + BUFFER_SIZE)
+		return ELIMIT;
+	
+	return EOK;
+}
+
+static bool uri_is_valid(char *uri)
+{
+	if (uri[0] != '/')
+		return false;
+	
+	if (uri[1] == '.')
+		return false;
+	
+	char *cp = uri + 1;
+	
+	while (*cp != '\0') {
+		char c = *cp++;
+		if (c == '/')
+			return false;
+	}
+	
+	return true;
+}
+
+static int send_response(int conn_sd, const char *msg)
+{
+	size_t response_size = str_size(msg);
+	
+	if (verbose)
+	    fprintf(stderr, "Sending response\n");
+	
+	ssize_t rc = send(conn_sd, (void *) msg, response_size, 0);
+	if (rc < 0) {
+		fprintf(stderr, "send() failed\n");
+		return rc;
+	}
+	
+	return EOK;
+}
+
+static int uri_get(const char *uri, int conn_sd)
+{
+	if (str_cmp(uri, "/") == 0)
+		uri = "/index.html";
+	
+	char *fname;
+	int rc = asprintf(&fname, "%s%s", WEB_ROOT, uri);
+	if (rc < 0)
+		return ENOMEM;
+	
+	int fd = open(fname, O_RDONLY);
+	if (fd < 0) {
+		rc = send_response(conn_sd, msg_not_found);
+		free(fname);
+		return rc;
+	}
+	
+	free(fname);
+	
+	rc = send_response(conn_sd, msg_ok);
+	if (rc != EOK)
+		return rc;
+	
+	while (true) {
+		ssize_t nr = read(fd, fbuf, BUFFER_SIZE);
+		if (nr == 0)
+			break;
+		
+		if (nr < 0) {
+			close(fd);
+			return EIO;
+		}
+		
+		rc = send(conn_sd, fbuf, nr, 0);
+		if (rc < 0) {
+			fprintf(stderr, "send() failed\n");
+			close(fd);
+			return rc;
+		}
+	}
+	
+	close(fd);
+	
+	return EOK;
+}
+
+static int req_process(int conn_sd)
+{
+	int rc = recv_line(conn_sd);
+	if (rc != EOK) {
+		fprintf(stderr, "recv_line() failed\n");
+		return rc;
+	}
+	
+	if (verbose)
+		fprintf(stderr, "Request: %s", lbuf);
+	
+	if (str_lcmp(lbuf, "GET ", 4) != 0) {
+		rc = send_response(conn_sd, msg_not_implemented);
+		return rc;
+	}
+	
+	char *uri = lbuf + 4;
+	char *end_uri = str_chr(uri, ' ');
+	if (end_uri == NULL) {
+		end_uri = lbuf + lbuf_used - 2;
+		assert(*end_uri == '\r');
+	}
+	
+	*end_uri = '\0';
+	if (verbose)
+		fprintf(stderr, "Requested URI: %s\n", uri);
+	
+	if (!uri_is_valid(uri)) {
+		rc = send_response(conn_sd, msg_bad_request);
+		return rc;
+	}
+	
+	return uri_get(uri, conn_sd);
+}
+
+static void usage(void)
+{
+	printf("Skeletal server\n"
+	    "\n"
+	    "Usage: " NAME " [options]\n"
+	    "\n"
+	    "Where options are:\n"
+	    "-p port_number | --port=port_number\n"
+	    "\tListening port (default " STRING(DEFAULT_PORT) ").\n"
+	    "\n"
+	    "-h | --help\n"
+	    "\tShow this application help.\n"
+	    "-v | --verbose\n"
+	    "\tVerbose mode\n");
+}
+
+static int parse_option(int argc, char *argv[], int *index)
+{
+	int value;
+	int rc;
+	
+	switch (argv[*index][1]) {
+	case 'h':
+		usage();
+		exit(0);
+		break;
+	case 'p':
+		rc = arg_parse_int(argc, argv, index, &value, 0);
+		if (rc != EOK)
+			return rc;
+		
+		port = (uint16_t) value;
+		break;
+	case 'v':
+		verbose = true;
+		break;
+	/* Long options with double dash */
+	case '-':
+		if (str_lcmp(argv[*index] + 2, "help", 5) == 0) {
+			usage();
+			exit(0);
+		} else if (str_lcmp(argv[*index] + 2, "port=", 5) == 0) {
+			rc = arg_parse_int(argc, argv, index, &value, 7);
+			if (rc != EOK)
+				return rc;
+			
+			port = (uint16_t) value;
+		} else if (str_cmp(argv[*index] +2, "verbose") == 0) {
+			verbose = true;
+		} else {
+			usage();
+			return EINVAL;
+		}
+		break;
+	default:
+		usage();
+		return EINVAL;
+	}
+	
+	return EOK;
+}
+
+int main(int argc, char *argv[])
+{
+	/* Parse command line arguments */
+	for (int i = 1; i < argc; i++) {
+		if (argv[i][0] == '-') {
+			int rc = parse_option(argc, argv, &i);
+			if (rc != EOK)
+				return rc;
+		} else {
+			usage();
+			return EINVAL;
+		}
+	}
+	
+	struct sockaddr_in addr;
+	
+	addr.sin_family = AF_INET;
+	addr.sin_port = htons(port);
+	
+	int rc = inet_pton(AF_INET, "127.0.0.1", (void *)
+	    &addr.sin_addr.s_addr);
+	if (rc != EOK) {
+		fprintf(stderr, "Error parsing network address (%s)\n",
+		    str_error(rc));
+		return 1;
+	}
+	
+	printf("%s: HelenOS web server\n", NAME);
+
+	if (verbose)
+		fprintf(stderr, "Creating socket\n");
+	
+	int listen_sd = socket(PF_INET, SOCK_STREAM, 0);
+	if (listen_sd < 0) {
+		fprintf(stderr, "Error creating listening socket (%s)\n",
+		    str_error(listen_sd));
+		return 2;
+	}
+	
+	rc = bind(listen_sd, (struct sockaddr *) &addr, sizeof(addr));
+	if (rc != EOK) {
+		fprintf(stderr, "Error binding socket (%s)\n",
+		    str_error(rc));
+		return 3;
+	}
+	
+	rc = listen(listen_sd, BACKLOG_SIZE);
+	if (rc != EOK) {
+		fprintf(stderr, "listen() failed (%s)\n", str_error(rc));
+		return 4;
+	}
+	
+	fprintf(stderr, "%s: Listening for connections at port %" PRIu16 "\n",
+	    NAME, port);
+
+	task_retval(0);
+
+	while (true) {
+		struct sockaddr_in raddr;
+		socklen_t raddr_len = sizeof(raddr);
+		int conn_sd = accept(listen_sd, (struct sockaddr *) &raddr,
+		    &raddr_len);
+		
+		if (conn_sd < 0) {
+			fprintf(stderr, "accept() failed (%s)\n", str_error(rc));
+			continue;
+		}
+		
+		if (verbose) {
+			fprintf(stderr, "Connection accepted (sd=%d), "
+			    "waiting for request\n", conn_sd);
+		}
+		
+		rbuf_out = 0;
+		rbuf_in = 0;
+		
+		rc = req_process(conn_sd);
+		if (rc != EOK)
+			fprintf(stderr, "Error processing request (%s)\n",
+			    str_error(rc));
+		
+		rc = closesocket(conn_sd);
+		if (rc != EOK) {
+			fprintf(stderr, "Error closing connection socket (%s)\n",
+			    str_error(rc));
+			closesocket(listen_sd);
+			return 5;
+		}
+		
+		if (verbose)
+			fprintf(stderr, "Connection closed\n");
+	}
+	
+	/* Not reached */
+	return 0;
+}
+
+/** @}
+ */
