Index: uspace/srv/net/dhcp/Makefile
===================================================================
--- uspace/srv/net/dhcp/Makefile	(revision d7b7f5e312adb3acb6e7c2044551454dfd1e4019)
+++ uspace/srv/net/dhcp/Makefile	(revision d7b7f5e312adb3acb6e7c2044551454dfd1e4019)
@@ -0,0 +1,35 @@
+#
+# Copyright (c) 2013 Jiri Svoboda
+# All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+#
+# - Redistributions of source code must retain the above copyright
+#   notice, this list of conditions and the following disclaimer.
+# - Redistributions in binary form must reproduce the above copyright
+#   notice, this list of conditions and the following disclaimer in the
+#   documentation and/or other materials provided with the distribution.
+# - The name of the author may not be used to endorse or promote products
+#   derived from this software without specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+# IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+# OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+# IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+# NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+# THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+#
+
+USPACE_PREFIX = ../../..
+BINARY = dhcp
+
+SOURCES = \
+	dhcp.c 
+
+include $(USPACE_PREFIX)/Makefile.common
Index: uspace/srv/net/dhcp/dhcp.c
===================================================================
--- uspace/srv/net/dhcp/dhcp.c	(revision d7b7f5e312adb3acb6e7c2044551454dfd1e4019)
+++ uspace/srv/net/dhcp/dhcp.c	(revision d7b7f5e312adb3acb6e7c2044551454dfd1e4019)
@@ -0,0 +1,491 @@
+/*
+ * Copyright (c) 2013 Jiri Svoboda
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ *   notice, this list of conditions and the following disclaimer.
+ * - Redistributions in binary form must reproduce the above copyright
+ *   notice, this list of conditions and the following disclaimer in the
+ *   documentation and/or other materials provided with the distribution.
+ * - The name of the author may not be used to endorse or promote products
+ *   derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/** @addtogroup dhcp
+ * @{
+ */
+/**
+ * @file
+ * @brief DHCP client
+ */
+
+#include <bitops.h>
+#include <inet/addr.h>
+#include <inet/dnsr.h>
+#include <inet/inetcfg.h>
+#include <loc.h>
+#include <net/in.h>
+#include <net/inet.h>
+#include <net/socket.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+#include "dhcp_std.h"
+
+#define NAME "dhcp"
+
+#define MAX_MSG_SIZE 1024
+
+static int transport_fd = -1;
+static addr48_t mac_addr;
+static uint8_t msgbuf[MAX_MSG_SIZE];
+
+typedef struct {
+	/** Message type */
+	enum dhcp_msg_type msg_type;
+	/** Offered address */
+	inet_naddr_t oaddr;
+	/** Server address */
+	inet_addr_t srv_addr;
+	/** Router address */
+	inet_addr_t router;
+	/** DNS server */
+	inet_addr_t dns_server;
+} dhcp_offer_t;
+
+/** Decode subnet mask into subnet prefix length. */
+static int subnet_mask_decode(uint32_t mask, int *bits)
+{
+	int zbits;
+	uint32_t nmask;
+
+	if (mask == 0xffffffff) {
+		*bits = 32;
+		return EOK;
+	}
+
+	zbits = 1 + fnzb32(mask ^ 0xffffffff);
+	nmask = BIT_RRANGE(uint32_t, zbits);
+
+	if ((mask ^ nmask) != 0xffffffff) {
+		/* The mask is not in the form 1**n,0**m */
+		return EINVAL;
+	}
+
+	*bits = 32 - zbits;
+	return EOK;
+}
+
+static uint32_t dhcp_uint32_decode(uint8_t *data)
+{
+	return
+	    ((uint32_t)data[0] << 24) |
+	    ((uint32_t)data[1] << 16) |
+	    ((uint32_t)data[2] << 8) |
+	    ((uint32_t)data[3]);
+}
+
+static int dhcp_send(void *msg, size_t size)
+{
+	struct sockaddr_in addr;
+	int rc;
+
+	addr.sin_family = AF_INET;
+	addr.sin_port = htons(dhcp_server_port);
+	addr.sin_addr.s_addr = htonl(addr32_broadcast_all_hosts);
+
+	rc = sendto(transport_fd, msg, size, 0,
+	    (struct sockaddr *)&addr, sizeof(addr));
+	if (rc != EOK) {
+		printf("Sending failed\n");
+		return rc;
+	}
+
+	return EOK;
+}
+
+static int dhcp_send_discover(void)
+{
+	dhcp_hdr_t *hdr = (dhcp_hdr_t *)msgbuf;
+	uint8_t *opt = msgbuf + sizeof(dhcp_hdr_t);
+
+	memset(msgbuf, 0, MAX_MSG_SIZE);
+	hdr->op = op_bootrequest;
+	hdr->htype = 1; /* AHRD_ETHERNET */
+	hdr->hlen = sizeof(addr48_t);
+	hdr->xid = host2uint32_t_be(42);
+	hdr->flags = flag_broadcast;
+
+	addr48(mac_addr, hdr->chaddr);
+	hdr->opt_magic = host2uint32_t_be(dhcp_opt_magic);
+
+	opt[0] = opt_msg_type;
+	opt[1] = 1;
+	opt[2] = msg_dhcpdiscover;
+	opt[3] = opt_end;
+
+	return dhcp_send(msgbuf, sizeof(dhcp_hdr_t) + 4);
+}
+
+static int dhcp_recv_msg(void **rmsg, size_t *rsize)
+{
+	struct sockaddr_in src_addr;
+	socklen_t src_addr_size;
+	size_t recv_size;
+	int rc;
+
+	src_addr_size = sizeof(src_addr);
+	rc = recvfrom(transport_fd, msgbuf, MAX_MSG_SIZE, 0,
+	    (struct sockaddr *)&src_addr, &src_addr_size);
+	if (rc < 0) {
+		printf("recvfrom failed (%d)\n", rc);
+		return rc;
+	}
+
+	recv_size = (size_t)rc;
+	*rmsg = msgbuf;
+	*rsize = recv_size;
+
+	return EOK;
+}
+
+static int dhcp_send_request(dhcp_offer_t *offer)
+{
+	dhcp_hdr_t *hdr = (dhcp_hdr_t *)msgbuf;
+	uint8_t *opt = msgbuf + sizeof(dhcp_hdr_t);
+	size_t i;
+
+	memset(msgbuf, 0, MAX_MSG_SIZE);
+	hdr->op = op_bootrequest;
+	hdr->htype = 1; /* AHRD_ETHERNET */
+	hdr->hlen = 6;
+	hdr->xid = host2uint32_t_be(42);
+	hdr->flags = flag_broadcast;
+	hdr->ciaddr = host2uint32_t_be(offer->oaddr.addr);
+	addr48(mac_addr, hdr->chaddr);
+	hdr->opt_magic = host2uint32_t_be(dhcp_opt_magic);
+
+	i = 0;
+
+	opt[i++] = opt_msg_type;
+	opt[i++] = 1;
+	opt[i++] = msg_dhcprequest;
+
+	opt[i++] = opt_req_ip_addr;
+	opt[i++] = 4;
+	opt[i++] = offer->oaddr.addr >> 24;
+	opt[i++] = (offer->oaddr.addr >> 16) & 0xff;
+	opt[i++] = (offer->oaddr.addr >> 8) & 0xff;
+	opt[i++] = offer->oaddr.addr & 0xff;
+
+	opt[i++] = opt_server_id;
+	opt[i++] = 4;
+	opt[i++] = offer->srv_addr.addr >> 24;
+	opt[i++] = (offer->srv_addr.addr >> 16) & 0xff;
+	opt[i++] = (offer->srv_addr.addr >> 8) & 0xff;
+	opt[i++] = offer->srv_addr.addr & 0xff;
+
+	opt[i++] = opt_end;
+
+	return dhcp_send(msgbuf, sizeof(dhcp_hdr_t) + i);
+}
+
+static int dhcp_recv_reply(void *msg, size_t size, dhcp_offer_t *offer)
+{
+	dhcp_hdr_t *hdr = (dhcp_hdr_t *)msg;
+	inet_addr_t yiaddr;
+	inet_addr_t siaddr;
+	inet_addr_t giaddr;
+	uint32_t subnet_mask;
+	bool have_subnet_mask = false;
+	bool have_server_id = false;
+	int subnet_bits;
+	char *saddr;
+	uint8_t opt_type, opt_len;
+	uint8_t *msgb;
+	int rc;
+	size_t i;
+
+	printf("Receive reply\n");
+	memset(offer, 0, sizeof(*offer));
+
+	yiaddr.family = AF_INET;
+	yiaddr.addr = uint32_t_be2host(hdr->yiaddr);
+	rc = inet_addr_format(&yiaddr, &saddr);
+	if (rc != EOK)
+		return rc;
+
+	printf("Your IP address: %s\n", saddr);
+	free(saddr);
+
+	siaddr.family = AF_INET;
+	siaddr.addr = uint32_t_be2host(hdr->siaddr);
+	rc = inet_addr_format(&siaddr, &saddr);
+	if (rc != EOK)
+		return rc;
+
+	printf("Next server IP address: %s\n", saddr);
+	free(saddr);
+
+	giaddr.family = AF_INET;
+	giaddr.addr = uint32_t_be2host(hdr->giaddr);
+	rc = inet_addr_format(&giaddr, &saddr);
+	if (rc != EOK)
+		return rc;
+
+	printf("Relay agent IP address: %s\n", saddr);
+	free(saddr);
+
+	offer->oaddr.family = AF_INET;
+	offer->oaddr.addr = yiaddr.addr;
+
+	msgb = (uint8_t *)msg;
+
+	i = sizeof(dhcp_hdr_t);
+	while (i < size) {
+		opt_type = msgb[i++];
+
+		if (opt_type == opt_pad)
+			continue;
+		if (opt_type == opt_end)
+			break;
+
+		if (i >= size)
+			return EINVAL;
+
+		opt_len = msgb[i++];
+
+		if (i + opt_len > size)
+			return EINVAL;
+
+		switch (opt_type) {
+		case opt_subnet_mask:
+			if (opt_len != 4)
+				return EINVAL;
+			subnet_mask = dhcp_uint32_decode(&msgb[i]);
+			rc = subnet_mask_decode(subnet_mask, &subnet_bits);
+			if (rc != EOK)
+				return EINVAL;
+			offer->oaddr.prefix = subnet_bits;
+			have_subnet_mask = true;
+			break;
+		case opt_msg_type:
+			if (opt_len != 1)
+				return EINVAL;
+			offer->msg_type = msgb[i];
+			break;
+		case opt_server_id:
+			if (opt_len != 4)
+				return EINVAL;
+			offer->srv_addr.family = AF_INET;
+			offer->srv_addr.addr = dhcp_uint32_decode(&msgb[i]);
+			have_server_id = true;
+			break;
+		case opt_router:
+			if (opt_len != 4)
+				return EINVAL;
+			offer->router.family = AF_INET;
+			offer->router.addr = dhcp_uint32_decode(&msgb[i]);
+			break;
+		case opt_dns_server:
+			if (opt_len != 4)
+				return EINVAL;
+			offer->dns_server.family = AF_INET;
+			offer->dns_server.addr = dhcp_uint32_decode(&msgb[i]);
+			break;
+		case opt_end:
+			break;
+		default:
+			break;
+		}
+
+		/* Advance to the next option */
+		i = i + opt_len;
+	}
+
+	if (!have_server_id) {
+		printf("Missing server ID option.\n");
+		return rc;
+	}
+
+	if (!have_subnet_mask) {
+		printf("Missing subnet mask option.\n");
+		return rc;
+	}
+
+	rc = inet_naddr_format(&offer->oaddr, &saddr);
+	if (rc != EOK)
+		return rc;
+
+	printf("Offered network address: %s\n", saddr);
+	free(saddr);
+
+	if (offer->router.addr != 0) {
+		rc = inet_addr_format(&offer->router, &saddr);
+		if (rc != EOK)
+			return rc;
+
+		printf("Router address: %s\n", saddr);
+		free(saddr);
+	}
+
+	if (offer->dns_server.addr != 0) {
+		rc = inet_addr_format(&offer->dns_server, &saddr);
+		if (rc != EOK)
+			return rc;
+
+		printf("DNS server: %s\n", saddr);
+		free(saddr);
+	}
+
+	return EOK;
+}
+
+static int dhcp_cfg_create(service_id_t iplink, dhcp_offer_t *offer)
+{
+	int rc;
+	service_id_t addr_id;
+	service_id_t sroute_id;
+	inet_naddr_t defr;
+
+	rc = inetcfg_addr_create_static("dhcp4a", &offer->oaddr, iplink,
+	    &addr_id);
+	if (rc != EOK) {
+		printf("Error creating IP address %s (%d)\n", "dhcp4a", rc);
+		return rc;
+	}
+
+	if (offer->router.addr != 0) {
+		defr.family = AF_INET;
+		defr.addr = 0;
+		defr.prefix = 0;
+
+		rc = inetcfg_sroute_create("dhcpdef", &defr, &offer->router, &sroute_id);
+		if (rc != EOK) {
+			printf("Error creating default route %s (%d).\n", "dhcpdef",
+			    rc);
+			return rc;
+		}
+	}
+
+	if (offer->dns_server.addr != 0) {
+		rc = dnsr_set_srvaddr(&offer->dns_server);
+		if (rc != EOK) {
+			printf("%s: Failed setting nameserver address (%d))\n",
+			    NAME, rc);
+			return rc;
+		}
+	}
+
+	return EOK;
+}
+
+int main(int argc, char *argv[])
+{
+	int fd;
+	struct sockaddr_in laddr;
+	void *msg;
+	service_id_t iplink;
+	size_t msg_size;
+	dhcp_offer_t offer;
+	int rc;
+
+	if (argc < 2) {
+		printf("syntax: %s <ip-link>\n", NAME);
+		return 1;
+	}
+
+	rc = inetcfg_init();
+	if (rc != EOK) {
+		printf("Failed contacting inet configuration service.\n");
+		return 1;
+	}
+
+	rc = loc_service_get_id(argv[1], &iplink, 0);
+	if (rc != EOK) {
+		printf("Failed resolving service '%s'.\n", argv[1]);
+		return 1;
+	}
+
+	/* XXX Determine MAC address automatically */
+	mac_addr[0] = 0xaa;
+	mac_addr[1] = 0xde;
+	mac_addr[2] = 0xad;
+	mac_addr[3] = 0xbe;
+	mac_addr[4] = 0xef;
+	mac_addr[5] = 0xfe;
+
+	laddr.sin_family = AF_INET;
+	laddr.sin_port = htons(dhcp_client_port);
+	laddr.sin_addr.s_addr = INADDR_ANY;
+
+	fd = socket(PF_INET, SOCK_DGRAM, IPPROTO_UDP);
+	if (fd < 0)
+		return 1;
+
+	printf("Bind socket.\n");
+	rc = bind(fd, (struct sockaddr *)&laddr, sizeof(laddr));
+	if (rc != EOK)
+		return 1;
+
+	printf("Set socket options\n");
+	rc = setsockopt(fd, SOL_SOCKET, SO_IPLINK, &iplink, sizeof(iplink));
+	if (rc != EOK)
+		return 1;
+
+	transport_fd = fd;
+
+	printf("Send DHCPDISCOVER\n");
+	rc = dhcp_send_discover();
+	if (rc != EOK)
+		return 1;
+
+	rc = dhcp_recv_msg(&msg, &msg_size);
+	if (rc != EOK)
+		return 1;
+
+	printf("Received %zu bytes\n", msg_size);
+
+	rc = dhcp_recv_reply(msg, msg_size, &offer);
+	if (rc != EOK)
+		return 1;
+
+	rc = dhcp_send_request(&offer);
+	if (rc != EOK)
+		return 1;
+
+	rc = dhcp_recv_msg(&msg, &msg_size);
+	if (rc != EOK)
+		return 1;
+
+	printf("Received %zu bytes\n", msg_size);
+
+	rc = dhcp_recv_reply(msg, msg_size, &offer);
+	if (rc != EOK)
+		return 1;
+
+	rc = dhcp_cfg_create(iplink, &offer);
+	if (rc != EOK)
+		return 1;
+
+	closesocket(fd);
+	return 0;
+}
+
+/** @}
+ */
Index: uspace/srv/net/dhcp/dhcp_std.h
===================================================================
--- uspace/srv/net/dhcp/dhcp_std.h	(revision d7b7f5e312adb3acb6e7c2044551454dfd1e4019)
+++ uspace/srv/net/dhcp/dhcp_std.h	(revision d7b7f5e312adb3acb6e7c2044551454dfd1e4019)
@@ -0,0 +1,130 @@
+/*
+ * Copyright (c) 2013 Jiri Svoboda
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ *   notice, this list of conditions and the following disclaimer.
+ * - Redistributions in binary form must reproduce the above copyright
+ *   notice, this list of conditions and the following disclaimer in the
+ *   documentation and/or other materials provided with the distribution.
+ * - The name of the author may not be used to endorse or promote products
+ *   derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/** @addtogroup dhcp
+ * @{
+ */
+/**
+ * @file
+ * @brief DHCP standard definitions
+ */
+
+#ifndef DHCP_STD_H
+#define DHCP_STD_H
+
+#include <stdint.h>
+
+enum {
+	dhcp_server_port = 67,
+	dhcp_client_port = 68
+};
+
+/** Fixed part of DHCP message */
+typedef struct {
+	/** Message op code */
+	uint8_t op;
+	/** Hardware address type */
+	uint8_t htype;
+	/** Hardware address length */
+	uint8_t hlen;
+	/** Hops */
+	uint8_t hops;
+	/** Transaction ID */
+	uint32_t xid;
+	/** Seconds elapsed since client began address acqusition or renewal */
+	uint16_t secs;
+	/** Flags */
+	uint16_t flags;
+	/** Client IP address */
+	uint32_t ciaddr;
+	/** Your (client) IP address */
+	uint32_t yiaddr;
+	/** IP address of next server */
+	uint32_t siaddr;
+	/** Relay agent IP address */
+	uint32_t giaddr;
+	/** Client hardware address */
+	uint8_t chaddr[16];
+	/** Server host name */
+	uint8_t sname[64];
+	/** Boot file name */
+	uint8_t file[128];
+	/** Magic cookie signalling the start of DHCP options */
+	uint32_t opt_magic;
+} dhcp_hdr_t;
+
+/** Values for dhcp_hdr_t.op */
+enum dhcp_op {
+	op_bootrequest = 1,
+	op_bootreply = 2
+};
+
+/** Values for dhcp_hdr_t.flags */
+enum dhcp_flags {
+	flag_broadcast = 0x80
+};
+
+/** Magic cookie signalling the start of DHCP options field */
+enum {
+	dhcp_opt_magic = (99 << 24) | (130 << 16) | (83 << 8) | 99
+};
+
+enum dhcp_option_code {
+	/** Padding */
+	opt_pad = 0,
+	/** Subnet mask */
+	opt_subnet_mask = 1,
+	/** Router IP address */
+	opt_router = 3,
+	/** Domain name server */
+	opt_dns_server = 6,
+	/** Requested IP address */
+	opt_req_ip_addr = 50,
+	/** DHCP message type */
+	opt_msg_type = 53,
+	/** Server identifier */
+	opt_server_id = 54,
+	/** End */
+	opt_end = 255
+};
+
+/** DHCP message type */
+enum dhcp_msg_type {
+	msg_dhcpdiscover = 1,
+	msg_dhcpoffer = 2,
+	msg_dhcprequest = 3,
+	msg_dhcpdecline = 4,
+	msg_dhcpack = 5,
+	msg_dhcpnak = 6,
+	msg_dhcprelease = 7
+};
+
+#endif
+
+/** @}
+ */
Index: uspace/srv/net/dnsrsrv/dnsrsrv.c
===================================================================
--- uspace/srv/net/dnsrsrv/dnsrsrv.c	(revision b11f6fb94c92ea221255fe8e522492de3a3a0829)
+++ uspace/srv/net/dnsrsrv/dnsrsrv.c	(revision d7b7f5e312adb3acb6e7c2044551454dfd1e4019)
@@ -60,5 +60,5 @@
 	rc = transport_init();
 	if (rc != EOK) {
-		log_msg(LOG_DEFAULT, LVL_ERROR, "Failed initializing tarnsport.");
+		log_msg(LOG_DEFAULT, LVL_ERROR, "Failed initializing transport.");
 		return EIO;
 	}
