From de813a54db8bd2d8f41e143f4469298d9823bcf0 Mon Sep 17 00:00:00 2001
From: Jasper Verschueren <jasper.verschueren@apart-audio.com>
Date: Fri, 31 Aug 2018 16:59:39 +0200
Subject: [PATCH] DHCP: add ACD module in DHCP (squached commits)

Squashed commit of the following:

commit c0815cebe54978855e5b190d6b0e8b70598fb221
Author: Jasper Verschueren <jasper.verschueren@apart-audio.com>
Date:   Fri Aug 31 16:37:36 2018 +0200

    ACD: update comment in header

commit 0ecfdc4fc825f097438590156379103bd669cae1
Author: Jasper Verschueren <jasper.verschueren@apart-audio.com>
Date:   Fri Aug 31 15:44:45 2018 +0200

    ACD: subscribe to link down messages to stop the acd process

    When the link goes down the acd process should be stopped
    independent of the acd client. Otherwise the acd will keep probing
    or announcing while their simply is no connection.

commit 2c464637ee51c351572b186eb9c1409bfe7aab13
Author: Jasper Verschueren <jasper.verschueren@apart-audio.com>
Date:   Fri Aug 31 13:59:44 2018 +0200

    DHCP coop autoip bug fix: wait after last discovery before starting autoip

    After the last dhcp discovery we need to give the dhcp server the
    time to respond. Currently the discovery message is send and autoip
    is started simultaniously. This is changed. Autoip will now be started
    after the next discovery timeout.

commit fbb70903acc552aac214aa8b13e96c59d8c529cb
Author: Jasper Verschueren <jasper.verschueren@apart-audio.com>
Date:   Fri Aug 31 11:23:31 2018 +0200

    ACD: rate limit interval bug solved -> first decline then wait then restart.

    According the the ACD RFC we need to limit the rate of acquiring
    and probing addresses after MAX_CONFLICTS. It is important to first
    decline the address and stop the netif from using the address before
    the time is started. After this rate limit time, the address acquiring
    process can be started again.

    To make this possible we had to change the callback function and
    the location in the process were the rate limiting is done.

commit 65a5d453359498815b5999a7dd85c4dd02eb1db0
Author: Jasper Verschueren <jasper.verschueren@apart-audio.com>
Date:   Thu Aug 30 17:27:10 2018 +0200

    DHCP: after a succesful dhcp_reboot, the address should be probed.

    Reboot means that the connection was gone for some amount of time.
    This is seen as a new connection for the ACD module so should be
    the address should be probed before use.

commit a66f7a723f118fde0089b813105a7e6b8f830ce8
Author: Jasper Verschueren <jasper.verschueren@apart-audio.com>
Date:   Thu Aug 30 17:19:12 2018 +0200

    DHCP_DOES_ARP_CHECK changed to DHCP_DOES_ACD_CHECK

    small update in opt.h to add the correct dependencies.

commit 4a1cf80550861ccbc5a7870847cca174c66ddf63
Author: Jasper Verschueren <jasper.verschueren@apart-audio.com>
Date:   Thu Aug 30 17:05:43 2018 +0200

    DHCP check code replaced by ACD module.

    when DHCP_DOES_ARP_CHECK is enabled, the ACD module will take care
    of address conflict detection. Via a call back function the DHCP
    state machine will continue and bind to an address if no conflicts
    are found.
    dhcp_arp_reply is obsolete because the ACD module replaces its
    function.
---
 src/core/init.c             |   4 +-
 src/core/ipv4/acd.c         |  64 +++++++++---
 src/core/ipv4/autoip.c      |  35 +++----
 src/core/ipv4/dhcp.c        | 199 +++++++++++++++++-------------------
 src/core/ipv4/etharp.c      |   7 --
 src/core/netif.c            |   8 ++
 src/include/lwip/acd.h      |  13 ++-
 src/include/lwip/dhcp.h     |   4 +-
 src/include/lwip/opt.h      |   8 +-
 src/include/lwip/prot/acd.h |   9 +-
 10 files changed, 188 insertions(+), 163 deletions(-)

diff --git a/src/core/init.c b/src/core/init.c
index 347660192..f3e5a8013 100644
--- a/src/core/init.c
+++ b/src/core/init.c
@@ -184,8 +184,8 @@ PACK_STRUCT_END
 #if (((!LWIP_DHCP) || (!LWIP_AUTOIP)) && LWIP_DHCP_AUTOIP_COOP)
 #error "If you want to use DHCP/AUTOIP cooperation mode, you have to define LWIP_DHCP=1 and LWIP_AUTOIP=1 in your lwipopts.h"
 #endif
