Index: uspace/srv/net/dhcp/Makefile
===================================================================
--- uspace/srv/net/dhcp/Makefile	(revision bd88bee745abe1948dac683975e4b015ecdfaf39)
+++ uspace/srv/net/dhcp/Makefile	(revision 66ec63a6d73f7dc361145d9e2af7aebf258cee02)
@@ -32,5 +32,6 @@
 SOURCES = \
 	dhcp.c \
-	main.c
+	main.c \
+	transport.c
 
 include $(USPACE_PREFIX)/Makefile.common
Index: uspace/srv/net/dhcp/dhcp.c
===================================================================
--- uspace/srv/net/dhcp/dhcp.c	(revision bd88bee745abe1948dac683975e4b015ecdfaf39)
+++ uspace/srv/net/dhcp/dhcp.c	(revision 66ec63a6d73f7dc361145d9e2af7aebf258cee02)
@@ -35,4 +35,5 @@
  */
 
+#include <adt/list.h>
 #include <bitops.h>
 #include <inet/addr.h>
@@ -49,12 +50,10 @@
 #include "dhcp.h"
 #include "dhcp_std.h"
-
-#define NAME "dhcp"
+#include "transport.h"
 
 #define MAX_MSG_SIZE 1024
-
-static int transport_fd = -1;
-static inet_link_info_t link_info;
 static uint8_t msgbuf[MAX_MSG_SIZE];
+
+static list_t dhcp_links;
 
 typedef struct {
@@ -71,4 +70,13 @@
 } dhcp_offer_t;
 
+typedef struct {
+	link_t links;
+	service_id_t link_id;
+	inet_link_info_t link_info;
+	dhcp_transport_t dt;
+} dhcp_link_t;
+
+static void dhcpsrv_recv(void *, void *, size_t);
+
 /** Decode subnet mask into subnet prefix length. */
 static int subnet_mask_decode(uint32_t mask, int *bits)
@@ -103,24 +111,5 @@
 }
 
-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) {
-		log_msg(LOG_DEFAULT, LVL_ERROR, "Sending failed");
-		return rc;
-	}
-
-	return EOK;
-}
-
-static int dhcp_send_discover(void)
+static int dhcp_send_discover(dhcp_link_t *dlink)
 {
 	dhcp_hdr_t *hdr = (dhcp_hdr_t *)msgbuf;
@@ -134,5 +123,5 @@
 	hdr->flags = flag_broadcast;
 
-	addr48(link_info.mac_addr, hdr->chaddr);
+	addr48(dlink->link_info.mac_addr, hdr->chaddr);
 	hdr->opt_magic = host2uint32_t_be(dhcp_opt_magic);
 
@@ -142,30 +131,8 @@
 	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) {
-		log_msg(LOG_DEFAULT, LVL_ERROR, "recvfrom failed (%d)", rc);
-		return rc;
-	}
-
-	recv_size = (size_t)rc;
-	*rmsg = msgbuf;
-	*rsize = recv_size;
-
-	return EOK;
-}
-
-static int dhcp_send_request(dhcp_offer_t *offer)
+	return dhcp_send(&dlink->dt, msgbuf, sizeof(dhcp_hdr_t) + 4);
+}
+
+static int dhcp_send_request(dhcp_link_t *dlink, dhcp_offer_t *offer)
 {
 	dhcp_hdr_t *hdr = (dhcp_hdr_t *)msgbuf;
@@ -180,5 +147,5 @@
 	hdr->flags = flag_broadcast;
 	hdr->ciaddr = host2uint32_t_be(offer->oaddr.addr);
-	addr48(link_info.mac_addr, hdr->chaddr);
+	addr48(dlink->link_info.mac_addr, hdr->chaddr);
 	hdr->opt_magic = host2uint32_t_be(dhcp_opt_magic);
 
@@ -205,8 +172,8 @@
 	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)