Index: uspace/srv/net/ethip/arp.c
===================================================================
--- uspace/srv/net/ethip/arp.c	(revision b11f6fb94c92ea221255fe8e522492de3a3a0829)
+++ uspace/srv/net/ethip/arp.c	(revision d7b7f5e312adb3acb6e7c2044551454dfd1e4019)
@@ -98,4 +98,10 @@
     addr48_t mac_addr)
 {
+	/* Broadcast address */
+	if (ip_addr == addr32_broadcast_all_hosts) {
+		addr48(addr48_broadcast, mac_addr);
+		return EOK;
+	}
+
 	int rc = atrans_lookup(ip_addr, mac_addr);
 	if (rc == EOK)
Index: uspace/srv/net/ethip/ethip_nic.c
===================================================================
--- uspace/srv/net/ethip/ethip_nic.c	(revision b11f6fb94c92ea221255fe8e522492de3a3a0829)
+++ uspace/srv/net/ethip/ethip_nic.c	(revision d7b7f5e312adb3acb6e7c2044551454dfd1e4019)
@@ -202,4 +202,11 @@
 	}
 
+	rc = nic_broadcast_set_mode(nic->sess, NIC_BROADCAST_ACCEPTED);
+	if (rc != EOK) {
+		log_msg(LOG_DEFAULT, LVL_ERROR, "Error enabling "
+		    "reception of broadcast frames on '%s'.", nic->svc_name);
+		goto error;
+	}
+
 	log_msg(LOG_DEFAULT, LVL_DEBUG, "Initialized IP link service,");
 