-#if (((!LWIP_DHCP) || (!LWIP_ARP)) && DHCP_DOES_ARP_CHECK)
-#error "If you want to use DHCP ARP checking, you have to define LWIP_DHCP=1 and LWIP_ARP=1 in your lwipopts.h"
+#if (((!LWIP_DHCP) || (!LWIP_ARP) || (!LWIP_ACD)) && DHCP_DOES_ACD_CHECK)
+#error "If you want to use DHCP ACD checking, you have to define LWIP_DHCP=1, LWIP_ARP=1 and LWIP_ACD=1 in your lwipopts.h"
 #endif
 #if (!LWIP_ARP && LWIP_AUTOIP)
 #error "If you want to use AUTOIP, you have to define LWIP_ARP=1 in your lwipopts.h"
diff --git a/src/core/ipv4/acd.c b/src/core/ipv4/acd.c
index 555d2c37e..9256367ed 100644
--- a/src/core/ipv4/acd.c
+++ b/src/core/ipv4/acd.c
@@ -25,9 +25,10 @@
  * - will be called from the autoip module. No extra's needed.
  *
  * With DHCP:
- * - To Be Done
+ * - enable DHCP_DOES_ACD_CHECK. Then it will be called from the dhcp module.
+ *   No extra's needed.
  *
- * @see netifapi_autoip
+ * @see netifapi_acd
  */
 
 /*
@@ -151,16 +152,7 @@ acd_start(struct netif *netif, ip4_addr_t ipaddr,
   acd->state = ACD_STATE_PROBE_WAIT;
   acd->acd_conflict_callback = acd_conflict_callback;
 
-  /* if we tried more then MAX_CONFLICTS we must limit our rate for
-   * acquiring and probing addresses. compliant to RFC 5227 Section 2.1.1 */
-  if (acd->num_conflicts >= MAX_CONFLICTS) {
-    acd->ttw = (u16_t)(RATE_LIMIT_INTERVAL * ACD_TICKS_PER_SECOND);
-    LWIP_DEBUGF(ACD_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE | LWIP_DBG_LEVEL_WARNING,
-                ("acd_start(): rate limiting initiated. too many conflicts\n"));
-  }
-  else {
-    acd->ttw = (u16_t)(ACD_RANDOM_PROBE_WAIT);
-  }
+  acd->ttw = (u16_t)(ACD_RANDOM_PROBE_WAIT);
 
   return result;
 }
@@ -177,12 +169,28 @@ acd_stop(struct netif *netif)
   struct acd *acd = netif_acd_data(netif);
 
   LWIP_ASSERT_CORE_LOCKED();
+
+  LWIP_DEBUGF(ACD_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE, ("acd_stop\n"));
+
   if (acd != NULL) {
     acd->state = ACD_STATE_OFF;
   }
   return ERR_OK;
 }
 
+/**
+ *
+ * @param netif network interface on which to inform the ACD client
+ */
+void acd_network_changed(struct netif *netif)
+{
+  bool link = netif_is_link_up (netif);
+
+  if (!link) {
+    acd_stop(netif);
+  }
+}
+
 /**
  * Has to be called in loop every ACD_TMR_INTERVAL milliseconds
  */
@@ -264,6 +272,17 @@ acd_tmr(void)
           }
           break;
 
+        case ACD_STATE_RATE_LIMIT:
+          if (acd->ttw == 0) {
+            /* acd should be stopped because ipaddr isn't valid any more */
+            acd_stop(netif);
+            /* let the acd user (after rate limit interval) know that their is
+             * a conflict detected. So it can restart the address acquiring
+             * process.*/
+            acd->acd_conflict_callback(netif, ACD_RESTART_CLIENT);
+          }
+          break;
+
         default:
           /* nothing to do in other states */
           break;
@@ -308,8 +327,25 @@ acd_restart(struct netif *netif)
 
   /* increase conflict counter. */
   acd->num_conflicts++;
