Index: uspace/srv/net/dhcp/dhcp.c
===================================================================
--- uspace/srv/net/dhcp/dhcp.c	(revision 66ec63a6d73f7dc361145d9e2af7aebf258cee02)
+++ uspace/srv/net/dhcp/dhcp.c	(revision b417559d1675d7d38ae1bd8281e4d9a5ded91c92)
@@ -37,4 +37,5 @@
 #include <adt/list.h>
 #include <bitops.h>
+#include <fibril_synch.h>
 #include <inet/addr.h>
 #include <inet/dnsr.h>
@@ -52,8 +53,25 @@
 #include "transport.h"
 
+#define DHCP_DISCOVER_TIMEOUT_VAL (5 * 1000 * 1000)
+#define DHCP_REQUEST_TIMEOUT_VAL (1 * 1000 * 1000)
+
 #define MAX_MSG_SIZE 1024
 static uint8_t msgbuf[MAX_MSG_SIZE];
 
+/** List of registered links (of dhcp_link_t) */
 static list_t dhcp_links;
+
+static void dhcpsrv_discover_timeout(void *);
+static void dhcpsrv_request_timeout(void *);
+
+typedef enum {
+	ds_bound,
+	ds_init,
+	ds_init_reboot,
+	ds_rebinding,
+	ds_renewing,
+	ds_requesting,
+	ds_selecting
+} dhcp_state_t;
 
 typedef struct {
@@ -71,8 +89,18 @@
 
 typedef struct {
+	/** Link to dhcp_links list */
 	link_t links;
+	/** Link service ID */
 	service_id_t link_id;
+	/** Link info */
 	inet_link_info_t link_info;
+	/** Transport */
 	dhcp_transport_t dt;
+	/** Transport timeout */
+	fibril_timer_t *timeout;
+	/** Link state */
+	dhcp_state_t state;
+	/** Last received offer */
+	dhcp_offer_t offer;
 } dhcp_link_t;
 
@@ -366,4 +394,19 @@
 }
 
+void dhcpsrv_links_init(void)
+{
+	list_initialize(&dhcp_links);
+}
+
+static dhcp_link_t *dhcpsrv_link_find(service_id_t link_id)
+{
+	list_foreach(dhcp_links, links, dhcp_link_t, dlink) {
+		if (dlink->link_id == link_id)
+			return dlink;
+	}
+
+	return NULL;
+}
+
 int dhcpsrv_link_add(service_id_t link_id)
 {
@@ -372,4 +415,10 @@
 
 	log_msg(LOG_DEFAULT, LVL_DEBUG, "dhcpsrv_link_add(%zu)", link_id);
+
+	if (dhcpsrv_link_find(link_id) != NULL) {
+		log_msg(LOG_DEFAULT, LVL_DEBUG, "Link %zu already added",
+		    link_id);
+		return EEXIST;
+	}
 
 	dlink = calloc(1, sizeof(dhcp_link_t));
@@ -378,4 +427,9 @@
 
 	dlink->link_id = link_id;
+	dlink->timeout = fibril_timer_create();
+	if (dlink->timeout == NULL) {
+		rc = ENOMEM;
+		goto error;
+	}
 
 	/* Get link hardware address */
@@ -396,4 +450,6 @@
 	}
 
+	dlink->state = ds_selecting;
+
 	log_msg(LOG_DEFAULT, LVL_DEBUG, "Send DHCPDISCOVER");
 	rc = dhcp_send_discover(dlink);
@@ -403,8 +459,13 @@
 	}
 
-	if (0) list_append(&dlink->links, &dhcp_links);
+	fibril_timer_set(dlink->timeout, DHCP_DISCOVER_TIMEOUT_VAL,
+	    dhcpsrv_discover_timeout, dlink);
+
+	list_append(&dlink->links, &dhcp_links);
 
 	return EOK;
 error:
+	if (dlink != NULL && dlink->timeout != NULL)
+		fibril_timer_destroy(dlink->timeout);
 	free(dlink);
 	return rc;
@@ -416,4 +477,50 @@
 }
 
+static void dhcpsrv_recv_offer(dhcp_link_t *dlink, dhcp_offer_t *offer)
+{
+	int rc;
+
+	if (dlink->state != ds_selecting) {
+		log_msg(LOG_DEFAULT, LVL_DEBUG, "Received offer in state "
+		    " %d, ignoring.", (int)dlink->state);
+		return;
+	}
+
+	fibril_timer_clear(dlink->timeout);
+	dlink->offer = *offer;
+	dlink->state = ds_requesting;
+
+	log_msg(LOG_DEFAULT, LVL_DEBUG, "Send DHCPREQUEST");
+	rc = dhcp_send_request(dlink, offer);
+	if (rc != EOK) {
+		log_msg(LOG_DEFAULT, LVL_DEBUG, "Error sending request.");
+		return;
+	}
+
+	fibril_timer_set(dlink->timeout, DHCP_REQUEST_TIMEOUT_VAL,
+	    dhcpsrv_request_timeout, dlink);
+}
+
+static void dhcpsrv_recv_ack(dhcp_link_t *dlink, dhcp_offer_t *offer)
+{
+	int rc;
+
+	if (dlink->state != ds_requesting) {
+		log_msg(LOG_DEFAULT, LVL_DEBUG, "Received ack in state "
+		    " %d, ignoring.", (int)dlink->state);
+		return;
+	}
+
+	fibril_timer_clear(dlink->timeout);
+	dlink->offer = *offer;
+	dlink->state = ds_bound;
+
+	rc = dhcp_cfg_create(dlink->link_id, offer);
+	if (rc != EOK) {
+		log_msg(LOG_DEFAULT, LVL_DEBUG, "Error creating configuration.");
+		return;
+	}
+}
+
 static void dhcpsrv_recv(void *arg, void *msg, size_t size)
 {
@@ -430,19 +537,54 @@
 	}
 
-	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;
-		}
-	}
+	switch (offer.msg_type) {
+	case msg_dhcpoffer:
+		dhcpsrv_recv_offer(dlink, &offer);
+		break;
+	case msg_dhcpack:
+		dhcpsrv_recv_ack(dlink, &offer);
+		break;
+	default:
+		log_msg(LOG_DEFAULT, LVL_DEBUG, "Received unexpected "
+		    "message type. %d", (int)offer.msg_type);
+		break;
+	}
+}
+
+static void dhcpsrv_discover_timeout(void *arg)
+{
+	dhcp_link_t *dlink = (dhcp_link_t *)arg;
+	int rc;
+
+	assert(dlink->state == ds_selecting);
+	log_msg(LOG_DEFAULT, LVL_NOTE, "dcpsrv_discover_timeout");
+
+	log_msg(LOG_DEFAULT, LVL_DEBUG, "Send DHCPDISCOVER");
+	rc = dhcp_send_discover(dlink);
+	if (rc != EOK) {
+		log_msg(LOG_DEFAULT, LVL_ERROR, "Error sending DHCPDISCOVER");
+		return;
+	}
+
+	fibril_timer_set(dlink->timeout, DHCP_DISCOVER_TIMEOUT_VAL,
+	    dhcpsrv_discover_timeout, dlink);
+}
+
+static void dhcpsrv_request_timeout(void *arg)
+{
+	dhcp_link_t *dlink = (dhcp_link_t *)arg;
+	int rc;
+
+	assert(dlink->state == ds_requesting);
+	log_msg(LOG_DEFAULT, LVL_NOTE, "dcpsrv_request_timeout");
+
+	log_msg(LOG_DEFAULT, LVL_DEBUG, "Send DHCPREQUEST");
+	rc = dhcp_send_request(dlink, &dlink->offer);
+	if (rc != EOK) {
+		log_msg(LOG_DEFAULT, LVL_DEBUG, "Error sending request.");
+		return;
+	}
+
+	fibril_timer_set(dlink->timeout, DHCP_REQUEST_TIMEOUT_VAL,
+	    dhcpsrv_request_timeout, dlink);
 }
 