Index: uspace/srv/net/inetsrv/icmp.c
===================================================================
--- uspace/srv/net/inetsrv/icmp.c	(revision b11f6fb94c92ea221255fe8e522492de3a3a0829)
+++ uspace/srv/net/inetsrv/icmp.c	(revision d7b7f5e312adb3acb6e7c2044551454dfd1e4019)
@@ -105,4 +105,5 @@
 	reply->checksum = host2uint16_t_be(checksum);
 
+	rdgram.iplink = 0;
 	rdgram.src = dgram->dest;
 	rdgram.dest = dgram->src;
@@ -171,4 +172,5 @@
 	inet_addr_set(sdu->dest, &dgram.dest);
 	
+	dgram.iplink = 0;
 	dgram.tos = ICMP_TOS;
 	dgram.data = rdata;
Index: uspace/srv/net/inetsrv/inet_link.c
===================================================================
--- uspace/srv/net/inetsrv/inet_link.c	(revision b11f6fb94c92ea221255fe8e522492de3a3a0829)
+++ uspace/srv/net/inetsrv/inet_link.c	(revision d7b7f5e312adb3acb6e7c2044551454dfd1e4019)
@@ -235,4 +235,5 @@
 	inet_addrobj_t *addr = NULL;
 	
+	/* XXX FIXME Cannot rely on loopback being the first IP link service!! */
 	if (first_link) {
 		addr = inet_addrobj_new();
@@ -240,13 +241,4 @@
 		inet_naddr(&addr->naddr, 127, 0, 0, 1, 24);
 		first_link = false;
-	} else {
-		/*
-		 * FIXME
-		 * Setting static IPv4 address for testing purposes:
-		 * 10.0.2.15/24
-		 */
-		addr = inet_addrobj_new();
-		
-		inet_naddr(&addr->naddr, 10, 0, 2, 15, 24);
 	}
 	