+	return dhcp_send(&dlink->dt, msgbuf, sizeof(dhcp_hdr_t) + i);
+}
+
+static int dhcp_parse_reply(void *msg, size_t size, dhcp_offer_t *offer)
 {
 	dhcp_hdr_t *hdr = (dhcp_hdr_t *)msg;
@@ -390,6 +357,6 @@
 		rc = dnsr_set_srvaddr(&offer->dns_server);
 		if (rc != EOK) {
-			log_msg(LOG_DEFAULT, LVL_ERROR, "%s: Error setting "
-			    "nameserver address (%d))", NAME, rc);
+			log_msg(LOG_DEFAULT, LVL_ERROR, "Error setting "
+			    "nameserver address (%d))", rc);
 			return rc;
 		}
@@ -401,76 +368,45 @@
 int dhcpsrv_link_add(service_id_t link_id)
 {
-	int fd;
-	struct sockaddr_in laddr;
-	void *msg;
-	size_t msg_size;
-	dhcp_offer_t offer;
+	dhcp_link_t *dlink;
 	int rc;
 
 	log_msg(LOG_DEFAULT, LVL_DEBUG, "dhcpsrv_link_add(%zu)", link_id);
 
+	dlink = calloc(1, sizeof(dhcp_link_t));
+	if (dlink == NULL)
+		return ENOMEM;
+
+	dlink->link_id = link_id;
+
 	/* Get link hardware address */
-	rc = inetcfg_link_get(link_id, &link_info);
+	rc = inetcfg_link_get(link_id, &dlink->link_info);
 	if (rc != EOK) {
 		log_msg(LOG_DEFAULT, LVL_ERROR, "Error getting properties "
 		    "for link %zu.", link_id);
-		return EIO;
-	}
-
-	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 EIO;
-
-	log_msg(LOG_DEFAULT, LVL_DEBUG, "Bind socket.");
-	rc = bind(fd, (struct sockaddr *)&laddr, sizeof(laddr));
-	if (rc != EOK)
-		return EIO;
-
-	log_msg(LOG_DEFAULT, LVL_DEBUG, "Set socket options");
-	rc = setsockopt(fd, SOL_SOCKET, SO_IPLINK, &link_id, sizeof(link_id));
-	if (rc != EOK)
-		return EIO;
-
-	transport_fd = fd;
+		rc = EIO;
+		goto error;
+	}
+
+	rc = dhcp_transport_init(&dlink->dt, link_id, dhcpsrv_recv, dlink);
+	if (rc != EOK) {
+		log_msg(LOG_DEFAULT, LVL_ERROR, "Error initializing DHCP "
+		    "transport for link %s.", dlink->link_info.name);
+		rc = EIO;
+		goto error;
+	}
 
 	log_msg(LOG_DEFAULT, LVL_DEBUG, "Send DHCPDISCOVER");
-	rc = dhcp_send_discover();
-	if (rc != EOK)
-		return EIO;
-
-	rc = dhcp_recv_msg(&msg, &msg_size);
-	if (rc != EOK)
-		return EIO;
-
-	log_msg(LOG_DEFAULT, LVL_DEBUG, "Received %zu bytes", msg_size);
-
-	rc = dhcp_recv_reply(msg, msg_size, &offer);
-	if (rc != EOK)
-		return EIO;
-
-	rc = dhcp_send_request(&offer);
-	if (rc != EOK)
-		return EIO;
-
-	rc = dhcp_recv_msg(&msg, &msg_size);
-	if (rc != EOK)
-		return EIO;
-
-	log_msg(LOG_DEFAULT, LVL_DEBUG, "Received %zu bytes", msg_size);
-
-	rc = dhcp_recv_reply(msg, msg_size, &offer);
-	if (rc != EOK)
-		return EIO;
-
-	rc = dhcp_cfg_create(link_id, &offer);
-	if (rc != EOK)
-		return EIO;
-
-	closesocket(fd);
+	rc = dhcp_send_discover(dlink);
+	if (rc != EOK) {
+		rc = EIO;
+		goto error;
+	}
+
+	if (0) list_append(&dlink->links, &dhcp_links);
+
 	return EOK;
+error:
+	free(dlink);
+	return rc;
 }
 
@@ -480,4 +416,34 @@
 }
 