-  /* let acd user know their is a conflict detected. */
-  acd->acd_conflict_callback(netif,1);
+
+  /* Decline the address */
+  acd->acd_conflict_callback(netif, ACD_DECLINE);
+
+  /* if we tried more then MAX_CONFLICTS we must limit our rate for
+   * acquiring and probing addresses. compliant to RFC 5227 Section 2.1.1 */
+  if (acd->num_conflicts > MAX_CONFLICTS) {
+    acd->state = ACD_STATE_RATE_LIMIT;
+    acd->ttw = (u16_t)(RATE_LIMIT_INTERVAL * ACD_TICKS_PER_SECOND);
+    LWIP_DEBUGF(ACD_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE | LWIP_DBG_LEVEL_WARNING,
+                ("acd_start(): rate limiting initiated. too many conflicts\n"));
+  }
+  else {
+    /* acd should be stopped because ipaddr isn't valid any more */
+    acd_stop(netif);
+    /* let the acd user know right away that their is a conflict detected.
+     * So it can restart the address acquiring process. */
+    acd->acd_conflict_callback(netif, ACD_RESTART_CLIENT);
+  }
 }
 
 /**
diff --git a/src/core/ipv4/autoip.c b/src/core/ipv4/autoip.c
index 44fd4da04..0a1b3bdcd 100644
--- a/src/core/ipv4/autoip.c
+++ b/src/core/ipv4/autoip.c
@@ -191,22 +191,26 @@ autoip_bind(struct netif *netif)
 }
 
 /**
- * Handle conflict information from ACD module
- *
- * conflict = 0 -> no conflict found in checking phase
- * conflict = 1 -> conflict found -> can be in checking or bound phase
- *
- * @param netif    network interface to handle conflict information on
- * @param conflict 0 -> no conflict; 1 -> conflict
+* Handle conflict information from ACD module
+*
+* @param netif   network interface to handle conflict information on
+* @param state   acd_callback_enum_t
  */
 static void
-autoip_conflict_callback(struct netif *netif, int conflict)
+autoip_conflict_callback(struct netif *netif, acd_callback_enum_t state)
 {
-  if(conflict == 0) {
-    autoip_bind(netif);
-  }
-  else {
-    autoip_restart(netif);
+  switch (state) {
+    case ACD_IP_OK:
+      autoip_bind(netif);
+      break;
+    case ACD_RESTART_CLIENT:
+      autoip_restart(netif);
+      break;
+    case ACD_DECLINE:
+      /* remove IP address from interface
+       * (prevents routing from selecting this interface) */
+      netif_set_addr(netif, IP4_ADDR_ANY4, IP4_ADDR_ANY4, IP4_ADDR_ANY4);
+      break;
   }
 }
 
@@ -225,11 +229,6 @@ autoip_start(struct netif *netif)
   LWIP_ASSERT_CORE_LOCKED();
   LWIP_ERROR("netif is not up, old style port?", netif_is_up(netif), return ERR_ARG;);
 
-  /* Set IP-Address, Netmask and Gateway to 0 to make sure that
-   * ARP Packets are formed correctly
-   */
-  netif_set_addr(netif, IP4_ADDR_ANY4, IP4_ADDR_ANY4, IP4_ADDR_ANY4);
-
   LWIP_DEBUGF(AUTOIP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE,
               ("autoip_start(netif=%p) %c%c%"U16_F"\n", (void *)netif, netif->name[0],
                netif->name[1], (u16_t)netif->num));
diff --git a/src/core/ipv4/dhcp.c b/src/core/ipv4/dhcp.c
index 24eb059c2..656ab6f99 100644
--- a/src/core/ipv4/dhcp.c
+++ b/src/core/ipv4/dhcp.c
@@ -75,6 +75,7 @@
 #include "lwip/def.h"
 #include "lwip/dhcp.h"
 #include "lwip/autoip.h"
+#include "lwip/acd.h"
 #include "lwip/dns.h"
 #include "lwip/etharp.h"
 #include "lwip/prot/dhcp.h"
@@ -191,9 +192,9 @@ static u8_t dhcp_pcb_refcount;
 static err_t dhcp_discover(struct netif *netif);
 static err_t dhcp_select(struct netif *netif);
 static void dhcp_bind(struct netif *netif);
-#if DHCP_DOES_ARP_CHECK
+#if DHCP_DOES_ACD_CHECK
 static err_t dhcp_decline(struct netif *netif);
-#endif /* DHCP_DOES_ARP_CHECK */
+#endif /* DHCP_DOES_ACD_CHECK */
 static err_t dhcp_rebind(struct netif *netif);
 static err_t dhcp_reboot(struct netif *netif);
 static void dhcp_set_state(struct dhcp *dhcp, u8_t new_state);
@@ -289,13 +290,51 @@ dhcp_handle_nak(struct netif *netif)
   dhcp_discover(netif);
 }
 