Index: uspace/srv/net/inetsrv/inetsrv.c
===================================================================
--- uspace/srv/net/inetsrv/inetsrv.c	(revision b11f6fb94c92ea221255fe8e522492de3a3a0829)
+++ uspace/srv/net/inetsrv/inetsrv.c	(revision d7b7f5e312adb3acb6e7c2044551454dfd1e4019)
@@ -68,4 +68,9 @@
 };
 
+static inet_addr_t broadcast4_all_hosts = {
+	.family = AF_INET,
+	.addr = 0xffffffff
+};
+
 static inet_addr_t multicast_all_nodes = {
 	.family = AF_INET6,
@@ -119,15 +124,4 @@
 	}
 	
-	inet_sroute_t *sroute = inet_sroute_new();
-	if (sroute == NULL) {
-		log_msg(LOG_DEFAULT, LVL_ERROR, "Failed creating default route (%d).", rc);
-		return ENOMEM;
-	}
-
-	inet_naddr(&sroute->dest, 0, 0, 0, 0, 0);
-	inet_addr(&sroute->router, 10, 0, 2, 2);
-	sroute->name = str_dup("default");
-	inet_sroute_add(sroute);
-
 	rc = inet_link_discovery_start();
 	if (rc != EOK)
@@ -186,5 +180,26 @@
 {
 	inet_dir_t dir;
+	inet_link_t *ilink;
 	int rc;
+
+	if (dgram->iplink != 0) {
+		log_msg(LOG_DEFAULT, LVL_DEBUG, "dgram directly to iplink %zu",
+		    dgram->iplink);
+		/* Send packet directly to the specified IP link */
+		ilink = inet_link_get_by_id(dgram->iplink);
+		if (ilink == 0)
+			return ENOENT;
+
+		if (dgram->src.family != AF_INET ||
+			dgram->dest.family != AF_INET)
+			return EINVAL;
+
+		return inet_link_send_dgram(ilink, dgram->src.addr,
+		    dgram->dest.addr, dgram, proto, ttl, df);
+	}
+
+	log_msg(LOG_DEFAULT, LVL_DEBUG, "dgram to be routed");
+
+	/* Route packet using source/destination addresses */
 
 	rc = inet_find_dir(&dgram->src, &dgram->dest, dgram->tos, &dir);
@@ -214,4 +229,9 @@
 
 	/* Take source address from the address object */
+	if (remote->family == AF_INET && remote->addr == 0xffffffff) {
+		local->family = AF_INET;
+		local->addr = 0;
+		return EOK;
+	}
 	inet_naddr_addr(&dir.aobj->naddr, local);
 	return EOK;
@@ -282,7 +302,8 @@
 	inet_dgram_t dgram;
 	
-	dgram.tos = IPC_GET_ARG1(*icall);
-	
-	uint8_t ttl = IPC_GET_ARG2(*icall);
+	dgram.iplink = IPC_GET_ARG1(*icall);
+	dgram.tos = IPC_GET_ARG2(*icall);
+	
+	uint8_t ttl = IPC_GET_ARG3(*icall);
 	int df = IPC_GET_ARG3(*icall);
 	
@@ -524,5 +545,6 @@
 	if ((addr != NULL) ||
 	    (inet_naddr_compare_mask(&solicited_node_mask, &packet->dest)) ||
-	    (inet_addr_compare(&multicast_all_nodes, &packet->dest))) {
+	    (inet_addr_compare(&multicast_all_nodes, &packet->dest)) || 
+	    (inet_addr_compare(&broadcast4_all_hosts, &packet->dest))) {
 		/* Destined for one of the local addresses */
 
Index: uspace/srv/net/inetsrv/inetsrv.h
===================================================================
--- uspace/srv/net/inetsrv/inetsrv.h	(revision b11f6fb94c92ea221255fe8e522492de3a3a0829)
+++ uspace/srv/net/inetsrv/inetsrv.h	(revision d7b7f5e312adb3acb6e7c2044551454dfd1e4019)
@@ -127,4 +127,5 @@
 
 typedef struct {
+	service_id_t iplink;
 	inet_addr_t src;
 	inet_addr_t dest;
Index: uspace/srv/net/tcp/tcp.c
===================================================================
--- uspace/srv/net/tcp/tcp.c	(revision b11f6fb94c92ea221255fe8e522492de3a3a0829)
+++ uspace/srv/net/tcp/tcp.c	(revision d7b7f5e312adb3acb6e7c2044551454dfd1e4019)
@@ -141,4 +141,5 @@
 	    pdu->text_size);
 
+	dgram.iplink = 0;
 	dgram.src = pdu->src;
 	dgram.dest = pdu->dest;
Index: uspace/srv/net/udp/assoc.c
===================================================================
--- uspace/srv/net/udp/assoc.c	(revision b11f6fb94c92ea221255fe8e522492de3a3a0829)
+++ uspace/srv/net/udp/assoc.c	(revision d7b7f5e312adb3acb6e7c2044551454dfd1e4019)
@@ -184,4 +184,18 @@
 	fibril_mutex_unlock(&assoc_list_lock);
 	udp_assoc_delref(assoc);
+}
+
+/** Set IP link in association.
+ *
+ * @param assoc		Association
+ * @param iplink	IP link
+ */
+void udp_assoc_set_iplink(udp_assoc_t *assoc, service_id_t iplink)
+{
+	log_msg(LOG_DEFAULT, LVL_DEBUG, "udp_assoc_set_iplink(%p, %zu)",
+	    assoc, iplink);
+	fibril_mutex_lock(&assoc->lock);
+	assoc->ident.iplink = iplink;
+	fibril_mutex_unlock(&assoc->lock);
 }
 
Index: uspace/srv/net/udp/assoc.h
===================================================================
--- uspace/srv/net/udp/assoc.h	(revision b11f6fb94c92ea221255fe8e522492de3a3a0829)
+++ uspace/srv/net/udp/assoc.h	(revision d7b7f5e312adb3acb6e7c2044551454dfd1e4019)
@@ -36,4 +36,5 @@
 #define ASSOC_H
 
+#include <ipc/loc.h>
 #include <sys/types.h>
 #include "udp_type.h"
@@ -45,4 +46,5 @@
 extern void udp_assoc_addref(udp_assoc_t *);
 extern void udp_assoc_delref(udp_assoc_t *);
+extern void udp_assoc_set_iplink(udp_assoc_t *, service_id_t);
 extern void udp_assoc_set_foreign(udp_assoc_t *, udp_sock_t *);
 extern void udp_assoc_set_local(udp_assoc_t *, udp_sock_t *);
Index: uspace/srv/net/udp/pdu.c
===================================================================
--- uspace/srv/net/udp/pdu.c	(revision b11f6fb94c92ea221255fe8e522492de3a3a0829)
+++ uspace/srv/net/udp/pdu.c	(revision d7b7f5e312adb3acb6e7c2044551454dfd1e4019)
@@ -215,4 +215,5 @@
 		return ENOMEM;
 
+	npdu->iplink = sp->iplink;
 	npdu->src = sp->local.addr;
 	npdu->dest = sp->foreign.addr;
Index: uspace/srv/net/udp/sock.c
===================================================================
--- uspace/srv/net/udp/sock.c	(revision b11f6fb94c92ea221255fe8e522492de3a3a0829)
+++ uspace/srv/net/udp/sock.c	(revision d7b7f5e312adb3acb6e7c2044551454dfd1e4019)
@@ -1,5 +1,5 @@
 /*
  * Copyright (c) 2008 Lukas Mejdrech
- * Copyright (c) 2012 Jiri Svoboda
+ * Copyright (c) 2013 Jiri Svoboda
  * All rights reserved.
  *
@@ -349,5 +349,6 @@
 	fibril_mutex_lock(&socket->lock);
 	
-	if (inet_addr_is_any(&socket->assoc->ident.local.addr)) {
+	if (inet_addr_is_any(&socket->assoc->ident.local.addr) &&
+		socket->assoc->ident.iplink == 0) {
 		/* Determine local IP address */
 		inet_addr_t loc_addr;
@@ -665,7 +666,49 @@
 static void udp_sock_setsockopt(udp_client_t *client, ipc_callid_t callid, ipc_call_t call)
 {
-	log_msg(LOG_DEFAULT, LVL_DEBUG, "udp_sock_setsockopt()");
-	async_answer_0(callid, ENOTSUP);
-}
+	log_msg(LOG_DEFAULT, LVL_DEBUG, "udp_sock_setsockopt)");
+	log_msg(LOG_DEFAULT, LVL_DEBUG, " - async_data_write_accept");
+	
+	void *data = NULL;
+	size_t data_len;
+	int rc = async_data_write_accept(&data, false, 0, 0, 0, &data_len);
+	if (rc != EOK) {
+		log_msg(LOG_DEFAULT, LVL_DEBUG, " - failed accepting data");
+		async_answer_0(callid, rc);
+		return;
+	}
+	
+	sysarg_t opt_level = SOL_SOCKET;
+	sysarg_t opt_name = SOCKET_GET_OPT_NAME(call);
+	
+	if (opt_level != SOL_SOCKET || opt_name != SO_IPLINK ||
+	    data_len != sizeof(service_id_t)) {
+		log_msg(LOG_DEFAULT, LVL_DEBUG, " - failed opt_level/name/len");
+		log_msg(LOG_DEFAULT, LVL_DEBUG, " - failed opt_level=%d, "
+		    "opt_name=%d, data_len=%zu", (int)opt_level, (int)opt_name,
+		    data_len);
+		async_answer_0(callid, EINVAL);
+		return;
+	}
+	
+	log_msg(LOG_DEFAULT, LVL_DEBUG, " - call socket_cores_find");
+	
+	socket_core_t *sock_core = socket_cores_find(&client->sockets,
+	    SOCKET_GET_SOCKET_ID(call));
+	if (sock_core == NULL) {
+		log_msg(LOG_DEFAULT, LVL_DEBUG, " - failed getting sock_core");
+		async_answer_0(callid, ENOENT);
+		return;
+	}
+	
+	udp_sockdata_t *socket =
+	    (udp_sockdata_t *) sock_core->specific_data;
+	
+	service_id_t iplink = *(service_id_t *)data;
+	udp_uc_set_iplink(socket->assoc, iplink);
+	
+	log_msg(LOG_DEFAULT, LVL_DEBUG, " - success");
+	async_answer_0(callid, EOK);
+}
+
 
 static int udp_sock_recv_fibril(void *arg)
