Free (GPLv2) TCP/IP stack developed by TASS Belgium

Dependents:   lpc1768-picotcp-demo ZeroMQ_PicoTCP_Publisher_demo TCPSocket_HelloWorld_PicoTCP Pico_TCP_UDP_Test ... more

PicoTCP. Copyright (c) 2013 TASS Belgium NV.

Released under the GNU General Public License, version 2.

Different licensing models may exist, at the sole discretion of the Copyright holders.

Official homepage: http://www.picotcp.com

Bug tracker: https://github.com/tass-belgium/picotcp/issues

Development steps:

  • initial integration with mbed RTOS
  • generic mbed Ethernet driver
  • high performance NXP LPC1768 specific Ethernet driver
  • Multi-threading support for mbed RTOS
  • Berkeley sockets and integration with the New Socket API
  • Fork of the apps running on top of the New Socket API
  • Scheduling optimizations
  • Debugging/benchmarking/testing

Demo application (measuring TCP sender performance):

Import programlpc1768-picotcp-demo

A PicoTCP demo app testing the ethernet throughput on the lpc1768 mbed board.

Revision:
51:ab4529a384a6
Parent:
29:1a47b7151851
Child:
61:392dc6d4c501
--- a/modules/pico_dhcp_client.c	Tue Aug 06 08:04:03 2013 +0000
+++ b/modules/pico_dhcp_client.c	Mon Sep 02 08:02:21 2013 +0000
@@ -2,730 +2,918 @@
 PicoTCP. Copyright (c) 2012 TASS Belgium NV. Some rights reserved.
 See LICENSE and COPYING for usage.
 
-Authors: Frederik Van Slycken, Kristof Roelants
+Authors: Kristof Roelants, Frederik Van Slycken 
 *********************************************************************/
 
+
 #include "pico_dhcp_client.h"
 #include "pico_stack.h"
 #include "pico_config.h"
 #include "pico_device.h"
 #include "pico_ipv4.h"
 #include "pico_socket.h"
+#include "pico_eth.h"
 
 #ifdef PICO_SUPPORT_DHCPC
+#define dhcpc_dbg(...) do{}while(0)
+//#define dhcpc_dbg dbg
 
-/***********
- * structs *
- ***********/
+/* timer values */
+#define DHCP_CLIENT_REINIT             3000 /* msec */
+#define DHCP_CLIENT_RETRANS            4 /* sec */
+#define DHCP_CLIENT_RETRIES            3
+
+#define DHCP_CLIENT_TIMER_STOPPED      0
+#define DHCP_CLIENT_TIMER_STARTED      1
+
+/* custom statuses */
+#define DHCP_CLIENT_STATUS_INIT        0
+#define DHCP_CLIENT_STATUS_SOCKET      1
+#define DHCP_CLIENT_STATUS_BOUND       2
+#define DHCP_CLIENT_STATUS_LINKED      3
+#define DHCP_CLIENT_STATUS_TRANSMITTED 4
+#define DHCP_CLIENT_STATUS_INITIALIZED 5
 
-static uint8_t dhcp_client_mutex = 1; /* to serialize client negotations if multiple devices */
+/* maximum size of a DHCP message */
+#define DHCP_CLIENT_MAXMSGZISE         PICO_IP_MTU
+
+/* serialize client negotiations if multiple devices */
+/* NOTE: ONLY initialization is serialized! */
+static uint8_t pico_dhcp_client_mutex = 1; 
 