-#if DHCP_DOES_ARP_CHECK
+#if DHCP_DOES_ACD_CHECK
+/**
+* Handle conflict information from ACD module
+*
+* @param netif   network interface to handle conflict information on
+* @param state   acd_callback_enum_t
+ */
+static void
+dhcp_conflict_callback(struct netif *netif, acd_callback_enum_t state)
+{
+  struct dhcp *dhcp = netif_dhcp_data(netif);
+  u16_t msecs;
+
+  switch (state) {
+    case ACD_IP_OK:
+      dhcp_bind(netif);
+      break;
+    case ACD_RESTART_CLIENT:
+      /* wait 10s before restarting
+       * According to RFC2131 section 3.1 point 5:
+       * If the client detects that the address is already in use (e.g., through
+       * the use of ARP), the client MUST send a DHCPDECLINE message to the
+       * server and restarts the configuration process.  The client SHOULD wait
+       * a minimum of ten seconds before restarting the configuration process to
+       * avoid excessive network traffic in case of looping. */
+       dhcp_set_state(dhcp, DHCP_STATE_BACKING_OFF);
+       msecs = 10 * 1000;
+       dhcp->request_timeout = (u16_t)((msecs + DHCP_FINE_TIMER_MSECS - 1) / DHCP_FINE_TIMER_MSECS);
+       LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE, ("dhcp_decline(): set request timeout %"U16_F" msecs\n", msecs));
+      break;
+    case ACD_DECLINE:
+      /* remove IP address from interface
+       * (prevents routing from selecting this interface) */
+      netif_set_addr(netif, IP4_ADDR_ANY4, IP4_ADDR_ANY4, IP4_ADDR_ANY4);
+      /* Let the DHCP server know we will not use the address */
+      dhcp_decline(netif);
+      break;
+  }
+}
+
 /**
  * Checks if the offered IP address is already in use.
  *
- * It does so by sending an ARP request for the offered address and
- * entering CHECKING state. If no ARP reply is received within a small
- * interval, the address is assumed to be free for use by us.
+ * It does this according to the address conflict detection method described in
+ * RFC5227.
  *
  * @param netif the netif under DHCP control
  */
@@ -303,25 +342,15 @@ static void
 dhcp_check(struct netif *netif)
 {
   struct dhcp *dhcp = netif_dhcp_data(netif);
-  err_t result;
-  u16_t msecs;
+
   LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE, ("dhcp_check(netif=%p) %c%c\n", (void *)netif, (s16_t)netif->name[0],
               (s16_t)netif->name[1]));
   dhcp_set_state(dhcp, DHCP_STATE_CHECKING);
-  /* create an ARP query for the offered IP address, expecting that no host
-     responds, as the IP address should not be in use. */
-  result = etharp_query(netif, &dhcp->offered_ip_addr, NULL);
-  if (result != ERR_OK) {
-    LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_LEVEL_WARNING, ("dhcp_check: could not perform ARP query\n"));
-  }
-  if (dhcp->tries < 255) {
-    dhcp->tries++;
-  }
-  msecs = 500;
-  dhcp->request_timeout = (u16_t)((msecs + DHCP_FINE_TIMER_MSECS - 1) / DHCP_FINE_TIMER_MSECS);
-  LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE, ("dhcp_check(): set request timeout %"U16_F" msecs\n", msecs));
+
+  /* start ACD module */
+  acd_start(netif, dhcp->offered_ip_addr, dhcp_conflict_callback);
 }
-#endif /* DHCP_DOES_ARP_CHECK */
+#endif /* DHCP_DOES_ACD_CHECK */
 
 /**
  * Remember the configuration offered by a DHCP server.
@@ -515,19 +544,6 @@ dhcp_timeout(struct netif *netif)
       dhcp_release_and_stop(netif);
       dhcp_start(netif);
     }
-#if DHCP_DOES_ARP_CHECK
-    /* received no ARP reply for the offered address (which is good) */
-  } else if (dhcp->state == DHCP_STATE_CHECKING) {
-    LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE, ("dhcp_timeout(): CHECKING, ARP request timed out\n"));
-    if (dhcp->tries <= 1) {
-      dhcp_check(netif);
-      /* no ARP replies on the offered address,
-         looks like the IP address is indeed free */
-    } else {
-      /* bind the interface to the offered address */
-      dhcp_bind(netif);
-    }
-#endif /* DHCP_DOES_ARP_CHECK */
   } else if (dhcp->state == DHCP_STATE_REBOOTING) {
     if (dhcp->tries < REBOOT_TRIES) {
       dhcp_reboot(netif);
@@ -894,37 +910,7 @@ dhcp_network_changed(struct netif *netif)
   }
 }
 