@@ -714,5 +757,4 @@
 
 	log_msg(LOG_DEFAULT, LVL_DEBUG, "udp_sock_recv_fibril() exited loop");
-	fibril_mutex_unlock(&sock->recv_buffer_lock);
 	udp_uc_destroy(sock->assoc);
 
@@ -730,4 +772,6 @@
 	/* Accept the connection */
 	async_answer_0(iid, EOK);
+
+	log_msg(LOG_DEFAULT, LVL_DEBUG, "udp_sock_connection: begin");
 
 	client.sess = async_callback_receive(EXCHANGE_SERIALIZE);
Index: uspace/srv/net/udp/ucall.c
===================================================================
--- uspace/srv/net/udp/ucall.c	(revision b11f6fb94c92ea221255fe8e522492de3a3a0829)
+++ uspace/srv/net/udp/ucall.c	(revision d7b7f5e312adb3acb6e7c2044551454dfd1e4019)
@@ -55,4 +55,12 @@
 	*assoc = nassoc;
 	return UDP_EOK;
+}
+
+void udp_uc_set_iplink(udp_assoc_t *assoc, service_id_t iplink)
+{
+	log_msg(LOG_DEFAULT, LVL_DEBUG, "udp_uc_set_iplink(%p, %zu)",
+	    assoc, iplink);
+
+	udp_assoc_set_iplink(assoc, iplink);
 }
 