+static void dhcpsrv_recv(void *arg, void *msg, size_t size)
+{
+	dhcp_link_t *dlink = (dhcp_link_t *)arg;
+	dhcp_offer_t offer;
+	int rc;
+
+	log_msg(LOG_DEFAULT, LVL_DEBUG, "dhcpsrv_recv() %zu bytes", size);
+
+	rc = dhcp_parse_reply(msg, size, &offer);
+	if (rc != EOK) {
+		log_msg(LOG_DEFAULT, LVL_DEBUG, "Error parsing reply");
+		return;
+	}
+
+	if (offer.msg_type == msg_dhcpoffer) {
+		rc = dhcp_send_request(dlink, &offer);
+		if (rc != EOK) {
+			log_msg(LOG_DEFAULT, LVL_DEBUG, "Error sending request.");
+			return;
+		}
+	}
+
+	if (offer.msg_type == msg_dhcpack) {
+		rc = dhcp_cfg_create(dlink->link_id, &offer);
+		if (rc != EOK) {
+			log_msg(LOG_DEFAULT, LVL_DEBUG, "Error creating configuration.");
+			return;
+		}
+	}
+}
 
 /** @}
Index: uspace/srv/net/dhcp/transport.c
===================================================================
--- uspace/srv/net/dhcp/transport.c	(revision 66ec63a6d73f7dc361145d9e2af7aebf258cee02)
+++ uspace/srv/net/dhcp/transport.c	(revision 66ec63a6d73f7dc361145d9e2af7aebf258cee02)
@@ -0,0 +1,189 @@
+/*
+ * 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 <io/log.h>
+#include <loc.h>
+#include <net/in.h>
+#include <net/inet.h>
+#include <net/socket.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+#include "dhcp.h"
+#include "dhcp_std.h"
+#include "transport.h"
+
+#define MAX_MSG_SIZE 1024
+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;
+
+static int dhcp_recv_fibril(void *);
+
+int dhcp_send(dhcp_transport_t *dt, 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(dt->fd, msg, size, 0,
+	    (struct sockaddr *)&addr, sizeof(addr));
+	if (rc != EOK) {
+		log_msg(LOG_DEFAULT, LVL_ERROR, "Sending failed");
+		return rc;
+	}
+
+	return EOK;
+}
+
+static int dhcp_recv_msg(dhcp_transport_t *dt, 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(dt->fd, msgbuf, MAX_MSG_SIZE, 0,
+	    (struct sockaddr *)&src_addr, &src_addr_size);
+	if (rc < 0) {
+		log_msg(LOG_DEFAULT, LVL_ERROR, "recvfrom failed (%d)", rc);
+		return rc;
+	}
+
+	recv_size = (size_t)rc;
+	*rmsg = msgbuf;
+	*rsize = recv_size;
+
+	return EOK;
+}
+
+int dhcp_transport_init(dhcp_transport_t *dt, service_id_t link_id,
+    dhcp_recv_cb_t recv_cb, void *arg)
+{
+	int fd;
+	struct sockaddr_in laddr;
+	int fid;
+	int rc;
+
+	log_msg(LOG_DEFAULT, LVL_DEBUG, "dhcptransport_init()");
+
+	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) {
+		rc = EIO;
+		goto error;
+	}
+
+	log_msg(LOG_DEFAULT, LVL_DEBUG, "Bind socket.");
+	rc = bind(fd, (struct sockaddr *)&laddr, sizeof(laddr));
+	if (rc != EOK) {
+		rc = EIO;
+		goto error;
+	}
+
+	log_msg(LOG_DEFAULT, LVL_DEBUG, "Set socket options");
+	rc = setsockopt(fd, SOL_SOCKET, SO_IPLINK, &link_id, sizeof(link_id));
+	if (rc != EOK) {
+		rc = EIO;
+		goto error;
+	}
+
+	dt->fd = fd;
+	dt->recv_cb = recv_cb;
+	dt->cb_arg = arg;
+
+	fid = fibril_create(dhcp_recv_fibril, dt);
+	if (fid == 0) {
+		rc = ENOMEM;
+		goto error;
+	}
+
+	dt->recv_fid = fid;
+	fibril_add_ready(fid);
+
+	return EOK;
+error:
+	closesocket(fd);
+	return rc;
+}
+
+void dhcp_transport_fini(dhcp_transport_t *dt)
+{
+	closesocket(dt->fd);
+}
+
+static int dhcp_recv_fibril(void *arg)
+{
+	dhcp_transport_t *dt = (dhcp_transport_t *)arg;
+	void *msg;
+	size_t size;
+	int rc;
+
+	while (true) {
+		rc = dhcp_recv_msg(dt, &msg, &size);
+		if (rc != EOK)
+			break;
+
+		dt->recv_cb(dt->cb_arg, msg, size);
+	}
+
+	return EOK;
+}
+
+/** @}
+ */
Index: uspace/srv/net/dhcp/transport.h
===================================================================
--- uspace/srv/net/dhcp/transport.h	(revision 66ec63a6d73f7dc361145d9e2af7aebf258cee02)
+++ uspace/srv/net/dhcp/transport.h	(revision 66ec63a6d73f7dc361145d9e2af7aebf258cee02)
@@ -0,0 +1,67 @@
+/*
+ * 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
+ */
+
+#ifndef TRANSPORT_H
+#define TRANSPORT_H
+
+#include <ipc/loc.h>
+#include <sys/types.h>
+
+struct dhcp_transport;
+typedef struct dhcp_transport dhcp_transport_t;
+
+typedef void (*dhcp_recv_cb_t)(void *, void *, size_t);
+
+struct dhcp_transport {
+	/** Transport socket */
+	int fd;
+	/** Receive callback */
+	dhcp_recv_cb_t recv_cb;
+	/** Callback argument */
+	void *cb_arg;
+	/** Receive fibril ID */
+	int recv_fid;
+};
+
+extern int dhcp_transport_init(dhcp_transport_t *, service_id_t,
+    dhcp_recv_cb_t, void *);
+extern void dhcp_transport_fini(dhcp_transport_t *);
+extern int dhcp_send(dhcp_transport_t *dt, void *msg, size_t size);
+
+#endif
+
+/** @}
+ */