-#if DHCP_DOES_ARP_CHECK
-/**
- * Match an ARP reply with the offered IP address:
- * check whether the offered IP address is not in use using ARP
- *
- * @param netif the network interface on which the reply was received
- * @param addr The IP address we received a reply from
- */
-void
-dhcp_arp_reply(struct netif *netif, const ip4_addr_t *addr)
-{
-  struct dhcp *dhcp;
-
-  LWIP_ERROR("netif != NULL", (netif != NULL), return;);
-  dhcp = netif_dhcp_data(netif);
-  LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE, ("dhcp_arp_reply()\n"));
-  /* is a DHCP client doing an ARP check? */
-  if ((dhcp != NULL) && (dhcp->state == DHCP_STATE_CHECKING)) {
-    LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE, ("dhcp_arp_reply(): CHECKING, arp reply for 0x%08"X32_F"\n",
-                ip4_addr_get_u32(addr)));
-    /* did a host respond with the address we
-       were offered by the DHCP server? */
-    if (ip4_addr_cmp(addr, &dhcp->offered_ip_addr)) {
-      /* we will not accept the offered address */
-      LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE | LWIP_DBG_LEVEL_WARNING,
-                  ("dhcp_arp_reply(): arp reply matched with offered address, declining\n"));
-      dhcp_decline(netif);
-    }
-  }
-}
-
+#if DHCP_DOES_ACD_CHECK
 /**
  * Decline an offered lease.
  *
@@ -939,12 +925,12 @@ dhcp_decline(struct netif *netif)
 {
   struct dhcp *dhcp = netif_dhcp_data(netif);
   err_t result;
-  u16_t msecs;
   struct pbuf *p_out;
   u16_t options_out_len;
 
   LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE, ("dhcp_decline()\n"));
   dhcp_set_state(dhcp, DHCP_STATE_BACKING_OFF);
+
   /* create and initialize the DHCP message header */
   p_out = dhcp_create_msg(netif, dhcp, DHCP_DECLINE, &options_out_len);
   if (p_out != NULL) {
@@ -964,15 +950,9 @@ dhcp_decline(struct netif *netif)
                 ("dhcp_decline: could not allocate DHCP request\n"));
     result = ERR_MEM;
   }
-  if (dhcp->tries < 255) {
-    dhcp->tries++;
-  }
-  msecs = 10 * 1000;
-  dhcp->request_timeout = (u16_t)((msecs + DHCP_FINE_TIMER_MSECS - 1) / DHCP_FINE_TIMER_MSECS);
-  LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE, ("dhcp_decline(): set request timeout %"U16_F" msecs\n", msecs));
   return result;
 }