Index: uspace/srv/net/udp/ucall.h
===================================================================
--- uspace/srv/net/udp/ucall.h	(revision b11f6fb94c92ea221255fe8e522492de3a3a0829)
+++ uspace/srv/net/udp/ucall.h	(revision d7b7f5e312adb3acb6e7c2044551454dfd1e4019)
@@ -36,8 +36,10 @@
 #define UCALL_H
 
+#include <ipc/loc.h>
 #include <sys/types.h>
 #include "udp_type.h"
 
 extern udp_error_t udp_uc_create(udp_assoc_t **);
+extern void udp_uc_set_iplink(udp_assoc_t *, service_id_t);
 extern udp_error_t udp_uc_set_foreign(udp_assoc_t *, udp_sock_t *);
 extern udp_error_t udp_uc_set_local(udp_assoc_t *, udp_sock_t *);
Index: uspace/srv/net/udp/udp_inet.c
===================================================================
--- uspace/srv/net/udp/udp_inet.c	(revision b11f6fb94c92ea221255fe8e522492de3a3a0829)
+++ uspace/srv/net/udp/udp_inet.c	(revision d7b7f5e312adb3acb6e7c2044551454dfd1e4019)
@@ -84,4 +84,5 @@
 	log_msg(LOG_DEFAULT, LVL_DEBUG, "udp_transmit_pdu()");
 