Index: uspace/srv/net/dhcp/dhcp.h
===================================================================
--- uspace/srv/net/dhcp/dhcp.h	(revision 66ec63a6d73f7dc361145d9e2af7aebf258cee02)
+++ uspace/srv/net/dhcp/dhcp.h	(revision b417559d1675d7d38ae1bd8281e4d9a5ded91c92)
@@ -40,4 +40,5 @@
 #include <ipc/loc.h>
 
+extern void dhcpsrv_links_init(void);
 extern int dhcpsrv_link_add(service_id_t);
 extern int dhcpsrv_link_remove(service_id_t);
Index: uspace/srv/net/dhcp/main.c
===================================================================
--- uspace/srv/net/dhcp/main.c	(revision 66ec63a6d73f7dc361145d9e2af7aebf258cee02)
+++ uspace/srv/net/dhcp/main.c	(revision b417559d1675d7d38ae1bd8281e4d9a5ded91c92)
@@ -56,4 +56,6 @@
 
 	log_msg(LOG_DEFAULT, LVL_DEBUG, "dhcp_init()");
+
+	dhcpsrv_links_init();
 
 	rc = inetcfg_init();
Index: uspace/srv/net/inetsrv/inet_link.c
===================================================================
--- uspace/srv/net/inetsrv/inet_link.c	(revision 66ec63a6d73f7dc361145d9e2af7aebf258cee02)
+++ uspace/srv/net/inetsrv/inet_link.c	(revision b417559d1675d7d38ae1bd8281e4d9a5ded91c92)
@@ -56,4 +56,5 @@
 
 static int inet_iplink_recv(iplink_t *, iplink_recv_sdu_t *, uint16_t);
+static inet_link_t *inet_link_get_by_id_locked(sysarg_t);
 
 static iplink_ev_ops_t inet_iplink_ev_ops = {
@@ -183,5 +184,17 @@
 
 	log_msg(LOG_DEFAULT, LVL_DEBUG, "Opened IP link '%s'", ilink->svc_name);
+
+	fibril_mutex_lock(&inet_links_lock);
+
+	if (inet_link_get_by_id_locked(sid) != NULL) {
+		fibril_mutex_unlock(&inet_links_lock);
+		log_msg(LOG_DEFAULT, LVL_DEBUG, "Link %zu already open",
+		    sid);
+		rc = EEXIST;
+		goto error;
+	}
+
 	list_append(&ilink->link_list, &inet_links);
+	fibril_mutex_unlock(&inet_links_lock);
 
 	inet_addrobj_t *addr = NULL;
@@ -412,17 +425,25 @@
 }
 
+static inet_link_t *inet_link_get_by_id_locked(sysarg_t link_id)
+{
+	assert(fibril_mutex_is_locked(&inet_links_lock));
+
+	list_foreach(inet_links, link_list, inet_link_t, ilink) {
+		if (ilink->svc_id == link_id)
+			return ilink;
+	}
+
+	return NULL;
+}
+
 inet_link_t *inet_link_get_by_id(sysarg_t link_id)
 {
+	inet_link_t *ilink;
+
 	fibril_mutex_lock(&inet_links_lock);
-
-	list_foreach(inet_links, link_list, inet_link_t, ilink) {
-		if (ilink->svc_id == link_id) {
-			fibril_mutex_unlock(&inet_links_lock);
-			return ilink;
-		}
-	}
-
+	ilink = inet_link_get_by_id_locked(link_id);
 	fibril_mutex_unlock(&inet_links_lock);
-	return NULL;
+
+	return ilink;
 }
 