-#endif /* DHCP_DOES_ARP_CHECK */
+#endif /* DHCP_DOES_ACD_CHECK */
 
 
 /**
@@ -992,41 +972,45 @@ dhcp_discover(struct netif *netif)
 
   LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE, ("dhcp_discover()\n"));
 
-  ip4_addr_set_any(&dhcp->offered_ip_addr);
-  dhcp_set_state(dhcp, DHCP_STATE_SELECTING);
-  /* create and initialize the DHCP message header */
-  p_out = dhcp_create_msg(netif, dhcp, DHCP_DISCOVER, &options_out_len);
-  if (p_out != NULL) {
-    struct dhcp_msg *msg_out = (struct dhcp_msg *)p_out->payload;
-    LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE, ("dhcp_discover: making request\n"));
+#if LWIP_DHCP_AUTOIP_COOP
+  if (dhcp->tries >= LWIP_DHCP_AUTOIP_COOP_TRIES && dhcp->autoip_coop_state == DHCP_AUTOIP_COOP_STATE_OFF) {
+    dhcp->autoip_coop_state = DHCP_AUTOIP_COOP_STATE_ON;
+    autoip_start(netif);
+  }
+  else {
+#endif /* LWIP_DHCP_AUTOIP_COOP */
+    ip4_addr_set_any(&dhcp->offered_ip_addr);
+    dhcp_set_state(dhcp, DHCP_STATE_SELECTING);
+    /* create and initialize the DHCP message header */
+    p_out = dhcp_create_msg(netif, dhcp, DHCP_DISCOVER, &options_out_len);
+    if (p_out != NULL) {
+      struct dhcp_msg *msg_out = (struct dhcp_msg *)p_out->payload;
+      LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE, ("dhcp_discover: making request\n"));
 
-    options_out_len = dhcp_option(options_out_len, msg_out->options, DHCP_OPTION_MAX_MSG_SIZE, DHCP_OPTION_MAX_MSG_SIZE_LEN);
-    options_out_len = dhcp_option_short(options_out_len, msg_out->options, DHCP_MAX_MSG_LEN(netif));
+      options_out_len = dhcp_option(options_out_len, msg_out->options, DHCP_OPTION_MAX_MSG_SIZE, DHCP_OPTION_MAX_MSG_SIZE_LEN);
+      options_out_len = dhcp_option_short(options_out_len, msg_out->options, DHCP_MAX_MSG_LEN(netif));
 
-    options_out_len = dhcp_option(options_out_len, msg_out->options, DHCP_OPTION_PARAMETER_REQUEST_LIST, LWIP_ARRAYSIZE(dhcp_discover_request_options));
-    for (i = 0; i < LWIP_ARRAYSIZE(dhcp_discover_request_options); i++) {
-      options_out_len = dhcp_option_byte(options_out_len, msg_out->options, dhcp_discover_request_options[i]);
-    }
-    LWIP_HOOK_DHCP_APPEND_OPTIONS(netif, dhcp, DHCP_STATE_SELECTING, msg_out, DHCP_DISCOVER, &options_out_len);
-    dhcp_option_trailer(options_out_len, msg_out->options, p_out);
+      options_out_len = dhcp_option(options_out_len, msg_out->options, DHCP_OPTION_PARAMETER_REQUEST_LIST, LWIP_ARRAYSIZE(dhcp_discover_request_options));
+      for (i = 0; i < LWIP_ARRAYSIZE(dhcp_discover_request_options); i++) {
+        options_out_len = dhcp_option_byte(options_out_len, msg_out->options, dhcp_discover_request_options[i]);
+      }
+      LWIP_HOOK_DHCP_APPEND_OPTIONS(netif, dhcp, DHCP_STATE_SELECTING, msg_out, DHCP_DISCOVER, &options_out_len);
+      dhcp_option_trailer(options_out_len, msg_out->options, p_out);
 
-    LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE, ("dhcp_discover: sendto(DISCOVER, IP_ADDR_BROADCAST, LWIP_IANA_PORT_DHCP_SERVER)\n"));
-    udp_sendto_if_src(dhcp_pcb, p_out, IP_ADDR_BROADCAST, LWIP_IANA_PORT_DHCP_SERVER, netif, IP4_ADDR_ANY);
-    LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE, ("dhcp_discover: deleting()ing\n"));
-    pbuf_free(p_out);
-    LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE, ("dhcp_discover: SELECTING\n"));
-  } else {
-    LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_LEVEL_SERIOUS, ("dhcp_discover: could not allocate DHCP request\n"));
+      LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE, ("dhcp_discover: sendto(DISCOVER, IP_ADDR_BROADCAST, LWIP_IANA_PORT_DHCP_SERVER)\n"));
+      udp_sendto_if_src(dhcp_pcb, p_out, IP_ADDR_BROADCAST, LWIP_IANA_PORT_DHCP_SERVER, netif, IP4_ADDR_ANY);
+      LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE, ("dhcp_discover: deleting()ing\n"));
+      pbuf_free(p_out);
+      LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE, ("dhcp_discover: SELECTING\n"));
+    } else {
+      LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_LEVEL_SERIOUS, ("dhcp_discover: could not allocate DHCP request\n"));
+    }
+#if LWIP_DHCP_AUTOIP_COOP
   }
+#endif /* LWIP_DHCP_AUTOIP_COOP */
   if (dhcp->tries < 255) {
     dhcp->tries++;
   }
-#if LWIP_DHCP_AUTOIP_COOP
-  if (dhcp->tries >= LWIP_DHCP_AUTOIP_COOP_TRIES && dhcp->autoip_coop_state == DHCP_AUTOIP_COOP_STATE_OFF) {
-    dhcp->autoip_coop_state = DHCP_AUTOIP_COOP_STATE_ON;
-    autoip_start(netif);
-  }
-#endif /* LWIP_DHCP_AUTOIP_COOP */
   msecs = (u16_t)((dhcp->tries < 6 ? 1 << dhcp->tries : 60) * 1000);
   dhcp->request_timeout = (u16_t)((msecs + DHCP_FINE_TIMER_MSECS - 1) / DHCP_FINE_TIMER_MSECS);
   LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE, ("dhcp_discover(): set request timeout %"U16_F" msecs\n", msecs));
@@ -1746,7 +1730,7 @@ decode_next:
     /* make sure the string is really NULL-terminated */
     dhcp->boot_file_name[DHCP_FILE_LEN-1] = 0;
   }