+	dgram.iplink = pdu->iplink;
 	dgram.src = pdu->src;
 	dgram.dest = pdu->dest;
Index: uspace/srv/net/udp/udp_type.h
===================================================================
--- uspace/srv/net/udp/udp_type.h	(revision b11f6fb94c92ea221255fe8e522492de3a3a0829)
+++ uspace/srv/net/udp/udp_type.h	(revision d7b7f5e312adb3acb6e7c2044551454dfd1e4019)
@@ -38,4 +38,5 @@
 #include <fibril.h>
 #include <fibril_synch.h>
+#include <ipc/loc.h>
 #include <socket_core.h>
 #include <sys/types.h>
@@ -71,4 +72,5 @@
 
 typedef struct {
+	service_id_t iplink;
 	udp_sock_t local;
 	udp_sock_t foreign;
@@ -85,9 +87,10 @@
 /** Encoded PDU */
 typedef struct {
+	/** IP link (optional) */
+	service_id_t iplink;
 	/** Source address */
 	inet_addr_t src;
 	/** Destination address */
 	inet_addr_t dest;
-	
 	/** Encoded PDU data including header */
 	void *data;
@@ -143,4 +146,6 @@
 	/** Connection */
 	udp_assoc_t *assoc;
+	/** User-configured IP link */
+	service_id_t iplink;
 	/** Receiving fibril */
 	fid_t recv_fibril;