-struct dhcp_timer_param{
-  uint16_t type;
-  struct pico_dhcp_client_cookie* cli;
-  int valid;
+enum dhcp_client_state {
+  DHCP_CLIENT_STATE_INIT_REBOOT = 0,
+  DHCP_CLIENT_STATE_REBOOTING,
+  DHCP_CLIENT_STATE_INIT,
+  DHCP_CLIENT_STATE_SELECTING,
+  DHCP_CLIENT_STATE_REQUESTING,
+  DHCP_CLIENT_STATE_BOUND,
+  DHCP_CLIENT_STATE_RENEWING,
+  DHCP_CLIENT_STATE_REBINDING
+};
+
+struct dhcp_client_timer
+{
+  uint8_t state;
+  uint32_t time;
 };
 
 struct pico_dhcp_client_cookie
 {
+  uint8_t status;
+  uint8_t event;
+  uint8_t retry;
   uint32_t xid;
-  uint32_t *xid_user;
+  uint32_t *uid;
+  enum dhcp_client_state state;
+  void (*cb)(void* dhcpc, int code);
+  unsigned long init_timestamp;
+  struct pico_socket *s;
   struct pico_ip4 address;
   struct pico_ip4 netmask;
   struct pico_ip4 gateway;
   struct pico_ip4 nameserver;
   struct pico_ip4 server_id;
-  uint32_t lease_time;
-  uint32_t T1;
-  uint32_t T2;
-  struct pico_socket* socket;
-  int connected;
-  struct pico_device* device;
-  unsigned long start_time;
-  int attempt;
-  enum dhcp_negotiation_state state;
-  void (*cb)(void* cli, int code);
-  struct dhcp_timer_param* timer_param_1;
-  struct dhcp_timer_param* timer_param_2;
-  struct dhcp_timer_param* timer_param_lease;
-  struct dhcp_timer_param* timer_param_retransmit;
-  int link_added;
+  struct pico_device *dev;
+  struct dhcp_client_timer init_timer;
+  struct dhcp_client_timer requesting_timer;
+  struct dhcp_client_timer renewing_timer;
+  struct dhcp_client_timer rebinding_timer;
+  struct dhcp_client_timer T1_timer;
+  struct dhcp_client_timer T2_timer;
+  struct dhcp_client_timer lease_timer;
 };
 
+static int pico_dhcp_client_init(struct pico_dhcp_client_cookie *dhcpc);
+static int reset(struct pico_dhcp_client_cookie *dhcpc, uint8_t *buf);
+static int pico_dhcp_client_msg(struct pico_dhcp_client_cookie *dhcpc, uint8_t msg_type);
+static void pico_dhcp_client_wakeup(uint16_t ev, struct pico_socket *s);
+static void pico_dhcp_state_machine(uint8_t event, struct pico_dhcp_client_cookie *dhcpc, uint8_t *buf);
+
 static int dhcp_cookies_cmp(void *ka, void *kb)
 {
   struct pico_dhcp_client_cookie *a = ka, *b = kb;
-  if (a->xid < b->xid)
-    return -1; 
-  else if (a->xid > b->xid)
-    return 1;
-  else
+  if (a->xid == b->xid)
     return 0;
+  return (a->xid < b->xid) ? -1 : 1;
 } 
 PICO_TREE_DECLARE(DHCPCookies, dhcp_cookies_cmp);
 
-/*************************
- * function declarations *
- *************************/
-static void pico_dhcp_state_machine(int type, struct pico_dhcp_client_cookie* cli, uint8_t* data, int len);
-static void pico_dhcp_reinitiate_negotiation(unsigned long now, void *arg);
+static struct pico_dhcp_client_cookie *pico_dhcp_client_add_cookie(uint32_t xid, struct pico_device *dev, void (*cb)(void *dhcpc, int code), uint32_t *uid)
+{
+  struct pico_dhcp_client_cookie *dhcpc = NULL, *found = NULL, test = {0};
+  
+  test.xid = xid;
+  found = pico_tree_findKey(&DHCPCookies, &test);
+  if (found) {
+    pico_err = PICO_ERR_EAGAIN;
+    return NULL;
+  }
+
+  dhcpc = pico_zalloc(sizeof(struct pico_dhcp_client_cookie));
+  if (!dhcpc) {
+    pico_err = PICO_ERR_ENOMEM;
+    return NULL;
+  }
+
+  dhcpc->state = DHCP_CLIENT_STATE_INIT;
+  dhcpc->status = DHCP_CLIENT_STATUS_INIT;
+  dhcpc->xid = xid;
+  dhcpc->uid = uid;
+  *(dhcpc->uid) = 0;
+  dhcpc->cb = cb;
+  dhcpc->dev = dev;
+
+  pico_tree_insert(&DHCPCookies, dhcpc);
+  return dhcpc;
+}
+
+static int pico_dhcp_client_del_cookie(uint32_t xid)
+{
+  struct pico_dhcp_client_cookie test = {0}, *found = NULL;
+
+  test.xid = xid;
+  found = pico_tree_findKey(&DHCPCookies, &test);
+  if (!found)
+    return -1;
+
+  pico_socket_close(found->s);
+  pico_tree_delete(&DHCPCookies, found);
+  pico_free(found);
+  return 0;
+}
+
+static struct pico_dhcp_client_cookie *pico_dhcp_client_find_cookie(uint32_t xid)
+{
+  struct pico_dhcp_client_cookie test = {0}, *found = NULL;
+
+  test.xid = xid;
+  found = pico_tree_findKey(&DHCPCookies, &test);
+  if (found)
+    return found;
+  else
+    return NULL;
+}
+
+static void pico_dhcp_client_init_timer(unsigned long now, void *arg)
+{
+  struct pico_dhcp_client_cookie *dhcpc = (struct pico_dhcp_client_cookie *)arg;
+
+  if (dhcpc->init_timer.state == DHCP_CLIENT_TIMER_STOPPED)
+    return;
 
-//cb
-static void pico_dhcp_wakeup(uint16_t ev, struct pico_socket *s);
-static void dhcp_timer_cb(unsigned long tick, void* param);
+  if (++dhcpc->retry >= DHCP_CLIENT_RETRIES) {
+    pico_err = PICO_ERR_EAGAIN;
+    dhcpc->cb(dhcpc, PICO_DHCP_ERROR);
+    pico_dhcp_client_del_cookie(dhcpc->xid);
+    pico_dhcp_client_mutex++;
+    return;
+  }
+
+  /* init_timer is restarted in retransmit function, 
+   * otherwise an old init_timer would go on indefinitely */
+  dhcpc->event = PICO_DHCP_EVENT_RETRANSMIT;
+  pico_dhcp_state_machine(dhcpc->event, dhcpc, NULL);
+  return;
+}
+
+static void pico_dhcp_client_requesting_timer(unsigned long now, void *arg)
+{
+  struct pico_dhcp_client_cookie *dhcpc = (struct pico_dhcp_client_cookie *)arg;
+
+  if (dhcpc->requesting_timer.state == DHCP_CLIENT_TIMER_STOPPED)
+    return;
+
+  if (++dhcpc->retry > DHCP_CLIENT_RETRIES) {
+    pico_dhcp_client_mutex++;
+    reset(dhcpc, NULL);
+    return;
+  }
+
+  /* requesting_timer is restarted in retransmit function, 
+   * otherwise an old requesting_timer would go on indefinitely */
+  dhcpc->event = PICO_DHCP_EVENT_RETRANSMIT;
+  pico_dhcp_state_machine(dhcpc->event, dhcpc, NULL);
+  return;
+}
 
-//util
-static void pico_dhcp_retry(struct pico_dhcp_client_cookie *client);
-static int dhclient_send(struct pico_dhcp_client_cookie *cli, uint8_t msg_type);
-static int pico_dhcp_verify_and_identify_type(uint8_t* data, int len, struct pico_dhcp_client_cookie *cli);
-static int init_cookie(struct pico_dhcp_client_cookie* cli);
-static struct pico_dhcp_client_cookie* get_cookie_by_xid(uint32_t xid);
-static uint32_t get_xid(uint8_t* data);
+static void pico_dhcp_client_renewing_timer(unsigned long now, void *arg)
+{
+  struct pico_dhcp_client_cookie *dhcpc = (struct pico_dhcp_client_cookie *)arg;
+
+  if (dhcpc->renewing_timer.state == DHCP_CLIENT_TIMER_STOPPED)
+    return;
+
+  /* renewing_timer is restarted in retransmit function, 
+   * otherwise an old renewing_timer would go on indefinitely */
+  dhcpc->retry++;
+  dhcpc->event = PICO_DHCP_EVENT_RETRANSMIT;
+  pico_dhcp_state_machine(dhcpc->event, dhcpc, NULL);
+  return;
+}
+
+static void pico_dhcp_client_rebinding_timer(unsigned long now, void *arg)
+{
+  struct pico_dhcp_client_cookie *dhcpc = (struct pico_dhcp_client_cookie *)arg;
+
+  if (dhcpc->rebinding_timer.state == DHCP_CLIENT_TIMER_STOPPED)
+    return;
+
+  /* rebinding_timer is restarted in retransmit function, 
+   * otherwise an old rebinding_timer would go on indefinitely */
+  dhcpc->retry++;
+  dhcpc->event = PICO_DHCP_EVENT_RETRANSMIT;
+  pico_dhcp_state_machine(dhcpc->event, dhcpc, NULL);
+  return;
+}
+
+static void pico_dhcp_client_T1_timer(unsigned long now, void *arg)
+{
+  struct pico_dhcp_client_cookie *dhcpc = (struct pico_dhcp_client_cookie *)arg;
+
+  if (dhcpc->T1_timer.state == DHCP_CLIENT_TIMER_STOPPED)
+    return;
 
-//fsm functions
-static int recv_offer(struct pico_dhcp_client_cookie *cli, uint8_t *data, int len);
-static int recv_ack(struct pico_dhcp_client_cookie *cli, uint8_t *data, int len);
-static int renew(struct pico_dhcp_client_cookie *cli, uint8_t *data, int len);
-static int reset(struct pico_dhcp_client_cookie *cli, uint8_t *data, int len);
-static int retransmit(struct pico_dhcp_client_cookie *cli, uint8_t *data, int len);
+  /* T1 state is set to stopped in renew function, 
+   * otherwise an old T1 could stop a valid T1 */
+  dhcpc->event = PICO_DHCP_EVENT_T1;
+  pico_dhcp_state_machine(dhcpc->event, dhcpc, NULL);
+  return;
+}
+
+static void pico_dhcp_client_T2_timer(unsigned long now, void *arg)
+{
+  struct pico_dhcp_client_cookie *dhcpc = (struct pico_dhcp_client_cookie *)arg;
+
+  if (dhcpc->T2_timer.state == DHCP_CLIENT_TIMER_STOPPED)
+    return;
+
+  /* T2 state is set to stopped in rebind function, 
+   * otherwise an old T2 could stop a valid T2. 
+   * Likewise for renewing_timer */
+  dhcpc->event = PICO_DHCP_EVENT_T2;
+  pico_dhcp_state_machine(dhcpc->event, dhcpc, NULL);
+  return;
+}
+
+static void pico_dhcp_client_lease_timer(unsigned long now, void *arg)
+{
+  struct pico_dhcp_client_cookie *dhcpc = (struct pico_dhcp_client_cookie *)arg;
+
+  if (dhcpc->lease_timer.state == DHCP_CLIENT_TIMER_STOPPED)
+    return;
+
+  /* lease state is set to stopped in reset function, 
+   * otherwise an old lease could stop a valid lease. 
+   * Likewise for rebinding_timer */
+  dhcpc->event = PICO_DHCP_EVENT_LEASE;
+  pico_dhcp_state_machine(dhcpc->event, dhcpc, NULL);
+  return;
+}
+
+static void pico_dhcp_client_reinit(unsigned long now, void *arg)
+{
+  struct pico_dhcp_client_cookie *dhcpc = (struct pico_dhcp_client_cookie *)arg;
+
+  if (++dhcpc->retry > DHCP_CLIENT_RETRIES) {
+    pico_err = PICO_ERR_EAGAIN;
+    dhcpc->cb(dhcpc, PICO_DHCP_ERROR);
+    pico_dhcp_client_del_cookie(dhcpc->xid);
+    return;
+  }
+  pico_dhcp_client_init(dhcpc);
+  return;
+}
+
+static void pico_dhcp_client_stop_timers(struct pico_dhcp_client_cookie *dhcpc)
+{
+  dhcpc->retry = 0;
+  dhcpc->init_timer.state = DHCP_CLIENT_TIMER_STOPPED;
+  dhcpc->requesting_timer.state = DHCP_CLIENT_TIMER_STOPPED;
+  dhcpc->renewing_timer.state = DHCP_CLIENT_TIMER_STOPPED;
+  dhcpc->rebinding_timer.state = DHCP_CLIENT_TIMER_STOPPED;
+  dhcpc->T1_timer.state = DHCP_CLIENT_TIMER_STOPPED;
+  dhcpc->T2_timer.state = DHCP_CLIENT_TIMER_STOPPED;
+  dhcpc->lease_timer.state = DHCP_CLIENT_TIMER_STOPPED;
+
+  return;
+}
+
+static void pico_dhcp_client_start_init_timer(struct pico_dhcp_client_cookie *dhcpc)
+{
+  uint32_t time = 0;
 
-//fsm implementation
-static void pico_dhcp_state_machine(int type, struct pico_dhcp_client_cookie* cli, uint8_t* data, int len);
+  /* timer value is doubled with every retry (exponential backoff) */
+  dhcpc->init_timer.state = DHCP_CLIENT_TIMER_STARTED;
+  dhcpc->init_timer.time = DHCP_CLIENT_RETRANS;
+  time = dhcpc->init_timer.time << dhcpc->retry;
+  pico_timer_add(time * 1000, pico_dhcp_client_init_timer, dhcpc);
+
+  return;
+}
+
+static void pico_dhcp_client_start_requesting_timer(struct pico_dhcp_client_cookie *dhcpc)
+{
+  uint32_t time = 0;
+
+  /* timer value is doubled with every retry (exponential backoff) */
+  dhcpc->init_timer.state = DHCP_CLIENT_TIMER_STOPPED;
+  dhcpc->requesting_timer.state = DHCP_CLIENT_TIMER_STARTED;
+  dhcpc->requesting_timer.time = DHCP_CLIENT_RETRANS;
+  time = dhcpc->requesting_timer.time << dhcpc->retry;
+  pico_timer_add(time * 1000, pico_dhcp_client_requesting_timer, dhcpc);
+
+  return;
+}
+
+static void pico_dhcp_client_start_renewing_timer(struct pico_dhcp_client_cookie *dhcpc)
+{
+  uint32_t halftime = 0;
+
+  /* wait one-half of the remaining time until T2, down to a minimum of 60 seconds */
+  /* (dhcpc->retry + 1): initial -> divide by 2, 1st retry -> divide by 4, 2nd retry -> divide by 8, etc */
+  dhcpc->T1_timer.state = DHCP_CLIENT_TIMER_STOPPED;
+  dhcpc->renewing_timer.state = DHCP_CLIENT_TIMER_STARTED;
+  halftime = dhcpc->renewing_timer.time >> (dhcpc->retry + 1);
+  if (halftime < 60)
+    halftime = 60;
+  pico_timer_add(halftime * 1000, pico_dhcp_client_renewing_timer, dhcpc);
+
+  return;
+}
 
-/***************
- * entry point *
- ***************/
+static void pico_dhcp_client_start_rebinding_timer(struct pico_dhcp_client_cookie *dhcpc)
+{
+  uint32_t halftime = 0;
+
+  /* wait one-half of the remaining time until T2, down to a minimum of 60 seconds */
+  /* (dhcpc->retry + 1): initial -> divide by 2, 1st retry -> divide by 4, 2nd retry -> divide by 8, etc */
+  dhcpc->T2_timer.state = DHCP_CLIENT_TIMER_STOPPED;
+  dhcpc->renewing_timer.state = DHCP_CLIENT_TIMER_STOPPED;
+  dhcpc->rebinding_timer.state = DHCP_CLIENT_TIMER_STARTED;
+  halftime = dhcpc->rebinding_timer.time >> (dhcpc->retry + 1);
+  if (halftime < 60)
+    halftime = 60;
+  pico_timer_add(halftime * 1000, pico_dhcp_client_rebinding_timer, dhcpc);
+
+  return;
+}
 
-static uint32_t pico_dhcp_execute_init(struct pico_dhcp_client_cookie *cli)
+static void pico_dhcp_client_start_reacquisition_timers(struct pico_dhcp_client_cookie *dhcpc)
 {
-  if (!dhcp_client_mutex) {
-    pico_timer_add(3000, pico_dhcp_reinitiate_negotiation, cli);
+  dhcpc->requesting_timer.state = DHCP_CLIENT_TIMER_STOPPED;
+  dhcpc->T1_timer.state = DHCP_CLIENT_TIMER_STARTED;
+  dhcpc->T2_timer.state = DHCP_CLIENT_TIMER_STARTED;
+  dhcpc->lease_timer.state = DHCP_CLIENT_TIMER_STARTED;
+  pico_timer_add(dhcpc->T1_timer.time * 1000, pico_dhcp_client_T1_timer, dhcpc);
+  pico_timer_add(dhcpc->T2_timer.time * 1000, pico_dhcp_client_T2_timer, dhcpc);
+  pico_timer_add(dhcpc->lease_timer.time * 1000, pico_dhcp_client_lease_timer, dhcpc);
+
+  return;
+}
+
+static int pico_dhcp_client_init(struct pico_dhcp_client_cookie *dhcpc)
+{
+  uint16_t port = PICO_DHCP_CLIENT_PORT;
+  struct pico_ip4 inaddr_any = {0}, netmask = {0};
+
+  /* serialize client negotations if multiple devices */
+  /* NOTE: ONLY initialization is serialized! */
+  if (!pico_dhcp_client_mutex) {
+    pico_timer_add(DHCP_CLIENT_REINIT, pico_dhcp_client_reinit, dhcpc);
     return 0;
   }
-  dhcp_client_mutex--;
+  pico_dhcp_client_mutex--;
+
+  switch (dhcpc->status)
+  {
+    case DHCP_CLIENT_STATUS_INIT:
+      dhcpc->s = pico_socket_open(PICO_PROTO_IPV4, PICO_PROTO_UDP, &pico_dhcp_client_wakeup);
+      if (!dhcpc->s) {
+        pico_dhcp_client_mutex++;
+        pico_timer_add(DHCP_CLIENT_REINIT, pico_dhcp_client_reinit, dhcpc);
+        break;
+      }
+      dhcpc->s->dev = dhcpc->dev;
+      dhcpc->status = DHCP_CLIENT_STATUS_SOCKET;
+      /* fallthrough */
+
+    case DHCP_CLIENT_STATUS_SOCKET:
+      if (pico_socket_bind(dhcpc->s, &inaddr_any, &port) < 0) {
+        pico_dhcp_client_mutex++;
+        pico_timer_add(DHCP_CLIENT_REINIT, pico_dhcp_client_reinit, dhcpc);
+        break;
+      }
+      dhcpc->status = DHCP_CLIENT_STATUS_BOUND;
+      /* fallthrough */
+
+    case DHCP_CLIENT_STATUS_BOUND:
+      /* adding a link with address 0.0.0.0 and netmask 0.0.0.0, 
+       * automatically adds a route for a global broadcast */
+      if (pico_ipv4_link_add(dhcpc->dev, inaddr_any, netmask) < 0) {
+        pico_dhcp_client_mutex++;
+        pico_timer_add(DHCP_CLIENT_REINIT, pico_dhcp_client_reinit, dhcpc);
+        break;
+      }
+      dhcpc->status = DHCP_CLIENT_STATUS_LINKED;
+      /* fallthrough */
+
+    case DHCP_CLIENT_STATUS_LINKED:
+      if (pico_dhcp_client_msg(dhcpc, PICO_DHCP_MSG_DISCOVER) < 0) {
+        pico_dhcp_client_mutex++;
+        pico_timer_add(DHCP_CLIENT_REINIT, pico_dhcp_client_reinit, dhcpc);
+        break;
+      }
+      dhcpc->status = DHCP_CLIENT_STATUS_TRANSMITTED;
+      /* fallthrough */
+
+    case DHCP_CLIENT_STATUS_TRANSMITTED:
+      dhcpc->retry = 0;
+      dhcpc->init_timestamp = PICO_TIME_MS();
+      pico_dhcp_client_start_init_timer(dhcpc);
+      break;
+
+    default:
+      return -1;
+  }
+  return 0;
+}
+
+int pico_dhcp_initiate_negotiation(struct pico_device *dev, void (*cb)(void *dhcpc, int code), uint32_t *uid)
+{
+  uint8_t retry = 32;
+  uint32_t xid = 0;
+  struct pico_dhcp_client_cookie *dhcpc = NULL;
+
+  if (!dev || !cb || !uid) {
+    pico_err = PICO_ERR_EINVAL;
+    return -1;
+  }
+  if (!dev->eth) {
+    pico_err = PICO_ERR_EOPNOTSUPP;
+    return -1;
+  }
+
+  /* attempt to generate a correct xid, else fail */
+  do {
+    xid = pico_rand();
+  } while (!xid && --retry);
+
+  if (!xid) {
+    pico_err = PICO_ERR_EAGAIN;
+    return -1;
+  }
+
+  dhcpc = pico_dhcp_client_add_cookie(xid, dev, cb, uid);
+  if (!dhcpc)
+    return -1;
+
+  dhcpc_dbg("DHCP client: cookie with xid %u\n", dhcpc->xid);
+  return pico_dhcp_client_init(dhcpc);
+}
 
-  if (init_cookie(cli) < 0)
+static void pico_dhcp_client_recv_params(struct pico_dhcp_client_cookie *dhcpc, struct pico_dhcp_opt *opt)
+{
+  do {
+    switch (opt->code)
+    {
+      case PICO_DHCP_OPT_PAD:
+        break;
+
+      case PICO_DHCP_OPT_END:
+        break;
+
+      case PICO_DHCP_OPT_MSGTYPE:
+        dhcpc->event = opt->ext.msg_type.type;
+        dhcpc_dbg("DHCP client: message type %u\n", dhcpc->event);
+        break;
+
+      case PICO_DHCP_OPT_LEASETIME:
+        dhcpc->lease_timer.time = long_be(opt->ext.lease_time.time);
+        dhcpc_dbg("DHCP client: lease time %u\n", dhcpc->lease_timer.time);
+        break;
+
+      case PICO_DHCP_OPT_RENEWALTIME:
+        dhcpc->T1_timer.time = long_be(opt->ext.renewal_time.time);
+        dhcpc_dbg("DHCP client: renewal time %u\n", dhcpc->T1_timer.time);
+        break;
+
+      case PICO_DHCP_OPT_REBINDINGTIME:
+        dhcpc->T2_timer.time = long_be(opt->ext.rebinding_time.time);
+        dhcpc_dbg("DHCP client: rebinding time %u\n", dhcpc->T2_timer.time);
+        break;
+
+      case PICO_DHCP_OPT_ROUTER:
+        dhcpc->gateway = opt->ext.router.ip;
+        dhcpc_dbg("DHCP client: router %08X\n", dhcpc->gateway.addr);
+        break;
+
+      case PICO_DHCP_OPT_DNS:
+        dhcpc->nameserver = opt->ext.dns.ip;
+        dhcpc_dbg("DHCP client: dns %08X\n", dhcpc->nameserver.addr);
+        break;
+
+      case PICO_DHCP_OPT_NETMASK:
+        dhcpc->netmask = opt->ext.netmask.ip;
+        dhcpc_dbg("DHCP client: netmask %08X\n", dhcpc->netmask.addr);
+        break;
+
+      case PICO_DHCP_OPT_SERVERID:
+        dhcpc->server_id = opt->ext.server_id.ip;
+        dhcpc_dbg("DHCP client: server ID %08X\n", dhcpc->server_id.addr);
+        break;
+
+      case PICO_DHCP_OPT_OPTOVERLOAD:
+        dhcpc_dbg("DHCP client: WARNING option overload present (not processed)");
+        break;
+
+      default:
+        dhcpc_dbg("DHCP client: WARNING unsupported option %u\n", opt->code);
+        break;
+    }
+  } while (pico_dhcp_next_option(&opt));
+
+  /* default values for T1 and T2 when not provided */
+  if (!dhcpc->T1_timer.time)
+    dhcpc->T1_timer.time = dhcpc->lease_timer.time >> 1;
+  if (!dhcpc->T2_timer.time)
+    dhcpc->T2_timer.time = (dhcpc->lease_timer.time * 875) / 1000;
+
+  return;
+}
+
+static int recv_offer(struct pico_dhcp_client_cookie *dhcpc, uint8_t *buf)
+{
+  struct pico_dhcp_hdr *hdr = (struct pico_dhcp_hdr *)buf;
+  struct pico_dhcp_opt *opt = (struct pico_dhcp_opt *)hdr->options;
+
+  pico_dhcp_client_recv_params(dhcpc, opt);
+  if ((dhcpc->event != PICO_DHCP_MSG_OFFER) || !dhcpc->server_id.addr || !dhcpc->netmask.addr || !dhcpc->lease_timer.time)
+    return -1;
+  dhcpc->address.addr = hdr->yiaddr;
+
+  /* we skip state SELECTING, process first offer received */
+  dhcpc->state = DHCP_CLIENT_STATE_REQUESTING;
+  dhcpc->retry = 0;
+  pico_dhcp_client_msg(dhcpc, PICO_DHCP_MSG_REQUEST);
+  pico_dhcp_client_start_requesting_timer(dhcpc);
+  return 0;
+}
+
+static int recv_ack(struct pico_dhcp_client_cookie *dhcpc, uint8_t *buf)
+{
+  struct pico_dhcp_hdr *hdr = (struct pico_dhcp_hdr *)buf;
+  struct pico_dhcp_opt *opt = (struct pico_dhcp_opt *)hdr->options;
+  struct pico_ip4 address = {0}, netmask = {0}, inaddr_any = {0}, bcast = { .addr = 0xFFFFFFFF };
+
+  pico_dhcp_client_recv_params(dhcpc, opt);
+  if ((dhcpc->event != PICO_DHCP_MSG_ACK) || !dhcpc->server_id.addr || !dhcpc->netmask.addr || !dhcpc->lease_timer.time)
     return -1;
 
-  dbg("DHCPC: cookie with xid %u\n", cli->xid);
-  
-  if (pico_tree_insert(&DHCPCookies, cli)) {
-    pico_err = PICO_ERR_EAGAIN;
-    if(cli->cb != NULL) {
-      cli->cb(cli, PICO_DHCP_ERROR);
-    }
-    pico_free(cli);
-    return -1; /* Element key already exists */
+  /* close the socket used for address (re)acquisition */
+  pico_socket_close(dhcpc->s);
+  /* delete the link with address 0.0.0.0, add new link with acquired address */
+  if (dhcpc->status == DHCP_CLIENT_STATUS_TRANSMITTED) {
+    pico_ipv4_link_del(dhcpc->dev, address);
+    pico_ipv4_link_add(dhcpc->dev, dhcpc->address, dhcpc->netmask);
+    dhcpc->status = DHCP_CLIENT_STATUS_INITIALIZED;
+  }
+  /* delete the default route for our global broadcast messages, otherwise another interface can not rebind */
+  if (dhcpc->state == DHCP_CLIENT_STATE_REBINDING)
+    pico_ipv4_route_del(bcast, netmask, inaddr_any, 1, pico_ipv4_link_get(&dhcpc->address));
+
+  dbg("DHCP client: renewal time (T1) %u\n", dhcpc->T1_timer.time);
+  dbg("DHCP client: rebinding time (T2) %u\n", dhcpc->T2_timer.time);
+  dbg("DHCP client: lease time %u\n", dhcpc->lease_timer.time);
+
+  dhcpc->retry = 0;
+  dhcpc->renewing_timer.time = dhcpc->T2_timer.time - dhcpc->T1_timer.time;
+  dhcpc->rebinding_timer.time = dhcpc->lease_timer.time - dhcpc->T2_timer.time;
+  pico_dhcp_client_start_reacquisition_timers(dhcpc);
+
+  pico_dhcp_client_mutex++;
+  *(dhcpc->uid) = dhcpc->xid;
+  dhcpc->cb(dhcpc, PICO_DHCP_SUCCESS);
+  dhcpc->state = DHCP_CLIENT_STATE_BOUND;
+  return 0;
+}
+
+static int renew(struct pico_dhcp_client_cookie *dhcpc, uint8_t *buf)
+{
+  uint16_t port = PICO_DHCP_CLIENT_PORT;
+
+  dhcpc->state = DHCP_CLIENT_STATE_RENEWING;
+  dhcpc->s = pico_socket_open(PICO_PROTO_IPV4, PICO_PROTO_UDP, &pico_dhcp_client_wakeup);
+  if (!dhcpc->s) {
+    dhcpc_dbg("DHCP client ERROR: failure opening socket on renew, aborting DHCP! (%s)\n", strerror(pico_err));
+    dhcpc->cb(dhcpc, PICO_DHCP_ERROR);
+    return -1;
+  }
+  if (pico_socket_bind(dhcpc->s, &dhcpc->address, &port) != 0) {
+    dhcpc_dbg("DHCP client ERROR: failure binding socket on renew, aborting DHCP! (%s)\n", strerror(pico_err));
+    pico_socket_close(dhcpc->s);
+    dhcpc->cb(dhcpc, PICO_DHCP_ERROR);
+    return -1;
   }
 
-  if (dhclient_send(cli, PICO_DHCP_MSG_DISCOVER) < 0)
+  dhcpc->retry = 0;
+  pico_dhcp_client_msg(dhcpc, PICO_DHCP_MSG_REQUEST);
+  pico_dhcp_client_start_renewing_timer(dhcpc);
+
+  return 0;
+}
+
+static int rebind(struct pico_dhcp_client_cookie *dhcpc, uint8_t *buf)
+{
+  struct pico_ip4 bcast = { .addr = 0xFFFFFFFF }, netmask = {0}, inaddr_any = {0};
+
+  dhcpc->state = DHCP_CLIENT_STATE_REBINDING;
+  dhcpc->retry = 0;
+  /* we need a default route for our global broadcast messages, otherwise they get dropped. */
+  pico_ipv4_route_add(bcast, netmask, inaddr_any, 1, pico_ipv4_link_get(&dhcpc->address));
+  pico_dhcp_client_msg(dhcpc, PICO_DHCP_MSG_REQUEST);
+  pico_dhcp_client_start_rebinding_timer(dhcpc);
+
+  return 0;
+}
+
+static int reset(struct pico_dhcp_client_cookie *dhcpc, uint8_t *buf)
+{
+  struct pico_ip4 address = {0}, netmask = {0}, inaddr_any = {0}, bcast = { .addr = 0xFFFFFFFF };
+
+  if (dhcpc->state == DHCP_CLIENT_STATE_REQUESTING)
+    address.addr = PICO_IP4_ANY;
+  else
+    address.addr = dhcpc->address.addr;
+
+  /* delete the default route for our global broadcast messages, otherwise another interface can not rebind */
+  if (dhcpc->state == DHCP_CLIENT_STATE_REBINDING)
+    pico_ipv4_route_del(bcast, netmask, inaddr_any, 1, pico_ipv4_link_get(&dhcpc->address));
+
+  /* close the socket used for address (re)acquisition */
+  pico_socket_close(dhcpc->s);
+  /* delete the link with the currently in use address */
+  pico_ipv4_link_del(dhcpc->dev, address);
+
+  dhcpc->cb(dhcpc, PICO_DHCP_RESET);
+  dhcpc->state = DHCP_CLIENT_STATE_INIT;
+  dhcpc->status = DHCP_CLIENT_STATUS_INIT;
+  pico_dhcp_client_stop_timers(dhcpc);
+  pico_dhcp_client_init(dhcpc);
+  return 0;
+}
+
+static int retransmit(struct pico_dhcp_client_cookie *dhcpc, uint8_t *buf)
+{
+  switch (dhcpc->state)
+  {
+    case DHCP_CLIENT_STATE_INIT:
+      pico_dhcp_client_msg(dhcpc, PICO_DHCP_MSG_DISCOVER);
+      pico_dhcp_client_start_init_timer(dhcpc);
+      break;
+
+    case DHCP_CLIENT_STATE_REQUESTING:
+      pico_dhcp_client_msg(dhcpc, PICO_DHCP_MSG_REQUEST);
+      pico_dhcp_client_start_requesting_timer(dhcpc);
+      break;
+
+    case DHCP_CLIENT_STATE_RENEWING:
+      pico_dhcp_client_msg(dhcpc, PICO_DHCP_MSG_REQUEST);
+      pico_dhcp_client_start_renewing_timer(dhcpc);
+      break;
+
+    case DHCP_CLIENT_STATE_REBINDING:
+      pico_dhcp_client_msg(dhcpc, PICO_DHCP_MSG_DISCOVER);
+      pico_dhcp_client_start_rebinding_timer(dhcpc);
+      break;
+
+    default:
+      dhcpc_dbg("DHCP client WARNING: retransmit in incorrect state (%u)!\n", dhcpc->state);
+      return -1;
+  }
+  return 0;
+}
+
+struct dhcp_action_entry {
+  int (*offer)(struct pico_dhcp_client_cookie *dhcpc, uint8_t *buf);
+  int (*ack)(struct pico_dhcp_client_cookie *dhcpc, uint8_t *buf);
+  int (*nak)(struct pico_dhcp_client_cookie *dhcpc, uint8_t *buf);
+  int (*timer1)(struct pico_dhcp_client_cookie *dhcpc, uint8_t *buf);
+  int (*timer2)(struct pico_dhcp_client_cookie *dhcpc, uint8_t *buf);
+  int (*timer_lease)(struct pico_dhcp_client_cookie *dhcpc, uint8_t *buf);
+  int (*timer_retransmit)(struct pico_dhcp_client_cookie *dhcpc, uint8_t *buf);
+};
+
+static struct dhcp_action_entry dhcp_fsm[] = 
+{ /* event                |offer      |ack      |nak    |T1    |T2     |lease  |retransmit */
+/* state init-reboot */ { NULL,       NULL,     NULL,   NULL,  NULL,   NULL,  NULL       },
+/* state rebooting   */ { NULL,       NULL,     NULL,   NULL,  NULL,   NULL,  NULL       },
+/* state init        */ { recv_offer, NULL,     NULL,   NULL,  NULL,   NULL,  retransmit },
+/* state selecting   */ { NULL,       NULL,     NULL,   NULL,  NULL,   NULL,  NULL       },
+/* state requesting  */ { NULL,       recv_ack, reset,  NULL,  NULL,   NULL,  retransmit },
+/* state bound       */ { NULL,       NULL,     NULL,   renew, NULL,   NULL,  NULL       },
+/* state renewing    */ { NULL,       recv_ack, reset,  NULL,  rebind, NULL,  retransmit },
+/* state rebinding   */ { NULL,       recv_ack, reset,  NULL,  NULL,   reset, retransmit },
+};
+
+/* TIMERS REMARK:
+ * In state bound we have T1, T2 and the lease timer running. If T1 goes off, we attempt to renew.
+ * If the renew succeeds a new T1, T2 and lease timer is started. The former T2 and lease timer is
+ * still running though. This poses no concerns as the T2 and lease event in state bound have a NULL 
+ * pointer in the fsm. If the former T2 or lease timer goes off, nothing happens. Same situation 
+ * applies for T2 and a succesfull rebind. */
+
+static void pico_dhcp_state_machine(uint8_t event, struct pico_dhcp_client_cookie *dhcpc, uint8_t *buf)
+{
+  switch (event) 
+  {
+    case PICO_DHCP_MSG_OFFER:
+      dhcpc_dbg("DHCP client: received OFFER\n");
+      if (dhcp_fsm[dhcpc->state].offer)
+        dhcp_fsm[dhcpc->state].offer(dhcpc, buf);
+      break;
+
+    case PICO_DHCP_MSG_ACK:
+      dhcpc_dbg("DHCP client: received ACK\n");
+      if (dhcp_fsm[dhcpc->state].ack)
+        dhcp_fsm[dhcpc->state].ack(dhcpc, buf);
+      break;
+
+    case PICO_DHCP_MSG_NAK:
+      dhcpc_dbg("DHCP client: received NAK\n");
+      if (dhcp_fsm[dhcpc->state].nak)
+        dhcp_fsm[dhcpc->state].nak(dhcpc, buf);
+      break;
+
+    case PICO_DHCP_EVENT_T1:
+      dhcpc_dbg("DHCP client: received T1 timeout\n");
+      if (dhcp_fsm[dhcpc->state].timer1)
+        dhcp_fsm[dhcpc->state].timer1(dhcpc, NULL);
+      break;
+
+    case PICO_DHCP_EVENT_T2:
+      dhcpc_dbg("DHCP client: received T2 timeout\n");
+      if (dhcp_fsm[dhcpc->state].timer2)
+        dhcp_fsm[dhcpc->state].timer2(dhcpc, NULL);
+      break;
+
+    case PICO_DHCP_EVENT_LEASE:
+      dhcpc_dbg("DHCP client: received LEASE timeout\n");
+      if (dhcp_fsm[dhcpc->state].timer_lease)
+        dhcp_fsm[dhcpc->state].timer_lease(dhcpc, NULL);
+      break;
+
+    case PICO_DHCP_EVENT_RETRANSMIT:
+      dhcpc_dbg("DHCP client: received RETRANSMIT timeout\n");
+      if (dhcp_fsm[dhcpc->state].timer_retransmit)
+        dhcp_fsm[dhcpc->state].timer_retransmit(dhcpc, NULL);
+      break;
+
+    default:
+      dhcpc_dbg("DHCP client WARNING: unrecognized event (%u)!\n", dhcpc->event);
+      return;
+  }
+  return;
+}
+
+static int pico_dhcp_client_opt_parse(void *ptr, int len)
+{
+  int optlen = len - sizeof(struct pico_dhcp_hdr);
+  struct pico_dhcp_hdr *hdr = (struct pico_dhcp_hdr *)ptr;
+  struct pico_dhcp_opt *opt = NULL;
+
+  if (hdr->dhcp_magic != PICO_DHCPD_MAGIC_COOKIE)
+    return -1;
+  if (!pico_dhcp_are_options_valid(hdr->options, optlen))
+    return -1;
+
+  opt = (struct pico_dhcp_opt *)hdr->options;
+  do {
+    if (opt->code == PICO_DHCP_OPT_MSGTYPE)
+      return opt->ext.msg_type.type;
+  } while (pico_dhcp_next_option(&opt));
+
+  return -1;
+}
+
+static int pico_dhcp_client_msg(struct pico_dhcp_client_cookie *dhcpc, uint8_t msg_type)
+{
+  int r = 0, optlen = 0, offset = 0;
+  struct pico_ip4 destination = { .addr = 0xFFFFFFFF};
+  struct pico_dhcp_hdr *hdr = NULL;
+
+  switch (msg_type)
+  {
+    case PICO_DHCP_MSG_DISCOVER:
+      dhcpc_dbg("DHCP client: sent DHCPDISCOVER\n");
+      optlen = PICO_DHCP_OPTLEN_MSGTYPE + PICO_DHCP_OPTLEN_MAXMSGSIZE + PICO_DHCP_OPTLEN_PARAMLIST + PICO_DHCP_OPTLEN_END;
+      hdr = pico_zalloc(sizeof(struct pico_dhcp_hdr) + optlen);
+      /* specific options */
+      offset += pico_dhcp_opt_maxmsgsize(&hdr->options[offset], DHCP_CLIENT_MAXMSGZISE);
+      break;
+
+    case PICO_DHCP_MSG_REQUEST:
+      dhcpc_dbg("DHCP client: sent DHCPREQUEST\n");
+      optlen = PICO_DHCP_OPTLEN_MSGTYPE + PICO_DHCP_OPTLEN_MAXMSGSIZE + PICO_DHCP_OPTLEN_PARAMLIST + PICO_DHCP_OPTLEN_REQIP + PICO_DHCP_OPTLEN_SERVERID 
+              + PICO_DHCP_OPTLEN_END;
+      hdr = pico_zalloc(sizeof(struct pico_dhcp_hdr) + optlen);
+      /* specific options */
+      offset += pico_dhcp_opt_maxmsgsize(&hdr->options[offset], DHCP_CLIENT_MAXMSGZISE);
+      if (dhcpc->state == DHCP_CLIENT_STATE_REQUESTING) {
+        offset += pico_dhcp_opt_reqip(&hdr->options[offset], &dhcpc->address);
+        offset += pico_dhcp_opt_serverid(&hdr->options[offset], &dhcpc->server_id);
+      }
+      break;
+
+    default:
+      return -1;
+  }
+
+  /* common options */
+  offset += pico_dhcp_opt_msgtype(&hdr->options[offset], msg_type);
+  offset += pico_dhcp_opt_paramlist(&hdr->options[offset]);
+  offset += pico_dhcp_opt_end(&hdr->options[offset]);
+
+  switch (dhcpc->state)
+  {
+    case DHCP_CLIENT_STATE_BOUND:
+      destination.addr = dhcpc->server_id.addr;
+      hdr->ciaddr = dhcpc->address.addr;
+      break;
+
+    case DHCP_CLIENT_STATE_RENEWING:
+      destination.addr = dhcpc->server_id.addr;
+      hdr->ciaddr = dhcpc->address.addr;
+      break;
+
+    case DHCP_CLIENT_STATE_REBINDING:
+      hdr->ciaddr = dhcpc->address.addr;
+      break;
+
+    default:
+      /* do nothing */
+      break;
+  }
+
+  /* header information */
+  hdr->op = PICO_DHCP_OP_REQUEST;
+  hdr->htype = PICO_DHCP_HTYPE_ETH;
+  hdr->hlen = PICO_SIZE_ETH;
+  hdr->xid = dhcpc->xid;
+  hdr->flags = short_be(PICO_DHCP_FLAG_BROADCAST);
+  hdr->dhcp_magic = PICO_DHCPD_MAGIC_COOKIE;
+  /* copy client hardware address */
+  memcpy(hdr->hwaddr, &dhcpc->dev->eth->mac, PICO_SIZE_ETH);
+
+  r = pico_socket_sendto(dhcpc->s, hdr, sizeof(struct pico_dhcp_hdr) + optlen, &destination, PICO_DHCPD_PORT);
+  pico_free(hdr);
+  if (r < 0)
     return -1;
 
   return 0;
 }
 
-/* returns a pointer to the client cookie. The user should pass this pointer every time he calls a dhcp-function. This is so that we can (one day) support dhcp on multiple interfaces */
-int pico_dhcp_initiate_negotiation(struct pico_device *device, void (*callback)(void *cli, int code), uint32_t *xid)
-{
-  struct pico_dhcp_client_cookie *cli;
-  
-  if(!device || !callback || !xid){
-    pico_err = PICO_ERR_EINVAL;
-    return -1;
-  }
-  cli = pico_zalloc(sizeof(struct pico_dhcp_client_cookie));
-  if(!cli){
-    pico_err = PICO_ERR_ENOMEM;
-    return -1;
-  }
-
-  cli->device = device;
-  cli->cb = callback;
-  cli->xid_user = xid;
-  *(cli->xid_user) = 0;
-
-  return pico_dhcp_execute_init(cli);
-}
-
-static void pico_dhcp_reinitiate_negotiation(unsigned long now, void *arg)
-{
-  struct pico_dhcp_client_cookie *cli = (struct pico_dhcp_client_cookie *) arg;
-
-  pico_dhcp_execute_init(cli);
-
-  return;
-}
-
-/********************
- * access functions *
- ********************/
-
-struct pico_ip4 pico_dhcp_get_address(void* cli)
-{
-  return ((struct pico_dhcp_client_cookie*)cli)->address;
-}
-
-struct pico_ip4 pico_dhcp_get_gateway(void* cli)
-{
-  return ((struct pico_dhcp_client_cookie*)cli)->gateway;
-}
-
-struct pico_ip4 pico_dhcp_get_nameserver(void* cli)
-{
-  return ((struct pico_dhcp_client_cookie*)cli)->nameserver;
-}
-
-/*************
- * callbacks *
- *************/
-
-static void pico_dhcp_wakeup(uint16_t ev, struct pico_socket *s)
-{
-  uint8_t buf[DHCPC_DATAGRAM_SIZE];
-  int r=0;
-  uint32_t peer;
-  uint16_t port;
-  int type;
-
-  struct pico_dhcp_client_cookie *cli;
-  dbg("DHCPC: called dhcp_wakeup\n");
-  if (ev == PICO_SOCK_EV_RD) {
-    do {
-      r = pico_socket_recvfrom(s, buf, DHCPC_DATAGRAM_SIZE, &peer, &port);
-      cli = get_cookie_by_xid(get_xid(buf));
-      if(cli == NULL)
-        return;
-      if (r > 0 && port == PICO_DHCPD_PORT) {
-        type = pico_dhcp_verify_and_identify_type(buf, r, cli);
-        pico_dhcp_state_machine(type, cli, buf, r);
-      }
-    } while(r>0);
-  }
-}
-
-static void dhcp_timer_cb(unsigned long tick, void* param)
-{
-  struct dhcp_timer_param* param2 = (struct dhcp_timer_param*) param;
-  if(param2->valid == 1){
-    //dbg("called timer cb on active timer type %d\n",param2->type);
-    pico_dhcp_state_machine(param2->type, param2->cli, NULL, 0);
-  }
-  if(param2->cli->timer_param_1 == param){
-    param2->cli->timer_param_1 = NULL;
-  }
-  if(param2->cli->timer_param_2 == param){
-    param2->cli->timer_param_2 = NULL;
-  }
-  if(param2->cli->timer_param_lease == param){
-    param2->cli->timer_param_lease = NULL;
-  }
-  if(param2->cli->timer_param_retransmit == param){
-    param2->cli->timer_param_retransmit = NULL;
-  }
-
-  pico_free(param);
-
-}
-/*****************
- * fsm functions *
- *****************/
-
-static int recv_offer(struct pico_dhcp_client_cookie *cli, uint8_t *data, int len)
+static void pico_dhcp_client_wakeup(uint16_t ev, struct pico_socket *s)
 {
-  struct pico_dhcphdr *dhdr = (struct pico_dhcphdr *) data;
-  uint8_t *nextopt, opt_data[20], opt_type;
-  int opt_len = 20;
-  uint8_t msg_type = 0xFF;
-  int T1_set = 0;
-  int T2_set = 0;
-
-  cli->address.addr = dhdr->yiaddr;
-
-  opt_type = dhcp_get_next_option(dhdr->options, opt_data, &opt_len, &nextopt);
-  while (opt_type != PICO_DHCPOPT_END) {
-    if (opt_type == PICO_DHCPOPT_MSGTYPE)
-      msg_type = opt_data[0];
-    if ((opt_type == PICO_DHCPOPT_LEASETIME) && (opt_len == 4)){
-      memcpy(&cli->lease_time, opt_data, 4);
-      cli->lease_time = long_be(cli->lease_time);
-    }
-    if ((opt_type == PICO_DHCPOPT_RENEWALTIME) && (opt_len == 4)){
-      memcpy(&cli->T1, opt_data, 4);
-      cli->T1 = long_be(cli->T1);
-      T1_set =1;
-    }
-    if ((opt_type == PICO_DHCPOPT_REBINDINGTIME) && (opt_len == 4)){
-      memcpy(&cli->T2, opt_data, 4);
-      cli->T2 = long_be(cli->T2);
-      T2_set =1;
-    }
-    if ((opt_type == PICO_DHCPOPT_ROUTER) && (opt_len == 4)) //XXX assuming only one router will be advertised...
-      memcpy(&cli->gateway.addr, opt_data, 4);
-    if ((opt_type == PICO_DHCPOPT_DNS) && (opt_len == 4))
-      memcpy(&cli->nameserver.addr, opt_data, 4);
-    if ((opt_type == PICO_DHCPOPT_NETMASK) && (opt_len == 4))
-      memcpy(&cli->netmask.addr, opt_data, 4);
-    if ((opt_type == PICO_DHCPOPT_SERVERID) && (opt_len == 4))
-      memcpy(&cli->server_id.addr, opt_data, 4);
-    if (opt_type == PICO_DHCPOPT_OPTIONOVERLOAD)
-      dbg("DHCPC: WARNING: option overload present (not processed)");
-
-    opt_len = 20;
-    opt_type = dhcp_get_next_option(NULL, opt_data, &opt_len, &nextopt);
-  }
-
-  /* default values for T1 and T2 if necessary */
-  if(T1_set != 1)
-    cli->T1 = cli->lease_time >> 1;
-  if(T2_set != 1)
-    cli->T2 = (cli->lease_time * 875) / 1000;
-
-
-
-  if ((msg_type != PICO_DHCP_MSG_OFFER) || !cli->lease_time || !cli->netmask.addr || !cli->server_id.addr )
-    return 0;
-
-
-  dhclient_send(cli, PICO_DHCP_MSG_REQUEST);
-  cli->state = DHCPSTATE_REQUEST;
-  return 1;
-}
-
-static int recv_ack(struct pico_dhcp_client_cookie *cli, uint8_t *data, int len)
-{
-  struct pico_ip4 address = {0};
-
-  if(cli->link_added == 0){
-    /* close the socket bound on address 0.0.0.0 */
-    pico_socket_close(cli->socket);
-    cli->socket = NULL;
-    pico_ipv4_link_del(cli->device, address);
-    pico_ipv4_link_add(cli->device, cli->address, cli->netmask);
-    cli->link_added = 1;
-  }
-  cli->state = DHCPSTATE_BOUND;
-
-  dbg("DHCPC: T1: %d\n",cli->T1);
-  dbg("DHCPC: T2: %d\n",cli->T2);
-  dbg("DHCPC: lease time: %d\n",cli->lease_time);
-
-  if(cli->timer_param_1)
-    cli->timer_param_1->valid = 0;
-  if(cli->timer_param_2)
-    cli->timer_param_2->valid = 0;
-  if(cli->timer_param_lease)
-    cli->timer_param_lease->valid = 0;
-  if(cli->timer_param_retransmit)
-    cli->timer_param_retransmit->valid = 0;
-
-
-  cli->timer_param_1 = pico_zalloc(sizeof(struct dhcp_timer_param));
-  if(!cli->timer_param_1){
-    if(cli->cb != NULL){
-      pico_err = PICO_ERR_ENOMEM;
-      cli->cb(cli, PICO_DHCP_ERROR);
-    }
-    return 0;
-  }
-  cli->timer_param_2 = pico_zalloc(sizeof(struct dhcp_timer_param));
-  if(!cli->timer_param_2){
-    if(cli->cb != NULL){
-      pico_err = PICO_ERR_ENOMEM;
-      cli->cb(cli, PICO_DHCP_ERROR);
-    }
-    return 0;
-  }
-  cli->timer_param_lease = pico_zalloc(sizeof(struct dhcp_timer_param));
-  if(!cli->timer_param_lease){
-    if(cli->cb != NULL){
-      pico_err = PICO_ERR_ENOMEM;
-      cli->cb(cli, PICO_DHCP_ERROR);
-    }
-    return 0;
-  }
-  cli->timer_param_1->valid = 1;
-  cli->timer_param_2->valid = 1;
-  cli->timer_param_lease->valid = 1;
-
-  cli->timer_param_1->cli = cli;
-  cli->timer_param_2->cli = cli;
-  cli->timer_param_lease->cli = cli;
-
-  cli->timer_param_1->type = PICO_DHCP_EVENT_T1;
-  cli->timer_param_2->type = PICO_DHCP_EVENT_T2;
-  cli->timer_param_lease->type = PICO_DHCP_EVENT_LEASE;
-  //add timer
-  pico_timer_add(cli->T1*1000, dhcp_timer_cb, cli->timer_param_1);
-  pico_timer_add(cli->T2*1000, dhcp_timer_cb, cli->timer_param_2);
-  pico_timer_add(cli->lease_time*1000, dhcp_timer_cb, cli->timer_param_lease);
-
-  *(cli->xid_user) = cli->xid;
-  if(cli->cb != NULL)
-    cli->cb(cli, PICO_DHCP_SUCCESS);
-  else
-    dbg("DHCPC: no callback\n");
-
-  dhcp_client_mutex++;
-  cli->state = DHCPSTATE_BOUND;
-  return 0;
-}
-
-static int renew(struct pico_dhcp_client_cookie *cli, uint8_t *data, int len)
-{
-  uint16_t port = PICO_DHCP_CLIENT_PORT;
-
-  /* open and bind to currently acquired address */
-  if (!cli->socket){
-    cli->socket = pico_socket_open(PICO_PROTO_IPV4, PICO_PROTO_UDP, &pico_dhcp_wakeup);
-    if (!cli->socket) {
-      dbg("DHCPC: error opening socket on renew: %s\n", strerror(pico_err));
-      if(cli->cb != NULL)
-        cli->cb(cli, PICO_DHCP_ERROR);
-      return -1;
-    }
-    if (pico_socket_bind(cli->socket, &cli->address, &port) != 0){
-      dbg("DHCPC: error binding socket on renew: %s\n", strerror(pico_err));
-      pico_socket_close(cli->socket);
-      if(cli->cb != NULL)
-        cli->cb(cli, PICO_DHCP_ERROR);
-      return -1;
-    }
-  }
-  cli->state = DHCPSTATE_RENEWING;
-  dhclient_send(cli, PICO_DHCP_MSG_REQUEST);
-
-  return 0;
-}
-
-static int reset(struct pico_dhcp_client_cookie *cli, uint8_t *data, int len)
-{
-  if(cli->cb != NULL)
-    cli->cb(cli, PICO_DHCP_RESET);
-  //reset pretty much everything
-
-  if(cli->timer_param_1)
-    cli->timer_param_1->valid = 0;
-  if(cli->timer_param_2)
-    cli->timer_param_2->valid = 0;
-  if(cli->timer_param_lease)
-    cli->timer_param_lease->valid = 0;
-  if(cli->timer_param_retransmit)
-    cli->timer_param_retransmit->valid = 0;
-
-  pico_socket_close(cli->socket);
-  pico_ipv4_link_del(cli->device, cli->address);
-
-  //initiate negotiations again
-  init_cookie(cli);
-  pico_dhcp_retry(cli);
-  dhclient_send(cli, PICO_DHCP_MSG_DISCOVER);
+  uint8_t buf[DHCP_CLIENT_MAXMSGZISE] = {0};
+  int r = 0;
+  struct pico_dhcp_hdr *hdr = NULL;
+  struct pico_dhcp_client_cookie *dhcpc = NULL;
 
-  return 0;
-
-}
-
-static int retransmit(struct pico_dhcp_client_cookie *cli, uint8_t *data, int len)
-{
-  pico_dhcp_retry(cli);
-
-  if(cli->state == DHCPSTATE_DISCOVER)
-    dhclient_send(cli, PICO_DHCP_MSG_DISCOVER);
-  else if(cli->state == DHCPSTATE_RENEWING)
-    dhclient_send(cli, PICO_DHCP_MSG_REQUEST);
-  else
-    dbg("DHCPC: WARNING: should not get here in state %d!\n", cli->state);
-
-  return 0;
-
-}
-
-/**********************
- * fsm implementation *
- **********************/
-
-struct dhcp_action_entry {
-  uint16_t tcpstate;
-  int (*offer)(struct pico_dhcp_client_cookie *cli, uint8_t *data, int len);
-  int (*ack)(struct pico_dhcp_client_cookie *cli, uint8_t *data, int len);
-  int (*nak)(struct pico_dhcp_client_cookie *cli, uint8_t *data, int len);
-  int (*timer1)(struct pico_dhcp_client_cookie *cli, uint8_t *data, int len);
-  int (*timer_lease)(struct pico_dhcp_client_cookie *cli, uint8_t *data, int len);
-  int (*timer_retransmit)(struct pico_dhcp_client_cookie *cli, uint8_t *data, int len);
-};
-
-static struct dhcp_action_entry dhcp_fsm[] = {
-    /* State             offer       ack       nak     timer1  timer_lease timer_retransmit*/
-  { DHCPSTATE_DISCOVER,  recv_offer, NULL,     NULL,   NULL,   reset,      retransmit},
-  { DHCPSTATE_OFFER,     NULL,       NULL,     NULL,   NULL,   reset,      NULL},
-  { DHCPSTATE_REQUEST,   NULL,       recv_ack, reset,  NULL,   reset,      retransmit},
-  { DHCPSTATE_BOUND,     NULL,       NULL,     reset,  renew,  reset,      NULL},
-  { DHCPSTATE_RENEWING,  NULL,       recv_ack, reset,  NULL,   reset,      retransmit},
-};
-
-
-static void pico_dhcp_state_machine(int type, struct pico_dhcp_client_cookie* cli, uint8_t* data, int len)
-{
-  dbg("DHCPC: received incoming event of type %d\n", type);
-  switch(type){
-    case PICO_DHCP_MSG_OFFER:
-      if(dhcp_fsm[cli->state].offer != NULL)
-        dhcp_fsm[cli->state].offer(cli, data, len);
-      break;
-    case PICO_DHCP_MSG_ACK:
-      if(dhcp_fsm[cli->state].ack != NULL){
-        dhcp_fsm[cli->state].ack(cli, data, len);
-      }
-      break;
-    case PICO_DHCP_MSG_NAK:
-      if(dhcp_fsm[cli->state].nak!= NULL){
-        dhcp_fsm[cli->state].nak(cli, data, len);
-      }
-      break;
-    case PICO_DHCP_EVENT_T1:
-      if(dhcp_fsm[cli->state].timer1!= NULL){
-        dhcp_fsm[cli->state].timer1(cli, NULL, 0);
-      }
-      break;
-    case PICO_DHCP_EVENT_LEASE:
-      if(dhcp_fsm[cli->state].timer_lease!= NULL){
-        dhcp_fsm[cli->state].timer_lease(cli, NULL, 0);
-      }
-      break;
-    case PICO_DHCP_EVENT_RETRANSMIT:
-      if(dhcp_fsm[cli->state].timer_retransmit!= NULL){
-        dhcp_fsm[cli->state].timer_retransmit(cli, NULL, 0);
-      }
-      break;
-    default:
-      dbg("DHCPC: event not supported yet!!\n");
-      break;
-  }
-}
-
-
-/*********************
- * utility functions *
- *********************/
-
-static void pico_dhcp_retry(struct pico_dhcp_client_cookie *cli)
-{
-  const int MAX_RETRY = 3;
-  uint32_t new_xid;
-  if (++cli->attempt > MAX_RETRY) {
-    cli->start_time = pico_tick;
-    cli->attempt = 0;
-     new_xid = pico_rand();
-    while(get_cookie_by_xid(new_xid) != NULL){
-      new_xid = pico_rand();
-    }
-    cli->xid = new_xid;
-    cli->state = DHCPSTATE_DISCOVER;
-  }
-}
-
-static int dhclient_send(struct pico_dhcp_client_cookie *cli, uint8_t msg_type)
-{
-  uint8_t buf_out[DHCPC_DATAGRAM_SIZE] = {0};
-  struct pico_dhcphdr *dh_out = (struct pico_dhcphdr *) buf_out;
-  int sent = 0;
-  int i = 0;
-  struct pico_ip4 destination;
-  uint16_t port = PICO_DHCPD_PORT;
-  if(cli->state == DHCPSTATE_BOUND || cli->state == DHCPSTATE_RENEWING){
-    destination.addr = cli->server_id.addr;
-  }else{
-    destination.addr = long_be(0xFFFFFFFF);
-  }
-
-  if(cli->device->eth == NULL){
-    pico_err = PICO_ERR_EOPNOTSUPP;
-    if(cli->cb != NULL){
-      cli->cb(cli, PICO_DHCP_ERROR);
-    }
-    return -1;
-  }
-  memcpy(dh_out->hwaddr, &cli->device->eth->mac, PICO_HLEN_ETHER);
-  dh_out->op = PICO_DHCP_OP_REQUEST;
-  dh_out->htype = PICO_HTYPE_ETHER;
-  dh_out->hlen = PICO_HLEN_ETHER;
-  dh_out->xid = cli->xid;
-  dh_out->secs = (msg_type == PICO_DHCP_MSG_REQUEST)?0:short_be((pico_tick - cli->start_time)/1000);
-  dh_out->dhcp_magic = PICO_DHCPD_MAGIC_COOKIE;
-  if (cli->state == DHCPSTATE_RENEWING)
-    dh_out->ciaddr = cli->address.addr;
-
-  /* Option: msg type, len 1 */
-  dh_out->options[i++] = PICO_DHCPOPT_MSGTYPE;
-  dh_out->options[i++] = 1;
-  dh_out->options[i++] = msg_type;
+  if (ev != PICO_SOCK_EV_RD)
+    return;
+  r = pico_socket_recvfrom(s, buf, DHCP_CLIENT_MAXMSGZISE, NULL, NULL);
+  if (r < 0)
+    return;
 
-  if (( msg_type == PICO_DHCP_MSG_REQUEST) && ( cli->state != DHCPSTATE_RENEWING )) {
-    dh_out->options[i++] = PICO_DHCPOPT_REQIP;
-    dh_out->options[i++] = 4;
-    dh_out->options[i++] = (long_be(cli->address.addr) & 0xFF000000) >> 24;
-    dh_out->options[i++] = (long_be(cli->address.addr) & 0xFF0000) >> 16;
-    dh_out->options[i++] = (long_be(cli->address.addr) & 0xFF00) >> 8;
-    dh_out->options[i++] = (long_be(cli->address.addr) & 0xFF);
-    dh_out->options[i++] = PICO_DHCPOPT_SERVERID;
-    dh_out->options[i++] = 4;
-    dh_out->options[i++] = (long_be(cli->server_id.addr) & 0xFF000000) >> 24;
-    dh_out->options[i++] = (long_be(cli->server_id.addr) & 0xFF0000) >> 16;
-    dh_out->options[i++] = (long_be(cli->server_id.addr) & 0xFF00) >> 8;
-    dh_out->options[i++] = (long_be(cli->server_id.addr) & 0xFF);
-  }
-
-  /* Option: req list, len 4 */
-  dh_out->options[i++] = PICO_DHCPOPT_PARMLIST;
-  dh_out->options[i++] = 7;
-  dh_out->options[i++] = PICO_DHCPOPT_NETMASK;
-  dh_out->options[i++] = PICO_DHCPOPT_BCAST;
-  dh_out->options[i++] = PICO_DHCPOPT_TIME;
-  dh_out->options[i++] = PICO_DHCPOPT_ROUTER;
-  dh_out->options[i++] = PICO_DHCPOPT_HOSTNAME;
-  dh_out->options[i++] = PICO_DHCPOPT_RENEWALTIME;
-  dh_out->options[i++] = PICO_DHCPOPT_REBINDINGTIME;
-
-  /* Option : max message size */
-  if( msg_type == PICO_DHCP_MSG_REQUEST || msg_type == PICO_DHCP_MSG_DISCOVER){
-    uint16_t dds = DHCPC_DATAGRAM_SIZE;
-    dh_out->options[i++] = PICO_DHCPOPT_MAXMSGSIZE;
-    dh_out->options[i++] = 2;
-    dh_out->options[i++] = (dds & 0xFF00) >> 8;
-    dh_out->options[i++] = (dds & 0xFF);
-  }
-
-
-
-  dh_out->options[i] = PICO_DHCPOPT_END;
-  sent = pico_socket_sendto(cli->socket, buf_out, DHCPC_DATAGRAM_SIZE, &destination, port);
-  if (sent < 0) {
-    dbg("DHCPC: sendto failed: %s\n", strerror(pico_err));
-    if(cli->cb != NULL)
-      cli->cb(cli, PICO_DHCP_ERROR);
-  }
-
-
-  //resend-timer :
-  if(cli->timer_param_retransmit != NULL)
-    cli->timer_param_retransmit->valid=0;
-
-  cli->timer_param_retransmit = pico_zalloc(sizeof(struct dhcp_timer_param));
-  if(!cli->timer_param_retransmit){
-    if(cli->cb != NULL)
-      pico_err = PICO_ERR_ENOMEM;
-      cli->cb(cli, PICO_DHCP_ERROR);
-    return -1;
-  }
-  cli->timer_param_retransmit->valid = 1;
-  cli->timer_param_retransmit->cli = cli;
-  cli->timer_param_retransmit->type = PICO_DHCP_EVENT_RETRANSMIT;
-  pico_timer_add(4000, dhcp_timer_cb, cli->timer_param_retransmit);
-
-  return 0;
-}
-
-//identifies type & does some preprocessing : checking if everything is valid
-static int pico_dhcp_verify_and_identify_type(uint8_t* data, int len, struct pico_dhcp_client_cookie *cli)
-{
-  struct pico_dhcphdr *dhdr = (struct pico_dhcphdr *) data;
-  uint8_t *nextopt, opt_data[20], opt_type;
-  int opt_len = 20;
-
-  if (dhdr->xid != cli->xid)
-    return 0;
-
-  if (!is_options_valid(dhdr->options, len - sizeof(struct pico_dhcphdr)))
-    return 0;
-
-  if( dhdr->dhcp_magic != PICO_DHCPD_MAGIC_COOKIE)
-    return 0;
-
-  opt_type = dhcp_get_next_option(dhdr->options, opt_data, &opt_len, &nextopt);
-  while (opt_type != PICO_DHCPOPT_END) {
-    /* parse interesting options here */
-    if (opt_type == PICO_DHCPOPT_MSGTYPE) {
-      return *opt_data;
-    }
-    opt_len = 20;
-    opt_type = dhcp_get_next_option(NULL, opt_data, &opt_len, &nextopt);
-  }
-  return 0;
-
-}
-
-static int init_cookie(struct pico_dhcp_client_cookie* cli)
-{
-  uint8_t n = 3;
-  uint16_t port = PICO_DHCP_CLIENT_PORT;
-  struct pico_ip4 address, netmask;
-
-  address.addr = long_be(0x00000000);
-  netmask.addr = long_be(0x00000000);
-
-  cli->state = DHCPSTATE_DISCOVER;
-  cli->start_time = pico_tick;
-  cli->attempt = 0;
-
-  cli->socket = pico_socket_open(PICO_PROTO_IPV4, PICO_PROTO_UDP, &pico_dhcp_wakeup);
-  if (!cli->socket) {
-    dbg("DHCPC: error opening socket: %s\n", strerror(pico_err));
-    if(cli->cb != NULL)
-      cli->cb(cli, PICO_DHCP_ERROR);
-    return -1;
-  }
-  if (pico_socket_bind(cli->socket, &address, &port) != 0){
-    dbg("DHCPC: error binding socket: %s\n", strerror(pico_err));
-    pico_socket_close(cli->socket);
-    if(cli->cb != NULL)
-      cli->cb(cli, PICO_DHCP_ERROR);
-    return -1;
-  }
-  cli->socket->dev = cli->device;
-
-  if(pico_ipv4_link_add(cli->device, address, netmask) != 0){
-    dbg("DHCPC: error adding link: %s\n", strerror(pico_err));
-    if(cli->cb != NULL)
-      cli->cb(cli, PICO_DHCP_ERROR);
-    return -1;
-  }
-
-  /* attempt to generate a correct xid 3 times, then fail */
-  do {
-    cli->xid = pico_rand();
-  } while (!cli->xid && --n);
-  if (!cli->xid) {
-    if(cli->cb != NULL)
-      cli->cb(cli, PICO_DHCP_ERROR);
-    return -1;
-  }
-
-  return 0;
-}
-
-static struct pico_dhcp_client_cookie *get_cookie_by_xid(uint32_t xid)
-{
-  struct pico_dhcp_client_cookie test = { }, *cookie = NULL;
-
-  if (!xid)
-    return NULL;
-
-  test.xid = xid;
-  cookie = pico_tree_findKey(&DHCPCookies, &test);
-  if (!cookie)
-    return NULL;
-  else
-    return cookie;
+  /* If the 'xid' of an arriving message does not match the 'xid' 
+   * of the most recent transmitted message, the message must be 
+   * silently discarded. */
+  hdr = (struct pico_dhcp_hdr *)buf;
+  dhcpc = pico_dhcp_client_find_cookie(hdr->xid);
+  if (!dhcpc)
+    return;
+  dhcpc->event = pico_dhcp_client_opt_parse(buf, r);
+  pico_dhcp_state_machine(dhcpc->event, dhcpc, buf);
 }
 
 void *pico_dhcp_get_identifier(uint32_t xid)
 {
-  return (void *) get_cookie_by_xid(xid);
+  return (void *)pico_dhcp_client_find_cookie(xid);
+}
+
+struct pico_ip4 pico_dhcp_get_address(void* dhcpc)
+{
+  return ((struct pico_dhcp_client_cookie*)dhcpc)->address;
 }
 
-static uint32_t get_xid(uint8_t* data)
+struct pico_ip4 pico_dhcp_get_gateway(void* dhcpc)
 {
-  struct pico_dhcphdr *dhdr = (struct pico_dhcphdr *) data;
-  return dhdr->xid;
+  return ((struct pico_dhcp_client_cookie*)dhcpc)->gateway;
 }
 
+struct pico_ip4 pico_dhcp_get_nameserver(void* dhcpc)
+{
+  return ((struct pico_dhcp_client_cookie*)dhcpc)->nameserver;
+}
 #endif