-#endif /* LWIP_DHCP_BOOTP_FILE */ 
+#endif /* LWIP_DHCP_BOOTP_FILE */
   return ERR_OK;
 }
 
@@ -1825,10 +1809,11 @@ dhcp_recv(void *arg, struct udp_pcb *pcb, struct pbuf *p, const ip_addr_t *addr,
   /* message type is DHCP ACK? */
   if (msg_type == DHCP_ACK) {
     LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE, ("DHCP_ACK received\n"));
-    /* in requesting state? */
-    if (dhcp->state == DHCP_STATE_REQUESTING) {
+    /* in requesting state or just reconnected to the network? */
+    if ((dhcp->state == DHCP_STATE_REQUESTING) ||
+        (dhcp->state == DHCP_STATE_REBOOTING)) {
       dhcp_handle_ack(netif, msg_in);
-#if DHCP_DOES_ARP_CHECK
+#if DHCP_DOES_ACD_CHECK
       if ((netif->flags & NETIF_FLAG_ETHARP) != 0) {
         /* check if the acknowledged lease address is already in use */
         dhcp_check(netif);
@@ -1841,8 +1826,8 @@ dhcp_recv(void *arg, struct udp_pcb *pcb, struct pbuf *p, const ip_addr_t *addr,
       dhcp_bind(netif);
 #endif
     }
-    /* already bound to the given lease address? */
-    else if ((dhcp->state == DHCP_STATE_REBOOTING) || (dhcp->state == DHCP_STATE_REBINDING) ||
+    /* already bound to the given lease address and using it? */
+    else if ((dhcp->state == DHCP_STATE_REBINDING) ||
              (dhcp->state == DHCP_STATE_RENEWING)) {
       dhcp_handle_ack(netif, msg_in);
       dhcp_bind(netif);
diff --git a/src/core/ipv4/etharp.c b/src/core/ipv4/etharp.c
index 3065255da..c667d5dea 100644
--- a/src/core/ipv4/etharp.c
+++ b/src/core/ipv4/etharp.c
@@ -732,13 +732,6 @@ etharp_input(struct pbuf *p, struct netif *netif)
     case PP_HTONS(ARP_REPLY):
       /* ARP reply. We already updated the ARP cache earlier. */
       LWIP_DEBUGF(ETHARP_DEBUG | LWIP_DBG_TRACE, ("etharp_input: incoming ARP reply\n"));
-#if (LWIP_DHCP && DHCP_DOES_ARP_CHECK)
-      /* DHCP wants to know about ARP replies from any host with an
-       * IP address also offered to us by the DHCP server. We do not
-       * want to take a duplicate IP address on a single network.
-       * @todo How should we handle redundant (fail-over) interfaces? */
-      dhcp_arp_reply(netif, &sipaddr);
-#endif /* (LWIP_DHCP && DHCP_DOES_ARP_CHECK) */
       break;
     default:
       LWIP_DEBUGF(ETHARP_DEBUG | LWIP_DBG_TRACE, ("etharp_input: ARP unknown opcode type %"S16_F"\n", lwip_htons(hdr->opcode)));
diff --git a/src/core/netif.c b/src/core/netif.c
index 3186f9c89..6a32247af 100644
--- a/src/core/netif.c
+++ b/src/core/netif.c
@@ -80,6 +80,9 @@
 #if LWIP_DHCP
 #include "lwip/dhcp.h"
 #endif /* LWIP_DHCP */
+#if LWIP_ACD
+#include "lwip/acd.h"
+#endif /* LWIP_ACD */
 #if LWIP_IPV6_DHCP6
 #include "lwip/dhcp6.h"
 #endif /* LWIP_IPV6_DHCP6 */
@@ -1034,6 +1037,11 @@ netif_set_link_down(struct netif *netif)
 
   if (netif->flags & NETIF_FLAG_LINK_UP) {
     netif_clear_flags(netif, NETIF_FLAG_LINK_UP);
+
+#if LWIP_ACD
+    acd_network_changed(netif);
+#endif /* LWIP_ACD */
+
     NETIF_LINK_CALLBACK(netif);
 #if LWIP_NETIF_EXT_STATUS_CALLBACK
     {
diff --git a/src/include/lwip/acd.h b/src/include/lwip/acd.h
index 28ecd401d..7a60ec029 100644
--- a/src/include/lwip/acd.h
+++ b/src/include/lwip/acd.h
@@ -46,6 +46,7 @@
 
 #include "lwip/netif.h"
 #include "lwip/etharp.h"
+#include "lwip/prot/acd.h"
 
 #ifdef __cplusplus
 extern "C" {
@@ -62,13 +63,10 @@ extern "C" {
 /**
  * Callback function: Handle conflict information from ACD module
  *
- * conflict = 0 -> no conflict found in checking phase
- * conflict = 1 -> conflict found -> can be in checking or bound phase
- *
- * @param netif    network interface to handle conflict information on
- * @param conflict 0 -> no conflict; 1 -> conflict
+ * @param netif   network interface to handle conflict information on
+ * @param state   acd_callback_enum_t
  */
-typedef void (*acd_conflict_callback_t)(struct netif *netif, int conflict);
+typedef void (*acd_conflict_callback_t)(struct netif *netif, acd_callback_enum_t state);
 
 /** ACD state information per netif */
 struct acd
@@ -76,7 +74,7 @@ struct acd
   /** the currently selected, probed, announced or used IP-Address */
   ip4_addr_t ipaddr;
   /** current ACD state machine state */
-  u8_t state;
+  acd_state_enum_t state;
   /** sent number of probes or announces, dependent on state */
   u8_t sent_num;
   /** ticks to wait, tick is ACD_TMR_INTERVAL long */
@@ -107,6 +105,7 @@ void acd_arp_reply(struct netif *netif, struct etharp_hdr *hdr);
 
 void acd_tmr(void);
 
+void acd_network_changed(struct netif *netif);
 
 
 #ifdef __cplusplus
diff --git a/src/include/lwip/dhcp.h b/src/include/lwip/dhcp.h
index c78aa0baf..91109bfa1 100644
--- a/src/include/lwip/dhcp.h
+++ b/src/include/lwip/dhcp.h
@@ -112,9 +112,7 @@ void dhcp_stop(struct netif *netif);
 void dhcp_release_and_stop(struct netif *netif);
 void dhcp_inform(struct netif *netif);
 void dhcp_network_changed(struct netif *netif);
-#if DHCP_DOES_ARP_CHECK
-void dhcp_arp_reply(struct netif *netif, const ip4_addr_t *addr);
-#endif
+
 u8_t dhcp_supplied_address(const struct netif *netif);
 /* to be called every minute */
 void dhcp_coarse_tmr(void);
diff --git a/src/include/lwip/opt.h b/src/include/lwip/opt.h
index 5aa316d59..f086e9a66 100644
--- a/src/include/lwip/opt.h
+++ b/src/include/lwip/opt.h
@@ -924,10 +924,10 @@
 #endif /* !LWIP_IPV4 */
 
 /**
- * DHCP_DOES_ARP_CHECK==1: Do an ARP check on the offered address.
+ * DHCP_DOES_ACD_CHECK==1: Perform address conflict detection on the dhcp address.
  */
-#if !defined DHCP_DOES_ARP_CHECK || defined __DOXYGEN__
-#define DHCP_DOES_ARP_CHECK             (LWIP_DHCP && LWIP_ARP)
+#if !defined DHCP_DOES_ACD_CHECK || defined __DOXYGEN__
+#define DHCP_DOES_ACD_CHECK             0
 #endif
 
 /**
@@ -1033,7 +1033,7 @@
   * LWIP_ACD==1: Enable ACD module. ACD module is needed when using AUTOIP.
   */
 #if !defined LWIP_ACD || defined __DOXYGEN__
-#define LWIP_ACD                     LWIP_AUTOIP
+#define LWIP_ACD                     (LWIP_AUTOIP || DHCP_DOES_ACD_CHECK)
 #endif
 #if !LWIP_IPV4
 /* disable ACD when IPv4 is disabled */
diff --git a/src/include/lwip/prot/acd.h b/src/include/lwip/prot/acd.h
index 6e4ee4367..ada87ab92 100644
--- a/src/include/lwip/prot/acd.h
+++ b/src/include/lwip/prot/acd.h
@@ -62,7 +62,14 @@ typedef enum {
   ACD_STATE_ANNOUNCE_WAIT,
   ACD_STATE_ANNOUNCING,
   ACD_STATE_ONGOING,
-} autoip_state_enum_t;
+  ACD_STATE_RATE_LIMIT,
+} acd_state_enum_t;
+
+typedef enum {
+  ACD_IP_OK,            /* IP address is good, no conflicts found in checking state */
+  ACD_RESTART_CLIENT,   /* Conflict found -> the client should try again */
+  ACD_DECLINE,          /* Decline the received IP address (rate limiting)*/
+} acd_callback_enum_t;
 
 #ifdef __cplusplus
 }
-- 
2.18.0

