From ede5d9dcbe01e4e01321ba80158c90c0dceebe0f Mon Sep 17 00:00:00 2001
From: Jasper Verschueren <jasper.verschueren@apart-audio.com>
Date: Thu, 13 Sep 2018 17:02:21 +0200
Subject: [PATCH] ACD module added + update and improve DHCP + AUTOIP behavior

Squashed commit of the following:

commit 2d98d8e2ef1941c3824ffb874f1e529d284667fc
Author: Jasper Verschueren <jasper.verschueren@apart-audio.com>
Date:   Thu Sep 13 16:15:06 2018 +0200

    AUTOIP: correct functionality autoip_supplied_address

    It does not mean that if autoip is bound, it also supplied the
    netif address. A check is added.

commit 2ca0a2183991ef73860c4207d95799b37acc64cc
Author: Jasper Verschueren <jasper.verschueren@apart-audio.com>
Date:   Thu Sep 13 16:06:49 2018 +0200

    AUTOIP: keep using the same link local address as much as possible

    Only calculate a new link local address at start up or when a
    conflict occured. On link up or down -> keep same address.

    TODO: in the future a function for persistent storage should be
    added.

commit aa70a693351e4c898aa28d8521308794614838f1
Author: Jasper Verschueren <jasper.verschueren@apart-audio.com>
Date:   Thu Sep 13 14:43:14 2018 +0200

    ACD, AUTOIP & DHCP: make link up & down functions where needed

    When the link goes down or up the approriate functions should be
    stopped or started again. To accomodate this, network_changed is
    adjusted to network_changed_link_up and network_changed_link_down.
    DHCP does not need to control AUTOIP. AUTOIP can take care of
    itself. The only thing DHCP needs to do is starting it when
    discovering is failing. The AUTOIP state variable is removed from
    DHCP.

commit ad469eb006b47f8a8c37f7c0de0216f47a8c19c7
Author: Jasper Verschueren <jasper.verschueren@apart-audio.com>
Date:   Thu Sep 13 11:25:58 2018 +0200

    ACD: add address change listener + passive conflict detection mode

    In the case their previously was a LL address on a netif that
    is now configured with a routable address, we want the LL
    address to be able to keep receiving packets.
    for as long as the LL address is available on the interface it
    should do ongoing conflict detection. But we cannot defend when
    the LL address is not the netif address any more.
    An address change listener is added to detect when an ACD module
    needs to go from active ongoing conflict detection to passive.
    When a conflict is detected autoip is stopped and will not be able
    to receive packets any more. Because we have a valid routable
    address on the netif, autoip is not restarted.

commit 07c4ec20cea78e2b4a6f5599569abaf075619c62
Author: Jasper Verschueren <jasper.verschueren@apart-audio.com>
Date:   Tue Sep 11 16:25:37 2018 +0200

    ACD: make module reusable within a netif

    DHCP and AUTOIP both have ACD running simultaniously. The ACD
    struct is added to the DHCP and AUTOIP structs. In the netif a list
    of ACD modules is kept to loop over if functions need to perform
    some action on all ACD modules (for example tmr function). With
    acd_add a module can be added to the list. ACD_FOREACH loops over
    the list similar to NETIF_FOREACH.

commit ee3b4585b7768f5353dd80190a2929bad45f7ff4
Author: Jasper Verschueren <jasper.verschueren@apart-audio.com>
Date:   Mon Sep 10 16:36:18 2018 +0200

    etharp & acd: add probe and announce functions to etharp.

    The new probe and announce functions are independent of
    netif->ip_addr. This means we can keep the LL address working
    while we start to probe for the newly received routable address.
    The netif->ip_addr does not need to be any for probing to work
    with this patch.

commit 7d3032bae8f1b8081368a807682388eb642729e0
Author: Jasper Verschueren <jasper.verschueren@apart-audio.com>
Date:   Mon Sep 3 13:35:51 2018 +0200

    autoip -> unused random function removed + small comment update

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

    ACD: update comment in header

commit 4491842991c90b3a58fa327f70aa42f04174546b
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 71f668aa7583354e132c20b3b50ba2c86bf08738
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 0da16604ec079195533f2591f0d0f04bdf212a72
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 a89a0601a251acb14abe270116f38c6d25c2d7a9
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 853afb448ba35c6e2b35e8238c9c367c599dece7
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 e28b4766bdef69e76f6170c470c93f5b251c579a
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.

commit 52193a0f5d13e8786a4db2fff1f1a8f1367a4eba
Author: Jasper Verschueren <jasper.verschueren@apart-audio.com>
Date:   Mon Aug 27 16:05:22 2018 +0200

    Issue 2 + 3 from Comment #5, task #13508 solved

    Removed C++ comments and // ----... marks as requested.

commit 7faaf61275d67ccfb88ea7e26c249428c3088536
Author: Jasper Verschueren <jasper.verschueren@apart-audio.com>
Date:   Mon Aug 27 15:55:24 2018 +0200

    Issue 1 from Comment #5, task #13508 solved

    Comments added to clarify how arp messages are handled.

commit 9348aea22623b705759fd30b873f06a50a104d16
Author: Jasper Verschueren <jasper.verschueren@apart-audio.com>
Date:   Tue Jul 24 11:45:35 2018 +0200

    acd: add comment for callback function

commit 49fdd1177c05b74d49fa179564dcaa5e650adbcc
Author: Jasper Verschueren <jasper.verschueren@apart-audio.com>
Date:   Tue Jul 24 11:42:59 2018 +0200

    autoip: Add debugging output and complete comments

commit 591856b82c029687a657a1b1ccc674522e6f4be0
Author: Jasper Verschueren <jasper.verschueren@apart-audio.com>
Date:   Tue Jul 24 11:25:18 2018 +0200

    Removed autoip from timeouts.c

    We do not need a timer anymore. All timing is part of acd.

commit 3b3272fdd14015cfca0b3b6d149505b1cf0e36b6
Author: Jasper Verschueren <jasper.verschueren@apart-audio.com>
Date:   Tue Jul 24 11:22:32 2018 +0200

    autoip_network_changed -> first bring down the netif and then acd

    Make it do what it says in the comment

commit 77b0ccf96efd22774279c6f9b5bade18c5e42c59
Author: Jasper Verschueren <jasper.verschueren@apart-audio.com>
Date:   Tue Jul 24 11:21:23 2018 +0200

    Simplified the autoIP states + update autoip_start()

commit 96e0581d36857f8b70c4b4cce4fb323fd3dd51ab
Author: Jasper Verschueren <jasper.verschueren@apart-audio.com>
Date:   Tue Jul 24 11:10:48 2018 +0200

    Deleted unused variables, functions and defines from autoip module

    The functionality is now embedded in the acd module so can be
    removed from autoip.

commit 9296e2ebb4b51019aaccfc47e8b9f51b265d37cd
Author: Jasper Verschueren <jasper.verschueren@apart-audio.com>
Date:   Tue Jul 24 11:07:34 2018 +0200

    Bug fix acd -> rate limiting needs to be after MAX conflicts

    It was only after MAX conflicts + 1.

commit c55e16903c045d0ea84336b50eccbf24d3d097e9
Author: Jasper Verschueren <jasper.verschueren@apart-audio.com>
Date:   Mon Jul 23 17:52:45 2018 +0200

    Small intruduction to module added

commit d1498a37293bd9f97f6b938b48e5980ab0a01bd1
Author: Jasper Verschueren <jasper.verschueren@apart-audio.com>
Date:   Mon Jul 23 17:49:58 2018 +0200

    Copyright information added

    I based the copyright text on the other files. I'm not sure this
    is how its done. I kept Dominik in their because I copied quite
    some code from the autoip module.

commit 359a845ef5e73061832069f364b370634ee0b071
Author: Jasper Verschueren <jasper.verschueren@apart-audio.com>
Date:   Mon Jul 23 17:08:44 2018 +0200

    Fixing comments in acd.c

commit 100d72549d0ef44157143d031848a727f5dfbe69
Author: Jasper Verschueren <jasper.verschueren@apart-audio.com>
Date:   Mon Jul 23 15:43:40 2018 +0200

    Probe wait time and Probe interval time made random via lwip_rand

    According to the RFC a random amount of time needs to be waited
    before probing can be started also a random amount of time
    needs to be waited between the probes.
    The random time is calculated via the LWIP_RAND function (see
    lwipopts and sys_arch).

commit f7f037c32e9416f8b803c3c7af617b871b55ee35
Author: Jasper Verschueren <jasper.verschueren@apart-audio.com>
Date:   Tue Jul 17 16:56:48 2018 +0200

    autoip will start probing again when the network has changed

commit 1f40f6274195f24aa1b05caf82b79285ad189c2a
Author: Jasper Verschueren <jasper.verschueren@apart-audio.com>
Date:   Tue Jul 17 16:55:47 2018 +0200

    Duplicate code removed -> already present in autoip_start

commit be59431271da862a8ca330dbca638842c87765bc
Author: Jasper Verschueren <jasper.verschueren@apart-audio.com>
Date:   Tue Jul 17 16:52:49 2018 +0200

    add rate limiting to acd when too many conflicts are detected.

    When more then max conflicts are detected during device on time,
    the rate at which probing for a new address starts is limited.
    For clarity we combined the conflict counting and the callback into
    a new function acd_restart.

commit 91448455e95edb24c1f418c341b6fb306391f4f1
Author: Jasper Verschueren <jasper.verschueren@apart-audio.com>
Date:   Tue Jul 17 15:50:01 2018 +0200

    ARP packet conflict detection added.

    If a conflict is detected between the begin until after announce
    wait, a new address needs to be chosen immediatly.
    This can occur in two situations:
    1) another host already has this ip address
    2) another host is also probing for the same address.
    If a conflict is detected during announcing or during the ongoing
    conflict detection, we defend our ip address once. If a second
    conflict occurs during defend interval, we take another ip address.
    If not we can keep our address and connections.

    When a conflict occurs and it's decided we need a new address,
    autoip is simply restarted. To do: test if a acd stop is needed.
    This will become more important when DHCP is added I believe.

commit 65f47ba9444d8b9f767dc908319579323eeb8664
Author: Jasper Verschueren <jasper.verschueren@apart-audio.com>
Date:   Tue Jul 17 15:33:44 2018 +0200

    Update ACD state machine with PROBE_WAIT and and ANNOUNCE_WAIT

    By adding the ANNOUNCE_WAIT state, we could remove duplicate code
    that was present in the original autoip state implementation.
    But because we cannot directly go to ANNOUNCING we needed this
    extra state during the wait period. It also makes the different
    states clearer.
    Their is no need to number the enum because the compiler takes
    care of this standard numbering.
    Also a indent issue on the state machine code is solved here.

commit 2d9f4414c7b1f2ed35c0b5cea78dabb9c9afee77
Author: Jasper Verschueren <jasper.verschueren@apart-audio.com>
Date:   Tue Jul 17 15:28:33 2018 +0200

    Added an extra check for etharp responses.

    Their is no need to answer a request that is initiated by ourselves.
    In my test case, a conflicting request would be seen here as a
    request needing a reply. Which off course isn't needed at all.
    The acd module will let the requester know that it is using our
    ip address.
    I could not think of a reason not to add this extra check but please
    check if this doesn't break other functionality.

commit f84cc1dba4061219bd1aadb97bd340278db07cd7
Author: Jasper Verschueren <jasper.verschueren@apart-audio.com>
Date:   Tue Jul 17 15:27:13 2018 +0200

    Redirect all incoming ARP messages to the ACD module.

    The ACD module will scan the packets and react on conflicts.
    autoip does not need the arp packets any more.

commit 9faf266993cc2df0b9434720b59b5922f17d7d33
Author: Jasper Verschueren <jasper.verschueren@apart-audio.com>
Date:   Tue Jul 17 10:08:39 2018 +0200

    Initiate acd from autoip implementation.

    The state machine in autoip is now replaced by the acd module.

commit 40a5a40d911c98e6ee8566c0adce7716f26f20e0
Author: Jasper Verschueren <jasper.verschueren@apart-audio.com>
Date:   Tue Jul 17 10:05:59 2018 +0200

    Add simple acd state machine to timer function.

    This state machine will be started from acd_start and will go
    through the probing and the announcing state. At this moment the
    timings are made fixed for easy debugging. Will be made variable
    afterwards.

commit 0a2629bf1f7942e80ec11bcc3e163ac1a2b9a580
Author: Jasper Verschueren <jasper.verschueren@apart-audio.com>
Date:   Tue Jul 17 09:44:19 2018 +0200

    Disable gratuitous ARP send from netif_issue_report when acd is enabled

    The acd module needs to be fully under control on what arp messages are
    send. It wouldn't be a clean solution if we kept this announce as one
    of the announce messages.
    So when ACD is enabled, this message will not be send.

commit a7b9a4d5039168723c2043677176c5c8fc69f8df
Author: Jasper Verschueren <jasper.verschueren@apart-audio.com>
Date:   Tue Jul 17 09:42:46 2018 +0200

    ARP probe and announce functions added.

    Both use etharp_requests to send out an arp message.

commit e2f1ce3fa84e2ce01bc944d8049412e156cd7cc7
Author: Jasper Verschueren <jasper.verschueren@apart-audio.com>
Date:   Tue Jul 17 09:26:07 2018 +0200

    Add conflict callback mechanism to acd module

    When starting the acd with acd_start, a conflict callback has to be
    provided. After probing when no conflict is detected, the callback
    is called with the conflict variable on 0. From the moment a conflict
    is detected, it will be called set to 1.
    By not making a fixed link between acd, autoip and dhcp but working
    with callback functions the user (caller) can easily use it for
    fixed ip too. This keeps the acd module completely independent from
    the other application layer protocols.

commit 21e7995888d2941601e99873ee129d1ea927e3f7
Author: Jasper Verschueren <jasper.verschueren@apart-audio.com>
Date:   Mon Jul 16 14:41:52 2018 +0200

    acd_stop function added.

    When calling this function the acd state machine will be put in
    ACD_STATE_OFF. This will disable the acd functionality until
    acd_start is called.

commit 503037fb462504e46a14ce7486bc763dbc6a8690
Author: Jasper Verschueren <jasper.verschueren@apart-audio.com>
Date:   Mon Jul 16 14:35:48 2018 +0200

    acd_start function added

    This function will allocate a struct for the acd module if one wasn't
    added with acd_set_struct. It will then initialize the acd struct
    and start the probe wait timer (for now fixed).

commit e439f6dffc44e93078a2976783bdebfe17304d8c
Author: Jasper Verschueren <jasper.verschueren@apart-audio.com>
Date:   Mon Jul 16 14:22:39 2018 +0200

    ACD_DEBUG define added for enabling / disabling debug messages.

commit 80d33e1eaf092934ace1045fac096464cd5be5e9
Author: Jasper Verschueren <jasper.verschueren@apart-audio.com>
Date:   Mon Jul 16 11:39:37 2018 +0200

    ACD struct added together with set struct function

    ACD needs some variables: state, timing, probe counter etc.
    We added the struct as netif data. For now one ACD module per netif
    is enough. We found some unclarities about ACD with multiple IP
    address on one interface. For now, ACD will only be implemented for
    the IP address that is going to be used / is being used on the netif.

commit 2c4cca36744973318c3efe7cbae6384b52dc71a8
Author: Jasper Verschueren <jasper.verschueren@apart-audio.com>
Date:   Mon Jul 16 11:35:06 2018 +0200

    Add acd timer to timeouts.c with interval of 100ms.

    This timer will be used for time to wait counting etc.
    Very similar to the auto ip timer.

commit e2ed447e00c4df790df21509acb4ab09b5b79e66
Author: Jasper Verschueren <jasper.verschueren@apart-audio.com>
Date:   Fri Jul 13 16:56:39 2018 +0200

    Add ACD protocol definitions

commit 445733214460eae18817556439bed979e9b3747c
Author: Jasper Verschueren <jasper.verschueren@apart-audio.com>
Date:   Fri Jul 13 16:07:58 2018 +0200

    Add c++ option (to use C code module in c++)

commit 63d78bc1f77ac6698c3d663a1c67b40a0c297125
Author: Jasper Verschueren <jasper.verschueren@apart-audio.com>
Date:   Fri Jul 13 15:57:34 2018 +0200

    Added on off option for ACD module

commit d5ec4b69eb4175d4dd569bcfc80ae0e192780015
Author: Jasper Verschueren <jasper.verschueren@apart-audio.com>
Date:   Fri Jul 13 15:09:40 2018 +0200

    ACD IPv4 module files added

    The files for IPv4 ACD or Address Conflict Detection are added
    to the lwip source.
---
 src/core/init.c                |   4 +-
 src/core/ipv4/acd.c            | 503 +++++++++++++++++++++++++++++++++
 src/core/ipv4/autoip.c         | 381 +++++++------------------
 src/core/ipv4/dhcp.c           | 185 +++++-------
 src/core/ipv4/etharp.c         |  69 ++++-
 src/core/netif.c               |  27 +-
 src/core/timeouts.c            |   8 +-
 src/include/lwip/acd.h         | 117 ++++++++
 src/include/lwip/autoip.h      |  20 +-
 src/include/lwip/dhcp.h        |  18 +-
 src/include/lwip/etharp.h      |   5 +
 src/include/lwip/netif.h       |   6 +
 src/include/lwip/opt.h         |  38 ++-
 src/include/lwip/prot/acd.h    |  91 ++++++
 src/include/lwip/prot/autoip.h |  19 +-
 15 files changed, 1044 insertions(+), 447 deletions(-)
 create mode 100644 src/core/ipv4/acd.c
 create mode 100644 src/include/lwip/acd.h
 create mode 100644 src/include/lwip/prot/acd.h

diff --git a/src/core/init.c b/src/core/init.c
index b3737a356..7a312a97d 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
new file mode 100644
index 000000000..e1d597ccb
--- /dev/null
+++ b/src/core/ipv4/acd.c
@@ -0,0 +1,503 @@
+/**
+ * @file
+ *
+ * ACD IPv4 Address Conflict Detection
+ *
+ * This is an IPv4 address conflict detection implementation for the lwIP TCP/IP
+ * stack. It aims to be conform to RFC5227.
+ *
+ * @defgroup acd ACD
+ * @ingroup ip4
+ * ACD related functions
+ * USAGE:
+ *
+ * define @ref LWIP_ACD 1 in your lwipopts.h
+ * Options:
+ * ACD_TMR_INTERVAL msecs,
+ *   I recommend a value of 100. The value must divide 1000 with a remainder almost 0.
+ *   Possible values are 1000, 500, 333, 250, 200, 166, 142, 125, 111, 100 ....
+ *
+ * For fixed IP:
+ * - call acd_start after selecting an IP address. The caller will be informed
+ *   on conflict status via the callback function.
+ *
+ * With AUTOIP:
+ * - will be called from the autoip module. No extra's needed.
+ *
+ * With DHCP:
+ * - enable DHCP_DOES_ACD_CHECK. Then it will be called from the dhcp module.
+ *   No extra's needed.
+ *
+ * @see netifapi_acd
+ */
+
+/*
+ *
+ * Copyright (c) 2007 Dominik Spies <kontakt@dspies.de>
+ * Copyright (c) 2018 Jasper Verschueren <jasper.verschueren@apart-audio.com>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ *    this list of conditions and the following disclaimer.
+ * 2. 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.
+ * 3. 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.
+ *
+ * Author: Jasper Verschueren <jasper.verschueren@apart-audio.com>
+ * Author: Dominik Spies <kontakt@dspies.de>
+ */
+
+#include "lwip/opt.h"
+
+/* don't build if not configured for use in lwipopts.h */
+#if LWIP_IPV4 && LWIP_ACD
+
+#include "lwip/acd.h"
+#include "lwip/prot/acd.h"
+
+/* Define good random function (LWIP_RAND) in lwipopts.h */
+#define ACD_RANDOM_PROBE_WAIT     (LWIP_RAND() % \
+                                  (PROBE_WAIT * ACD_TICKS_PER_SECOND))
+
+#define ACD_RANDOM_PROBE_INTERVAL ((LWIP_RAND() % ((PROBE_MAX - PROBE_MIN) \
+                                   * ACD_TICKS_PER_SECOND)) + \
+                                   (PROBE_MIN * ACD_TICKS_PER_SECOND ))
+
+/* Function definitions */
+static void acd_restart(struct netif *netif, struct acd *acd);
+static void acd_handle_arp_conflict(struct netif *netif, struct acd *acd);
+static void acd_put_in_passive_mode(struct netif *netif, struct acd *acd);
+
+/**
+ * @ingroup acd
+ * Add ACD client to the client list and initialize callback function
+ *
+ * @param netif                 network interface on which to start the acd
+ *                              client
+ * @param acd                   acd module to be added to the list
+ * @param acd_conflict_callback callback to be called when conflict information
+ *                              is available
+ */
+err_t
+acd_add(struct netif *netif, struct acd *acd,
+         acd_conflict_callback_t acd_conflict_callback)
+{
+  struct acd *acd2;
+
+  /* Set callback */
+  LWIP_ASSERT_CORE_LOCKED();
+  LWIP_ASSERT("acd_conflict_callback != NULL", acd_conflict_callback != NULL);
+  acd->acd_conflict_callback = acd_conflict_callback;
+
+  /* Check if the acd struct is already added */
+  for (acd2 = netif->acd_list; acd2 != NULL; acd2 = acd2->next) {
+    if (acd2 == acd) {
+      LWIP_DEBUGF(ACD_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE,
+                  ("acd_add(): acd already added to list\n"));
+      return ERR_OK;
+    }
+  }
+
+  /* add acd struct to the list */
+  acd->next = netif->acd_list;
+  netif->acd_list = acd;
+
+  return ERR_OK;
+}
+
+/**
+ * @ingroup acd
+ * Start ACD client
+ *
+ * @param netif   network interface on which to start the acd client
+ * @param acd     acd module to start
+ * @param ipaddr  ip address to perform acd on
+ */
+err_t
+acd_start(struct netif *netif, struct acd *acd, ip4_addr_t ipaddr)
+{
+  err_t result = ERR_OK;
+
+  LWIP_DEBUGF(ACD_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE,
+              ("acd_start(netif=%p) %c%c%"U16_F"\n",
+              (void *)netif, netif->name[0],
+               netif->name[1], (u16_t)netif->num));
+
+  /* init probing state */
+  acd->sent_num = 0;
+  acd->lastconflict = 0;
+  ip4_addr_copy(acd->ipaddr, ipaddr);
+  acd->state = ACD_STATE_PROBE_WAIT;
+
+  acd->ttw = (u16_t)(ACD_RANDOM_PROBE_WAIT);
+
+  return result;
+}
+
+/**
+ * @ingroup acd
+ * Stop ACD client
+ *
+ * @param netif network interface on which to stop the ACD client
+ * @param acd   acd module to stop
+ */
+err_t
+acd_stop(struct netif *netif, struct acd *acd)
+{
+  LWIP_DEBUGF(ACD_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE, ("acd_stop\n"));
+
+  if (acd != NULL) {
+    acd->state = ACD_STATE_OFF;
+  }
+  return ERR_OK;
+}
+
+/**
+ * @ingroup acd
+ * Inform the ACD modules when the link goes down
+ *
+ * @param netif network interface on which to inform the ACD clients
+ */
+void
+acd_network_changed_link_down(struct netif *netif)
+{
+  struct acd *acd;
+  /* loop over the acd's*/
+  ACD_FOREACH(acd, netif->acd_list) {
+    acd_stop(netif, acd);
+  }
+}
+
+/**
+ * Has to be called in loop every ACD_TMR_INTERVAL milliseconds
+ */
+void
+acd_tmr(void)
+{
+  struct netif *netif;
+  struct acd *acd;
+  /* loop through netif's */
+  NETIF_FOREACH(netif) {
+    ACD_FOREACH(acd, netif->acd_list) {
+      if (acd->lastconflict > 0) {
+        acd->lastconflict--;
+      }
+
+      LWIP_DEBUGF(ACD_DEBUG | LWIP_DBG_TRACE,
+                  ("acd_tmr() ACD-State: %"U16_F", ttw=%"U16_F"\n",
+                   (u16_t)(acd->state), acd->ttw));
+
+      if (acd->ttw > 0) {
+        acd->ttw--;
+      }
+
+      switch (acd->state) {
+        case ACD_STATE_PROBE_WAIT:
+        case ACD_STATE_PROBING:
+          if (acd->ttw == 0) {
+            acd->state = ACD_STATE_PROBING;
+            etharp_acd_probe(netif, &acd->ipaddr);
+            LWIP_DEBUGF(ACD_DEBUG | LWIP_DBG_TRACE,
+                        ("acd_tmr() PROBING Sent Probe\n"));
+            acd->sent_num++;
+            if (acd->sent_num >= PROBE_NUM) {
+              /* Switch to ANNOUNCE_WAIT: last probe is sent*/
+              acd->state = ACD_STATE_ANNOUNCE_WAIT;
+
+              acd->sent_num = 0;
+
+              /* calculate time to wait before announcing */
+              acd->ttw = (u16_t)(ANNOUNCE_WAIT * ACD_TICKS_PER_SECOND);
+            } else {
+              /* calculate time to wait to next probe */
+              acd->ttw = (u16_t)(ACD_RANDOM_PROBE_INTERVAL);
+            }
+          }
+          break;
+
+        case ACD_STATE_ANNOUNCE_WAIT:
+        case ACD_STATE_ANNOUNCING:
+          if (acd->ttw == 0) {
+            if (acd->sent_num == 0) {
+              acd->state = ACD_STATE_ANNOUNCING;
+
+              /* let acd user know that the address is good and can be used */
+              acd->acd_conflict_callback(netif, ACD_IP_OK);
+
+              LWIP_DEBUGF(ACD_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE,
+                    ("acd_tmr(): changing state to ANNOUNCING: %"U16_F".%"U16_F".%"U16_F".%"U16_F"\n",
+                     ip4_addr1_16(&acd->ipaddr), ip4_addr2_16(&acd->ipaddr),
+                     ip4_addr3_16(&acd->ipaddr), ip4_addr4_16(&acd->ipaddr)));
+            }
+
+            etharp_acd_announce(netif, &acd->ipaddr);
+            LWIP_DEBUGF(ACD_DEBUG | LWIP_DBG_TRACE,
+                        ("acd_tmr() ANNOUNCING Sent Announce\n"));
+            acd->ttw = ANNOUNCE_INTERVAL * ACD_TICKS_PER_SECOND;
+            acd->sent_num++;
+
+            if (acd->sent_num >= ANNOUNCE_NUM) {
+              acd->state = ACD_STATE_ONGOING;
+              acd->sent_num = 0;
+              acd->ttw = 0;
+              LWIP_DEBUGF(ACD_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE,
+                    ("acd_tmr(): changing state to ONGOING: %"U16_F".%"U16_F".%"U16_F".%"U16_F"\n",
+                     ip4_addr1_16(&acd->ipaddr), ip4_addr2_16(&acd->ipaddr),
+                     ip4_addr3_16(&acd->ipaddr), ip4_addr4_16(&acd->ipaddr)));
+            }
+          }
+          break;
+
+        case ACD_STATE_RATE_LIMIT:
+          if (acd->ttw == 0) {
+            /* acd should be stopped because ipaddr isn't valid any more */
+            acd_stop(netif, acd);
+            /* 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;
+      }
+    }
+  }
+}
+
+/**
+ * Restarts the acd module
+ *
+ * The number of conflicts is increased and the upper layer is informed.
+ */
+static void
+acd_restart(struct netif *netif, struct acd *acd)
+{
+  /* increase conflict counter. */
+  acd->num_conflicts++;
+
+  /* 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_restart(): rate limiting initiated. too many conflicts\n"));
+  }
+  else {
+    /* acd should be stopped because ipaddr isn't valid any more */
+    acd_stop(netif, acd);
+    /* 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);
+  }
+}
+
+/**
+ * Handles every incoming ARP Packet, called by etharp_input().
+ *
+ * @param netif network interface to use for acd processing
+ * @param hdr   Incoming ARP packet
+ */
+void
+acd_arp_reply(struct netif *netif, struct etharp_hdr *hdr)
+{
+  struct acd *acd;
+  ip4_addr_t sipaddr, dipaddr;
+  struct eth_addr netifaddr;
+  SMEMCPY(netifaddr.addr, netif->hwaddr, ETH_HWADDR_LEN);
+
+  /* Copy struct ip4_addr_wordaligned to aligned ip4_addr, to support
+   * compilers without structure packing (not using structure copy which
+   * breaks strict-aliasing rules).
+   */
+  IPADDR_WORDALIGNED_COPY_TO_IP4_ADDR_T(&sipaddr, &hdr->sipaddr);
+  IPADDR_WORDALIGNED_COPY_TO_IP4_ADDR_T(&dipaddr, &hdr->dipaddr);
+
+  LWIP_DEBUGF(ACD_DEBUG | LWIP_DBG_TRACE, ("acd_arp_reply()\n"));
+
+  /* loop over the acd's*/
+  ACD_FOREACH(acd, netif->acd_list) {
+    switch(acd->state) {
+      case ACD_STATE_OFF:
+      case ACD_STATE_RATE_LIMIT:
+        /* do nothing */
+        break;
+
+      case ACD_STATE_PROBE_WAIT:
+      case ACD_STATE_PROBING:
+      case ACD_STATE_ANNOUNCE_WAIT:
+        /* RFC 5227 Section 2.1.1:
+         * from beginning to after ANNOUNCE_WAIT seconds we have a conflict if
+         * ip.src == ipaddr (someone is already using the address)
+         * OR
+         * ip.dst == ipaddr && hw.src != own hwaddr (someone else is probing it)
+         */
+        if ((ip4_addr_cmp(&sipaddr, &acd->ipaddr)) ||
+            (ip4_addr_isany_val(sipaddr) &&
+             ip4_addr_cmp(&dipaddr, &acd->ipaddr) &&
+             !eth_addr_cmp(&netifaddr, &hdr->shwaddr))) {
+          LWIP_DEBUGF(ACD_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE | LWIP_DBG_LEVEL_WARNING,
+                      ("acd_arp_reply(): Probe Conflict detected\n"));
+          acd_restart(netif, acd);
+        }
+        break;
+
+      case ACD_STATE_ANNOUNCING:
+      case ACD_STATE_ONGOING:
+      case ACD_STATE_PASSIVE_ONGOING:
+        /* RFC 5227 Section 2.4:
+         * in any state we have a conflict if
+         * ip.src == ipaddr && hw.src != own hwaddr (someone is using our address)
+         */
+        if (ip4_addr_cmp(&sipaddr, &acd->ipaddr) &&
+            !eth_addr_cmp(&netifaddr, &hdr->shwaddr)) {
+          LWIP_DEBUGF(ACD_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE | LWIP_DBG_LEVEL_WARNING,
+                      ("acd_arp_reply(): Conflicting ARP-Packet detected\n"));
+          acd_handle_arp_conflict(netif, acd);
+        }
+        break;
+    }
+  }
+}
+
+/**
+ * Handle a IP address conflict after an ARP conflict detection
+ */
+static void
+acd_handle_arp_conflict(struct netif *netif, struct acd *acd)
+{
+  /* RFC5227, 2.4 "Ongoing Address Conflict Detection and Address Defense"
+     allows three options where:
+     a) means retreat on the first conflict,
+     b) allows to keep an already configured address when having only one
+        conflict in DEFEND_INTERVAL seconds and
+     c) the host will not give up it's address and defend it indefinitely
+
+     We use option b) when the acd module represents the netif address, since it
+     helps to improve the chance that one of the two conflicting hosts may be
+     able to retain its address. while we are flexible enough to help network
+     performance
+
+     We use option a) when the acd module does not represent the netif address,
+     since we cannot have the acd module announcing or restarting. This
+     situation occurs for the LL acd module when a routable address is used on
+     the netif but the LL address is still open in the background. */
+
+  if (acd->state == ACD_STATE_PASSIVE_ONGOING) {
+    /* Imediatly back off on a conflict. */
+    LWIP_DEBUGF(ACD_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE,
+      ("acd_handle_arp_conflict(): conflict when we are in passive mode -> back off\n"));
+    acd_stop(netif, acd);
+    acd->acd_conflict_callback(netif, ACD_DECLINE);
+  }
+  else {
+    if (acd->lastconflict > 0) {
+      /* retreat, there was a conflicting ARP in the last DEFEND_INTERVAL seconds */
+      LWIP_DEBUGF(ACD_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE,
+        ("acd_handle_arp_conflict(): conflict within DEFEND_INTERVAL -> retreating\n"));
+
+      /* Active TCP sessions are aborted when removing the ip address but a bad
+       * connection was inevitable anyway with conflicting hosts */
+       acd_restart(netif, acd);
+    } else {
+      LWIP_DEBUGF(ACD_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE,
+          ("acd_handle_arp_conflict(): we are defending, send ARP Announce\n"));
+      etharp_acd_announce(netif, &acd->ipaddr);
+      acd->lastconflict = DEFEND_INTERVAL * ACD_TICKS_PER_SECOND;
+    }
+  }
+}
+
+/**
+ * Put the acd module in passive ongoing conflict detection.
+ */
+static void
+acd_put_in_passive_mode(struct netif *netif, struct acd *acd)
+{
+  switch(acd->state) {
+    case ACD_STATE_OFF:
+    case ACD_STATE_PASSIVE_ONGOING:
+      /* do nothing */
+      break;
+
+    case ACD_STATE_PROBE_WAIT:
+    case ACD_STATE_PROBING:
+    case ACD_STATE_ANNOUNCE_WAIT:
+    case ACD_STATE_RATE_LIMIT:
+      acd_stop(netif, acd);
+      acd->acd_conflict_callback(netif, ACD_DECLINE);
+      break;
+
+    case ACD_STATE_ANNOUNCING:
+    case ACD_STATE_ONGOING:
+      acd->state = ACD_STATE_PASSIVE_ONGOING;
+      LWIP_DEBUGF(ACD_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE,
+        ("acd_put_in_passive_mode()\n"));
+      break;
+  }
+}
+
+/**
+ * @ingroup acd
+ * Inform the ACD modules of address changes
+ *
+ * @param netif     network interface on which the address is changing
+ * @param old_addr  old ip address
+ * @param new_addr  new ip address
+ */
+void
+acd_netif_ip_addr_changed(struct netif *netif, const ip_addr_t *old_addr,
+                          const ip_addr_t *new_addr)
+{
+  struct acd *acd;
+
+  LWIP_DEBUGF(ACD_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE,
+    ("acd_netif_ip_addr_changed(): Address changed\n"));
+
+  LWIP_DEBUGF(ACD_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE,
+    ("acd_netif_ip_addr_changed(): old address = %s\n", ip4addr_ntoa(old_addr)));
+  LWIP_DEBUGF(ACD_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE,
+    ("acd_netif_ip_addr_changed(): new address = %s\n", ip4addr_ntoa(new_addr)));
+
+  /* If we change from ANY to an IP or from an IP to ANY we do nothing */
+  if (ip4_addr_isany(old_addr) || ip4_addr_isany(new_addr)) {
+    return;
+  }
+
+  ACD_FOREACH(acd, netif->acd_list) {
+    /* Find ACD module of old address */
+    if(ip4_addr_cmp(&acd->ipaddr, old_addr)) {
+      /* Did we change from a LL address to a routable address? */
+      if (ip4_addr_islinklocal(old_addr) && !ip4_addr_islinklocal(new_addr)) {
+        LWIP_DEBUGF(ACD_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE,
+          ("acd_netif_ip_addr_changed(): changed from LL to routable address\n"));
+        /* Put the module in passive conflict detection mode */
+        acd_put_in_passive_mode(netif, acd);
+      }
+    }
+  }
+}
+
+#endif /* LWIP_IPV4 && LWIP_ACD */
diff --git a/src/core/ipv4/autoip.c b/src/core/ipv4/autoip.c
index 9f7139bc6..1901f10e5 100644
--- a/src/core/ipv4/autoip.c
+++ b/src/core/ipv4/autoip.c
@@ -3,7 +3,9 @@
  * AutoIP Automatic LinkLocal IP Configuration
  *
  * This is a AutoIP implementation for the lwIP TCP/IP stack. It aims to conform
- * with RFC 3927.
+ * with RFC 3927. It uses IPv4 address conflict detection to evaluate the chosen
+ * address. The ACD module aims to be conform to RFC 5227.
+ * RFC 5227 is extracted out of RFC 3927 so the acd module fits nicely in autoip.
  *
  * @defgroup autoip AUTOIP
  * @ingroup ip4
@@ -11,10 +13,6 @@
  * USAGE:
  *
  * define @ref LWIP_AUTOIP 1 in your lwipopts.h
- * Options:
- * AUTOIP_TMR_INTERVAL msecs,
- *   I recommend a value of 100. The value must divide 1000 with a remainder almost 0.
- *   Possible values are 1000, 500, 333, 250, 200, 166, 142, 125, 111, 100 ....
  *
  * Without DHCP:
  * - Call autoip_start() after netif_add().
@@ -65,21 +63,12 @@
 #include "lwip/ip_addr.h"
 #include "lwip/netif.h"
 #include "lwip/autoip.h"
+#include "lwip/acd.h"
 #include "lwip/etharp.h"
 #include "lwip/prot/autoip.h"
 
 #include <string.h>
 
-/** Pseudo random macro based on netif informations.
- * You could use "rand()" from the C Library if you define LWIP_AUTOIP_RAND in lwipopts.h */
-#ifndef LWIP_AUTOIP_RAND
-#define LWIP_AUTOIP_RAND(netif) ( (((u32_t)((netif->hwaddr[5]) & 0xff) << 24) | \
-                                   ((u32_t)((netif->hwaddr[3]) & 0xff) << 16) | \
-                                   ((u32_t)((netif->hwaddr[2]) & 0xff) << 8) | \
-                                   ((u32_t)((netif->hwaddr[4]) & 0xff))) + \
-                                   (netif_autoip_data(netif)? netif_autoip_data(netif)->tried_llipaddr : 0))
-#endif /* LWIP_AUTOIP_RAND */
-
 /**
  * Macro that generates the initial IP address to be tried by AUTOIP.
  * If you want to override this, define it to something else in lwipopts.h.
@@ -90,9 +79,12 @@
                  ((u32_t)((u8_t)(netif->hwaddr[5]))) << 8)))
 #endif /* LWIP_AUTOIP_CREATE_SEED_ADDR */
 
-/* static functions */
-static err_t autoip_arp_announce(struct netif *netif);
-static void autoip_start_probing(struct netif *netif);
+/* Function definitions */
+static void autoip_restart(struct netif *netif);
+static void autoip_create_addr(struct netif *netif, ip4_addr_t *ipaddr);
+static err_t autoip_bind(struct netif *netif);
+static void autoip_conflict_callback(struct netif *netif,
+                                     acd_callback_enum_t state);
 
 /**
  * @ingroup autoip
@@ -129,35 +121,6 @@ autoip_restart(struct netif *netif)
   autoip_start(netif);
 }
 
-/**
- * Handle a IP address conflict after an ARP conflict detection
- */
-static void
-autoip_handle_arp_conflict(struct netif *netif)
-{
-  struct autoip *autoip = netif_autoip_data(netif);
-
-  /* RFC3927, 2.5 "Conflict Detection and Defense" allows two options where
-     a) means retreat on the first conflict and
-     b) allows to keep an already configured address when having only one
-        conflict in 10 seconds
-     We use option b) since it helps to improve the chance that one of the two
-     conflicting hosts may be able to retain its address. */
-
-  if (autoip->lastconflict > 0) {
-    /* retreat, there was a conflicting ARP in the last DEFEND_INTERVAL seconds */
-    LWIP_DEBUGF(AUTOIP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE,
-                ("autoip_handle_arp_conflict(): we are defending, but in DEFEND_INTERVAL, retreating\n"));
-
-    /* Active TCP sessions are aborted when removing the ip addresss */
-    autoip_restart(netif);
-  } else {
-    LWIP_DEBUGF(AUTOIP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE,
-                ("autoip_handle_arp_conflict(): we are defend, send ARP Announce\n"));
-    autoip_arp_announce(netif);
-    autoip->lastconflict = DEFEND_INTERVAL * AUTOIP_TICKS_PER_SECOND;
-  }
-}
 
 /**
  * Create an IP-Address out of range 169.254.1.0 to 169.254.254.255
@@ -195,29 +158,6 @@ autoip_create_addr(struct netif *netif, ip4_addr_t *ipaddr)
                ip4_addr3_16(ipaddr), ip4_addr4_16(ipaddr)));
 }
 
-/**
- * Sends an ARP probe from a network interface
- *
- * @param netif network interface used to send the probe
- */
-static err_t
-autoip_arp_probe(struct netif *netif)
-{
-  struct autoip *autoip = netif_autoip_data(netif);
-  /* this works because netif->ip_addr is ANY */
-  return etharp_request(netif, &autoip->llipaddr);
-}
-
-/**
- * Sends an ARP announce from a network interface
- *
- * @param netif network interface used to send the announce
- */
-static err_t
-autoip_arp_announce(struct netif *netif)
-{
-  return etharp_gratuitous(netif);
-}
 
 /**
  * Configure interface for use with current LL IP-Address
@@ -230,6 +170,8 @@ autoip_bind(struct netif *netif)
   struct autoip *autoip = netif_autoip_data(netif);
   ip4_addr_t sn_mask, gw_addr;
 
+  autoip->state = AUTOIP_STATE_BOUND;
+
   LWIP_DEBUGF(AUTOIP_DEBUG | LWIP_DBG_TRACE,
               ("autoip_bind(netif=%p) %c%c%"U16_F" %"U16_F".%"U16_F".%"U16_F".%"U16_F"\n",
                (void *)netif, netif->name[0], netif->name[1], (u16_t)netif->num,
@@ -245,6 +187,33 @@ autoip_bind(struct netif *netif)
   return ERR_OK;
 }
 
+/**
+* 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, acd_callback_enum_t state)
+{
+  struct autoip *autoip = netif_autoip_data(netif);
+
+  switch (state) {
+    case ACD_IP_OK:
+      autoip_bind(netif);
+      break;
+    case ACD_RESTART_CLIENT:
+      autoip_restart(netif);
+      break;
+    case ACD_DECLINE:
+      /* "delete" conflicting address so a new one will be selected in
+       * autoip_start() */
+      ip4_addr_set_any(&autoip->llipaddr);
+      autoip_stop(netif);
+      break;
+  }
+}
+
 /**
  * @ingroup autoip
  * Start AutoIP client
@@ -260,82 +229,85 @@ 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));
-  if (autoip == NULL) {
-    /* no AutoIP client attached yet? */
-    LWIP_DEBUGF(AUTOIP_DEBUG | LWIP_DBG_TRACE,
-                ("autoip_start(): starting new AUTOIP client\n"));
-    autoip = (struct autoip *)mem_calloc(1, sizeof(struct autoip));
+  if (autoip->state == AUTOIP_STATE_OFF) {
+    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));
     if (autoip == NULL) {
+      /* no AutoIP client attached yet? */
       LWIP_DEBUGF(AUTOIP_DEBUG | LWIP_DBG_TRACE,
-                  ("autoip_start(): could not allocate autoip\n"));
-      return ERR_MEM;
+                  ("autoip_start(): starting new AUTOIP client\n"));
+      autoip = (struct autoip *)mem_calloc(1, sizeof(struct autoip));
+      if (autoip == NULL) {
+        LWIP_DEBUGF(AUTOIP_DEBUG | LWIP_DBG_TRACE,
+                    ("autoip_start(): could not allocate autoip\n"));
+        return ERR_MEM;
+      }
+      /* store this AutoIP client in the netif */
+      netif_set_client_data(netif, LWIP_NETIF_CLIENT_DATA_INDEX_AUTOIP, autoip);
+      LWIP_DEBUGF(AUTOIP_DEBUG | LWIP_DBG_TRACE, ("autoip_start(): allocated autoip"));
     }
-    /* store this AutoIP client in the netif */
-    netif_set_client_data(netif, LWIP_NETIF_CLIENT_DATA_INDEX_AUTOIP, autoip);
-    LWIP_DEBUGF(AUTOIP_DEBUG | LWIP_DBG_TRACE, ("autoip_start(): allocated autoip"));
-  } else {
-    autoip->state = AUTOIP_STATE_OFF;
-    autoip->ttw = 0;
-    autoip->sent_num = 0;
-    ip4_addr_set_zero(&autoip->llipaddr);
-    autoip->lastconflict = 0;
-  }
 
-  autoip_create_addr(netif, &(autoip->llipaddr));
-  autoip_start_probing(netif);
+    /* add acd struct to list*/
+    acd_add(netif, &autoip->acd, autoip_conflict_callback);
+
+    /* In accordance to RFC3927 section 2.1:
+     * Keep using the same link local address as much as possible.
+     * Only when their is none or when their was a conflict, select a new one.
+     */
+    if (!ip4_addr_islinklocal(&autoip->llipaddr)) {
+      autoip_create_addr(netif, &(autoip->llipaddr));
+    }
+    autoip->state = AUTOIP_STATE_CHECKING;
+    acd_start(netif, &autoip->acd, autoip->llipaddr);
+  }
+  else {
+    LWIP_DEBUGF(AUTOIP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE,
+                ("autoip_start(): already started on netif=%p %c%c%"U16_F"\n",
+                (void *)netif, netif->name[0],
+                 netif->name[1], (u16_t)netif->num));
+  }
 
   return result;
 }
 
-static void
-autoip_start_probing(struct netif *netif)
+
+/**
+ * Handle a possible change in the network configuration: link up
+ *
+ * If there is an AutoIP address configured and AutoIP is not in cooperation
+ * with DHCP, start probing for previous address.
+ */
+void
+autoip_network_changed_link_up(struct netif *netif)
 {
   struct autoip *autoip = netif_autoip_data(netif);
 
-  autoip->state = AUTOIP_STATE_PROBING;
-  autoip->sent_num = 0;
-  LWIP_DEBUGF(AUTOIP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE,
-              ("autoip_start_probing(): changing state to PROBING: %"U16_F".%"U16_F".%"U16_F".%"U16_F"\n",
-               ip4_addr1_16(&autoip->llipaddr), ip4_addr2_16(&autoip->llipaddr),
-               ip4_addr3_16(&autoip->llipaddr), ip4_addr4_16(&autoip->llipaddr)));
-
-  /* time to wait to first probe, this is randomly
-   * chosen out of 0 to PROBE_WAIT seconds.
-   * compliant to RFC 3927 Section 2.2.1
-   */
-  autoip->ttw = (u16_t)(LWIP_AUTOIP_RAND(netif) % (PROBE_WAIT * AUTOIP_TICKS_PER_SECOND));
-
-  /*
-   * if we tried more then MAX_CONFLICTS we must limit our rate for
-   * acquiring and probing address
-   * compliant to RFC 3927 Section 2.2.1
-   */
-  if (autoip->tried_llipaddr > MAX_CONFLICTS) {
-    autoip->ttw = RATE_LIMIT_INTERVAL * AUTOIP_TICKS_PER_SECOND;
+  if (autoip && (autoip->state != AUTOIP_STATE_OFF) && !LWIP_DHCP_AUTOIP_COOP) {
+    LWIP_DEBUGF(AUTOIP_DEBUG | LWIP_DBG_TRACE,
+                ("autoip_network_changed_link_up(): start acd"));
+    autoip->state = AUTOIP_STATE_CHECKING;
+    /* Start acd check again for the last used address */
+    acd_start(netif, &autoip->acd, autoip->llipaddr);
   }
 }
 
 /**
- * Handle a possible change in the network configuration.
+ * Handle a possible change in the network configuration: link down
  *
- * If there is an AutoIP address configured, take the interface down
- * and begin probing with the same address.
+ * If there is an AutoIP address configured and AutoIP is in cooperation
+ * with DHCP, then stop the autoip module. When the link goes up, we do not want
+ * the autoip module to start again. DHCP will initiate autoip when needed.
  */
 void
-autoip_network_changed(struct netif *netif)
+autoip_network_changed_link_down(struct netif *netif)
 {
   struct autoip *autoip = netif_autoip_data(netif);
 
-  if (autoip && (autoip->state != AUTOIP_STATE_OFF)) {
-    autoip_start_probing(netif);
+  if (autoip && (autoip->state != AUTOIP_STATE_OFF) && LWIP_DHCP_AUTOIP_COOP) {
+    LWIP_DEBUGF(AUTOIP_DEBUG | LWIP_DBG_TRACE,
+                ("autoip_network_changed_link_down(): stop autoip"));
+    autoip_stop(netif);
   }
 }
 
@@ -356,172 +328,33 @@ autoip_stop(struct netif *netif)
     if (ip4_addr_islinklocal(netif_ip4_addr(netif))) {
       netif_set_addr(netif, IP4_ADDR_ANY4, IP4_ADDR_ANY4, IP4_ADDR_ANY4);
     }
+    LWIP_DEBUGF(AUTOIP_DEBUG | LWIP_DBG_TRACE,("autoip_stop()"));
   }
   return ERR_OK;
 }
 
-/**
- * Has to be called in loop every AUTOIP_TMR_INTERVAL milliseconds
- */
-void
-autoip_tmr(void)
-{
-  struct netif *netif;
-  /* loop through netif's */
-  NETIF_FOREACH(netif) {
-    struct autoip *autoip = netif_autoip_data(netif);
-    /* only act on AutoIP configured interfaces */
-    if (autoip != NULL) {
-      if (autoip->lastconflict > 0) {
-        autoip->lastconflict--;
-      }
-
-      LWIP_DEBUGF(AUTOIP_DEBUG | LWIP_DBG_TRACE,
-                  ("autoip_tmr() AutoIP-State: %"U16_F", ttw=%"U16_F"\n",
-                   (u16_t)(autoip->state), autoip->ttw));
-
-      if (autoip->ttw > 0) {
-        autoip->ttw--;
-      }
-
-      switch (autoip->state) {
-        case AUTOIP_STATE_PROBING:
-          if (autoip->ttw == 0) {
-            if (autoip->sent_num >= PROBE_NUM) {
-              /* Switch to ANNOUNCING: now we can bind to an IP address and use it */
-              autoip->state = AUTOIP_STATE_ANNOUNCING;
-              autoip_bind(netif);
-              /* autoip_bind() calls netif_set_addr(): this triggers a gratuitous ARP
-                 which counts as an announcement */
-              autoip->sent_num = 1;
-              autoip->ttw = ANNOUNCE_WAIT * AUTOIP_TICKS_PER_SECOND;
-              LWIP_DEBUGF(AUTOIP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE,
-                          ("autoip_tmr(): changing state to ANNOUNCING: %"U16_F".%"U16_F".%"U16_F".%"U16_F"\n",
-                           ip4_addr1_16(&autoip->llipaddr), ip4_addr2_16(&autoip->llipaddr),
-                           ip4_addr3_16(&autoip->llipaddr), ip4_addr4_16(&autoip->llipaddr)));
-            } else {
-              autoip_arp_probe(netif);
-              LWIP_DEBUGF(AUTOIP_DEBUG | LWIP_DBG_TRACE, ("autoip_tmr() PROBING Sent Probe\n"));
-              autoip->sent_num++;
-              if (autoip->sent_num == PROBE_NUM) {
-                /* calculate time to wait to for announce */
-                autoip->ttw = ANNOUNCE_WAIT * AUTOIP_TICKS_PER_SECOND;
-              } else {
-                /* calculate time to wait to next probe */
-                autoip->ttw = (u16_t)((LWIP_AUTOIP_RAND(netif) %
-                                       ((PROBE_MAX - PROBE_MIN) * AUTOIP_TICKS_PER_SECOND) ) +
-                                      PROBE_MIN * AUTOIP_TICKS_PER_SECOND);
-              }
-            }
-          }
-          break;
-
-        case AUTOIP_STATE_ANNOUNCING:
-          if (autoip->ttw == 0) {
-            autoip_arp_announce(netif);
-            LWIP_DEBUGF(AUTOIP_DEBUG | LWIP_DBG_TRACE, ("autoip_tmr() ANNOUNCING Sent Announce\n"));
-            autoip->ttw = ANNOUNCE_INTERVAL * AUTOIP_TICKS_PER_SECOND;
-            autoip->sent_num++;
-
-            if (autoip->sent_num >= ANNOUNCE_NUM) {
-              autoip->state = AUTOIP_STATE_BOUND;
-              autoip->sent_num = 0;
-              autoip->ttw = 0;
-              LWIP_DEBUGF(AUTOIP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE,
-                          ("autoip_tmr(): changing state to BOUND: %"U16_F".%"U16_F".%"U16_F".%"U16_F"\n",
-                           ip4_addr1_16(&autoip->llipaddr), ip4_addr2_16(&autoip->llipaddr),
-                           ip4_addr3_16(&autoip->llipaddr), ip4_addr4_16(&autoip->llipaddr)));
-            }
-          }
-          break;
-
-        default:
-          /* nothing to do in other states */
-          break;
-      }
-    }
-  }
-}
-
-/**
- * Handles every incoming ARP Packet, called by etharp_input().
- *
- * @param netif network interface to use for autoip processing
- * @param hdr Incoming ARP packet
- */
-void
-autoip_arp_reply(struct netif *netif, struct etharp_hdr *hdr)
-{
-  struct autoip *autoip = netif_autoip_data(netif);
-
-  LWIP_DEBUGF(AUTOIP_DEBUG | LWIP_DBG_TRACE, ("autoip_arp_reply()\n"));
-  if ((autoip != NULL) && (autoip->state != AUTOIP_STATE_OFF)) {
-    /* when ip.src == llipaddr && hw.src != netif->hwaddr
-     *
-     * when probing  ip.dst == llipaddr && hw.src != netif->hwaddr
-     * we have a conflict and must solve it
-     */
-    ip4_addr_t sipaddr, dipaddr;
-    struct eth_addr netifaddr;
-    SMEMCPY(netifaddr.addr, netif->hwaddr, ETH_HWADDR_LEN);
-
-    /* Copy struct ip4_addr_wordaligned to aligned ip4_addr, to support compilers without
-     * structure packing (not using structure copy which breaks strict-aliasing rules).
-     */
-    IPADDR_WORDALIGNED_COPY_TO_IP4_ADDR_T(&sipaddr, &hdr->sipaddr);
-    IPADDR_WORDALIGNED_COPY_TO_IP4_ADDR_T(&dipaddr, &hdr->dipaddr);
-
-    if (autoip->state == AUTOIP_STATE_PROBING) {
-      /* RFC 3927 Section 2.2.1:
-       * from beginning to after ANNOUNCE_WAIT
-       * seconds we have a conflict if
-       * ip.src == llipaddr OR
-       * ip.dst == llipaddr && hw.src != own hwaddr
-       */
-      if ((ip4_addr_cmp(&sipaddr, &autoip->llipaddr)) ||
-          (ip4_addr_isany_val(sipaddr) &&
-           ip4_addr_cmp(&dipaddr, &autoip->llipaddr) &&
-           !eth_addr_cmp(&netifaddr, &hdr->shwaddr))) {
-        LWIP_DEBUGF(AUTOIP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE | LWIP_DBG_LEVEL_WARNING,
-                    ("autoip_arp_reply(): Probe Conflict detected\n"));
-        autoip_restart(netif);
-      }
-    } else {
-      /* RFC 3927 Section 2.5:
-       * in any state we have a conflict if
-       * ip.src == llipaddr && hw.src != own hwaddr
-       */
-      if (ip4_addr_cmp(&sipaddr, &autoip->llipaddr) &&
-          !eth_addr_cmp(&netifaddr, &hdr->shwaddr)) {
-        LWIP_DEBUGF(AUTOIP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE | LWIP_DBG_LEVEL_WARNING,
-                    ("autoip_arp_reply(): Conflicting ARP-Packet detected\n"));
-        autoip_handle_arp_conflict(netif);
-      }
-    }
-  }
-}
-
 /** check if AutoIP supplied netif->ip_addr
  *
  * @param netif the netif to check
- * @return 1 if AutoIP supplied netif->ip_addr (state BOUND or ANNOUNCING),
+ * @return 1 if AutoIP supplied netif->ip_addr (state BOUND),
  *         0 otherwise
  */
 u8_t
-autoip_supplied_address(const struct netif *netif)
+autoip_supplied_address(struct netif *netif)
 {
-  if ((netif != NULL) && (netif_autoip_data(netif) != NULL)) {
-    struct autoip *autoip = netif_autoip_data(netif);
-    return (autoip->state == AUTOIP_STATE_BOUND) || (autoip->state == AUTOIP_STATE_ANNOUNCING);
-  }
-  return 0;
+  struct autoip *autoip = netif_autoip_data(netif);
+  return     (autoip != NULL)
+          && (ip4_addr_cmp(&netif->ip_addr, &(autoip->llipaddr)))
+          && (autoip->state == AUTOIP_STATE_BOUND);
 }
 
 u8_t
 autoip_accept_packet(struct netif *netif, const ip4_addr_t *addr)
 {
   struct autoip *autoip = netif_autoip_data(netif);
-  return (autoip != NULL) && ip4_addr_cmp(addr, &(autoip->llipaddr));
+  return     (autoip != NULL)
+          && (ip4_addr_cmp(addr, &(autoip->llipaddr)))
+          && (autoip->state == AUTOIP_STATE_BOUND);
 }
 
 #endif /* LWIP_IPV4 && LWIP_AUTOIP */
diff --git a/src/core/ipv4/dhcp.c b/src/core/ipv4/dhcp.c
index 534574fea..d2cbc8753 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->acd, dhcp->offered_ip_addr);
 }
-#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);
@@ -777,6 +793,13 @@ dhcp_start(struct netif *netif)
   memset(dhcp, 0, sizeof(struct dhcp));
   /* dhcp_set_state(&dhcp, DHCP_STATE_OFF); */
 
+
+#if DHCP_DOES_ACD_CHECK
+  /* add acd struct to list*/
+  acd_add(netif, &dhcp->acd, dhcp_conflict_callback);
+#endif /* DHCP_DOES_ACD_CHECK */
+
+
   LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE, ("dhcp_start(): starting DHCP configuration\n"));
 
   if (dhcp_inc_pcb_refcount() != ERR_OK) { /* ensure DHCP PCB is allocated */
@@ -855,7 +878,7 @@ dhcp_inform(struct netif *netif)
  * address is still valid.
  */
 void
-dhcp_network_changed(struct netif *netif)
+dhcp_network_changed_link_up(struct netif *netif)
 {
   struct dhcp *dhcp = netif_dhcp_data(netif);
 
@@ -878,12 +901,6 @@ dhcp_network_changed(struct netif *netif)
       /* INIT/REQUESTING/CHECKING/BACKING_OFF restart with new 'rid' because the
          state changes, SELECTING: continue with current 'rid' as we stay in the
          same state */
-#if LWIP_DHCP_AUTOIP_COOP
-      if (dhcp->autoip_coop_state == DHCP_AUTOIP_COOP_STATE_ON) {
-        autoip_stop(netif);
-        dhcp->autoip_coop_state = DHCP_AUTOIP_COOP_STATE_OFF;
-      }
-#endif /* LWIP_DHCP_AUTOIP_COOP */
       /* ensure we start with short timeouts, even if already discovering */
       dhcp->tries = 0;
       dhcp_discover(netif);
@@ -891,37 +908,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.
  *
@@ -936,12 +923,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) {
@@ -961,15 +948,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 */
 
 
 /**
@@ -989,6 +970,12 @@ dhcp_discover(struct netif *netif)
 
   LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE, ("dhcp_discover()\n"));
 
+#if LWIP_DHCP_AUTOIP_COOP
+  if (dhcp->tries >= LWIP_DHCP_AUTOIP_COOP_TRIES) {
+    autoip_start(netif);
+  }
+#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 */
@@ -1015,15 +1002,10 @@ dhcp_discover(struct netif *netif)
   } else {
     LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_LEVEL_SERIOUS, ("dhcp_discover: could not allocate DHCP request\n"));
   }
+
   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));
@@ -1123,13 +1105,6 @@ dhcp_bind(struct netif *netif)
     ip4_addr_set_u32(&gw_addr, ip4_addr_get_u32(&gw_addr) | PP_HTONL(0x00000001UL));
   }
 
-#if LWIP_DHCP_AUTOIP_COOP
-  if (dhcp->autoip_coop_state == DHCP_AUTOIP_COOP_STATE_ON) {
-    autoip_stop(netif);
-    dhcp->autoip_coop_state = DHCP_AUTOIP_COOP_STATE_OFF;
-  }
-#endif /* LWIP_DHCP_AUTOIP_COOP */
-
   LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_STATE, ("dhcp_bind(): IP: 0x%08"X32_F" SN: 0x%08"X32_F" GW: 0x%08"X32_F"\n",
               ip4_addr_get_u32(&dhcp->offered_ip_addr), ip4_addr_get_u32(&sn_mask), ip4_addr_get_u32(&gw_addr)));
   /* netif is now bound to DHCP leased address - set this before assigning the address
@@ -1365,17 +1340,10 @@ dhcp_release_and_stop(struct netif *netif)
       /* sending release failed, but that's not a problem since the correct behaviour of dhcp does not rely on release */
       LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_LEVEL_SERIOUS, ("dhcp_release: could not allocate DHCP request\n"));
     }
-  }
 
-  /* remove IP address from interface (prevents routing from selecting this interface) */
-  netif_set_addr(netif, IP4_ADDR_ANY4, IP4_ADDR_ANY4, IP4_ADDR_ANY4);
-
-#if LWIP_DHCP_AUTOIP_COOP
-  if (dhcp->autoip_coop_state == DHCP_AUTOIP_COOP_STATE_ON) {
-    autoip_stop(netif);
-    dhcp->autoip_coop_state = DHCP_AUTOIP_COOP_STATE_OFF;
+    /* remove IP address from interface (prevents routing from selecting this interface) */
+    netif_set_addr(netif, IP4_ADDR_ANY4, IP4_ADDR_ANY4, IP4_ADDR_ANY4);
   }
-#endif /* LWIP_DHCP_AUTOIP_COOP */
 
   dhcp_set_state(dhcp, DHCP_STATE_OFF);
 
@@ -1743,7 +1711,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;
 }
 
@@ -1822,10 +1790,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);
@@ -1838,8 +1807,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 442aac08a..b3b7c73c4 100644
--- a/src/core/ipv4/etharp.c
+++ b/src/core/ipv4/etharp.c
@@ -52,6 +52,7 @@
 #include "lwip/snmp.h"
 #include "lwip/dhcp.h"
 #include "lwip/autoip.h"
+#include "lwip/acd.h"
 #include "lwip/prot/iana.h"
 #include "netif/ethernet.h"
 
@@ -643,7 +644,7 @@ etharp_input(struct pbuf *p, struct netif *netif)
   struct etharp_hdr *hdr;
   /* these are aligned properly, whereas the ARP header fields might not be */
   ip4_addr_t sipaddr, dipaddr;
-  u8_t for_us;
+  u8_t for_us, from_us;
 
   LWIP_ASSERT_CORE_LOCKED();
 
@@ -666,12 +667,16 @@ etharp_input(struct pbuf *p, struct netif *netif)
   }
   ETHARP_STATS_INC(etharp.recv);
 
-#if LWIP_AUTOIP
-  /* We have to check if a host already has configured our random
-   * created link local address and continuously check if there is
-   * a host with this IP-address so we can detect collisions */
-  autoip_arp_reply(netif, hdr);
-#endif /* LWIP_AUTOIP */
+#if LWIP_ACD
+  /* We have to check if a host already has configured our ip address and
+   * continuously check if there is a host with this IP-address so we can
+   * detect collisions.
+   * acd_arp_reply ensures the detection of conflicts. It will handle possible
+   * defending or retreating and will make sure a new IP address is selected.
+   * etharp_input does not need to handle packets that originate "from_us".
+   */
+  acd_arp_reply(netif, hdr);
+#endif /* LWIP_ACD */
 
   /* Copy struct ip4_addr_wordaligned to aligned ip4_addr, to support compilers without
    * structure packing (not using structure copy which breaks strict-aliasing rules). */
@@ -681,9 +686,12 @@ etharp_input(struct pbuf *p, struct netif *netif)
   /* this interface is not configured? */
   if (ip4_addr_isany_val(*netif_ip4_addr(netif))) {
     for_us = 0;
+    from_us = 0;
   } else {
     /* ARP packet directed to us? */
     for_us = (u8_t)ip4_addr_cmp(&dipaddr, netif_ip4_addr(netif));
+    /* ARP packet from us? */
+    from_us = (u8_t)ip4_addr_cmp(&sipaddr, netif_ip4_addr(netif));
   }
 
   /* ARP message directed to us?
@@ -704,7 +712,7 @@ etharp_input(struct pbuf *p, struct netif *netif)
 
       LWIP_DEBUGF (ETHARP_DEBUG | LWIP_DBG_TRACE, ("etharp_input: incoming ARP request\n"));
       /* ARP request for our address? */
-      if (for_us) {
+      if (for_us && !from_us) {
         /* send ARP response */
         etharp_raw(netif,
                    (struct eth_addr *)netif->hwaddr, &hdr->shwaddr,
@@ -724,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)));
@@ -1201,4 +1202,42 @@ etharp_request(struct netif *netif, const ip4_addr_t *ipaddr)
   return etharp_request_dst(netif, ipaddr, &ethbroadcast);
 }
 
+#if LWIP_ACD
+/**
+ * Send an ARP request packet probing for an ipaddr.
+ * Used to send probe messages for address conflict detection.
+ *
+ * @param netif the lwip network interface on which to send the request
+ * @param ipaddr the IP address to probe
+ * @return ERR_OK if the request has been sent
+ *         ERR_MEM if the ARP packet couldn't be allocated
+ *         any other err_t on failure
+ */
+err_t
+etharp_acd_probe(struct netif *netif, const ip4_addr_t *ipaddr)
+{
+  return etharp_raw(netif, (struct eth_addr *)netif->hwaddr, &ethbroadcast,
+                    (struct eth_addr *)netif->hwaddr, IP4_ADDR_ANY4, &ethzero,
+                    ipaddr, ARP_REQUEST);
+}
+
+/**
+ * Send an ARP request packet announcing an ipaddr.
+ * Used to send announce messages for address conflict detection.
+ *
+ * @param netif the lwip network interface on which to send the request
+ * @param ipaddr the IP address to announce
+ * @return ERR_OK if the request has been sent
+ *         ERR_MEM if the ARP packet couldn't be allocated
+ *         any other err_t on failure
+ */
+err_t
+etharp_acd_announce(struct netif *netif, const ip4_addr_t *ipaddr)
+{
+  return etharp_raw(netif, (struct eth_addr *)netif->hwaddr, &ethbroadcast,
+                    (struct eth_addr *)netif->hwaddr, ipaddr, &ethzero,
+                    ipaddr, ARP_REQUEST);
+}
+#endif /* LWIP_ACD */
+
 #endif /* LWIP_IPV4 && LWIP_ARP */
diff --git a/src/core/netif.c b/src/core/netif.c
index 15200a274..569677706 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 */
@@ -470,6 +473,10 @@ netif_do_set_ipaddr(struct netif *netif, const ip4_addr_t *ipaddr, ip_addr_t *ol
     LWIP_DEBUGF(NETIF_DEBUG | LWIP_DBG_STATE, ("netif_set_ipaddr: netif address being changed\n"));
     netif_do_ip_addr_changed(old_addr, &new_addr);
 
+#if LWIP_ACD
+    acd_netif_ip_addr_changed(netif, old_addr, &new_addr);
+#endif /* LWIP_ACD */
+
     mib2_remove_ip4(netif);
     mib2_remove_route_ip4(0, netif);
     /* set new IP address to netif */
@@ -885,8 +892,11 @@ netif_issue_reports(struct netif *netif, u8_t report_type)
 #if LWIP_IPV4
   if ((report_type & NETIF_REPORT_TYPE_IPV4) &&
       !ip4_addr_isany_val(*netif_ip4_addr(netif))) {
-#if LWIP_ARP
-    /* For Ethernet network interfaces, we would like to send a "gratuitous ARP" */
+#if LWIP_ARP && !LWIP_ACD
+    /* For Ethernet network interfaces:
+     * we would like to send a "gratuitous ARP".
+     * Only needs to be done here if ACD isn't configured. 
+     */
     if (netif->flags & (NETIF_FLAG_ETHARP)) {
       etharp_gratuitous(netif);
     }
@@ -995,11 +1005,11 @@ netif_set_link_up(struct netif *netif)
     netif_set_flags(netif, NETIF_FLAG_LINK_UP);
 
 #if LWIP_DHCP
-    dhcp_network_changed(netif);
+    dhcp_network_changed_link_up(netif);
 #endif /* LWIP_DHCP */
 
 #if LWIP_AUTOIP
-    autoip_network_changed(netif);
+    autoip_network_changed_link_up(netif);
 #endif /* LWIP_AUTOIP */
 
     netif_issue_reports(netif, NETIF_REPORT_TYPE_IPV4 | NETIF_REPORT_TYPE_IPV6);
@@ -1031,6 +1041,15 @@ netif_set_link_down(struct netif *netif)
 
   if (netif->flags & NETIF_FLAG_LINK_UP) {
     netif_clear_flags(netif, NETIF_FLAG_LINK_UP);
+
+#if LWIP_AUTOIP
+    autoip_network_changed_link_down(netif);
+#endif /* LWIP_AUTOIP */
+
+#if LWIP_ACD
+    acd_network_changed_link_down(netif);
+#endif /* LWIP_ACD */
+
     NETIF_LINK_CALLBACK(netif);
 #if LWIP_NETIF_EXT_STATUS_CALLBACK
     {
diff --git a/src/core/timeouts.c b/src/core/timeouts.c
index f37acfec9..81e8450d0 100644
--- a/src/core/timeouts.c
+++ b/src/core/timeouts.c
@@ -51,7 +51,7 @@
 #include "lwip/ip4_frag.h"
 #include "lwip/etharp.h"
 #include "lwip/dhcp.h"
-#include "lwip/autoip.h"
+#include "lwip/acd.h"
 #include "lwip/igmp.h"
 #include "lwip/dns.h"
 #include "lwip/nd6.h"
@@ -91,9 +91,9 @@ const struct lwip_cyclic_timer lwip_cyclic_timers[] = {
   {DHCP_COARSE_TIMER_MSECS, HANDLER(dhcp_coarse_tmr)},
   {DHCP_FINE_TIMER_MSECS, HANDLER(dhcp_fine_tmr)},
 #endif /* LWIP_DHCP */
-#if LWIP_AUTOIP
-  {AUTOIP_TMR_INTERVAL, HANDLER(autoip_tmr)},
-#endif /* LWIP_AUTOIP */
+#if LWIP_ACD
+  {ACD_TMR_INTERVAL, HANDLER(acd_tmr)},
+#endif /* LWIP_ACD */
 #if LWIP_IGMP
   {IGMP_TMR_INTERVAL, HANDLER(igmp_tmr)},
 #endif /* LWIP_IGMP */
diff --git a/src/include/lwip/acd.h b/src/include/lwip/acd.h
new file mode 100644
index 000000000..e8ded9c98
--- /dev/null
+++ b/src/include/lwip/acd.h
@@ -0,0 +1,117 @@
+/**
+ * @file
+ *
+ * ACD IPv4 Address Conflict Detection
+ */
+
+/*
+ *
+ * Copyright (c) 2007 Dominik Spies <kontakt@dspies.de>
+ * Copyright (c) 2018 Jasper Verschueren <jasper.verschueren@apart-audio.com>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ *    this list of conditions and the following disclaimer.
+ * 2. 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.
+ * 3. 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.
+ *
+ * Author: Jasper Verschueren <jasper.verschueren@apart-audio.com>
+ * Author: Dominik Spies <kontakt@dspies.de>
+ */
+
+#ifndef LWIP_HDR_ACD_H
+#define LWIP_HDR_ACD_H
+
+#include "lwip/opt.h"
+
+/* don't build if not configured for use in lwipopts.h */
+#if LWIP_IPV4 && LWIP_ACD
+
+#include "lwip/netif.h"
+#include "lwip/etharp.h"
+#include "lwip/prot/acd.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/** ACD Timing
+ *  ACD_TMR_INTERVAL msecs, I recommend a value of 100.
+ *  The value must divide 1000 with a remainder almost 0. Possible values are
+ *  1000, 500, 333, 250, 200, 166, 142, 125, 111, 100 ....
+ */
+#define ACD_TMR_INTERVAL      100
+#define ACD_TICKS_PER_SECOND  (1000 / ACD_TMR_INTERVAL)
+
+#define ACD_FOREACH(acd, acd_list) for ((acd) = acd_list; (acd) != NULL; (acd) = (acd)->next)
+
+/**
+ * Callback function: Handle conflict information from ACD module
+ *
+ * @param netif   network interface to handle conflict information on
+ * @param state   acd_callback_enum_t
+ */
+typedef void (*acd_conflict_callback_t)(struct netif *netif, acd_callback_enum_t state);
+
+/** ACD state information per netif */
+struct acd
+{
+  /** next acd module */
+  struct acd *next;
+  /** the currently selected, probed, announced or used IP-Address */
+  ip4_addr_t ipaddr;
+  /** current ACD state machine 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 */
+  u16_t ttw;
+  /** ticks until a conflict can again be solved by defending */
+  u8_t lastconflict;
+  /** total number of probed/used IP-Addresses that resulted in a conflict */
+  u8_t num_conflicts;
+  /** callback function -> let's the acd user know if the address is good or
+      if a conflict is detected */
+  acd_conflict_callback_t acd_conflict_callback;
+};
+
+err_t acd_add(struct netif *netif, struct acd *acd,
+              acd_conflict_callback_t acd_conflict_callback);
+
+err_t acd_start(struct netif *netif, struct acd *acd, ip4_addr_t ipaddr);
+
+err_t acd_stop(struct netif *netif, struct acd *acd);
+
+void acd_arp_reply(struct netif *netif, struct etharp_hdr *hdr);
+
+void acd_tmr(void);
+
+void acd_network_changed_link_down(struct netif *netif);
+
+void acd_netif_ip_addr_changed(struct netif *netif, const ip_addr_t *old_addr,
+                               const ip_addr_t *new_addr);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* LWIP_IPV4 && LWIP_ACD */
+
+#endif /* LWIP_HDR_ACD_H */
diff --git a/src/include/lwip/autoip.h b/src/include/lwip/autoip.h
index 1d85bccff..b7558eb09 100644
--- a/src/include/lwip/autoip.h
+++ b/src/include/lwip/autoip.h
@@ -48,15 +48,12 @@
 #include "lwip/netif.h"
 /* #include "lwip/udp.h" */
 #include "lwip/etharp.h"
+#include "lwip/acd.h"
 
 #ifdef __cplusplus
 extern "C" {
 #endif
 
-/** AutoIP Timing */
-#define AUTOIP_TMR_INTERVAL      100
-#define AUTOIP_TICKS_PER_SECOND (1000 / AUTOIP_TMR_INTERVAL)
-
 /** AutoIP state information per netif */
 struct autoip
 {
@@ -64,14 +61,10 @@ struct autoip
   ip4_addr_t llipaddr;
   /** current AutoIP state machine state */
   u8_t state;
-  /** sent number of probes or announces, dependent on state */
-  u8_t sent_num;
-  /** ticks to wait, tick is AUTOIP_TMR_INTERVAL long */
-  u16_t ttw;
-  /** ticks until a conflict can be solved by defending */
-  u8_t lastconflict;
   /** total number of probed/used Link Local IP-Addresses */
   u8_t tried_llipaddr;
+  /** acd struct */
+  struct acd acd;
 };
 
 
@@ -80,10 +73,9 @@ void autoip_set_struct(struct netif *netif, struct autoip *autoip);
 #define autoip_remove_struct(netif) do { (netif)->autoip = NULL; } while (0)
 err_t autoip_start(struct netif *netif);
 err_t autoip_stop(struct netif *netif);
-void autoip_arp_reply(struct netif *netif, struct etharp_hdr *hdr);
-void autoip_tmr(void);
-void autoip_network_changed(struct netif *netif);
-u8_t autoip_supplied_address(const struct netif *netif);
+void autoip_network_changed_link_up(struct netif *netif);
+void autoip_network_changed_link_down(struct netif *netif);
+u8_t autoip_supplied_address(struct netif *netif);
 
 /* for lwIP internal use by ip4.c */
 u8_t autoip_accept_packet(struct netif *netif, const ip4_addr_t *addr);
diff --git a/src/include/lwip/dhcp.h b/src/include/lwip/dhcp.h
index c78aa0baf..df33bb2df 100644
--- a/src/include/lwip/dhcp.h
+++ b/src/include/lwip/dhcp.h
@@ -45,6 +45,10 @@
 #include "lwip/netif.h"
 #include "lwip/udp.h"
 
+#if DHCP_DOES_ACD_CHECK
+#include "lwip/acd.h"
+#endif /* DHCP_DOES_ACD_CHECK */
+
 #ifdef __cplusplus
 extern "C" {
 #endif
@@ -74,9 +78,7 @@ struct dhcp
   u8_t state;
   /** retries of current request */
   u8_t tries;
-#if LWIP_DHCP_AUTOIP_COOP
-  u8_t autoip_coop_state;
-#endif
+  
   u8_t subnet_mask_given;
 
   u16_t request_timeout; /* #ticks with period DHCP_FINE_TIMER_SECS for request timeout */
@@ -98,6 +100,10 @@ struct dhcp
   ip4_addr_t offered_si_addr;
   char boot_file_name[DHCP_BOOT_FILE_LEN];
 #endif /* LWIP_DHCP_BOOTPFILE */
+#if DHCP_DOES_ACD_CHECK
+  /** acd struct */
+  struct acd acd;
+#endif /* DHCP_DOES_ACD_CHECK */
 };
 
 
@@ -111,10 +117,8 @@ err_t dhcp_release(struct netif *netif);
 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
+void dhcp_network_changed_link_up(struct netif *netif);
+
 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/etharp.h b/src/include/lwip/etharp.h
index 2036b2464..48a1d229e 100644
--- a/src/include/lwip/etharp.h
+++ b/src/include/lwip/etharp.h
@@ -88,6 +88,11 @@ err_t etharp_request(struct netif *netif, const ip4_addr_t *ipaddr);
 #define etharp_gratuitous(netif) etharp_request((netif), netif_ip4_addr(netif))
 void etharp_cleanup_netif(struct netif *netif);
 
+#if LWIP_ACD
+err_t etharp_acd_probe(struct netif *netif, const ip4_addr_t *ipaddr);
+err_t etharp_acd_announce(struct netif *netif, const ip4_addr_t *ipaddr);
+#endif /* LWIP_ACD */
+
 #if ETHARP_SUPPORT_STATIC_ENTRIES
 err_t etharp_add_static_entry(const ip4_addr_t *ipaddr, struct eth_addr *ethaddr);
 err_t etharp_remove_static_entry(const ip4_addr_t *ipaddr);
diff --git a/src/include/lwip/netif.h b/src/include/lwip/netif.h
index 911196ab3..9e2007a64 100644
--- a/src/include/lwip/netif.h
+++ b/src/include/lwip/netif.h
@@ -119,6 +119,9 @@ enum lwip_internal_netif_client_data_index
 #if LWIP_AUTOIP
    LWIP_NETIF_CLIENT_DATA_INDEX_AUTOIP,
 #endif
+#if LWIP_ACD
+   LWIP_NETIF_CLIENT_DATA_INDEX_ACD,
+#endif
 #if LWIP_IGMP
    LWIP_NETIF_CLIENT_DATA_INDEX_IGMP,
 #endif
@@ -376,6 +379,9 @@ struct netif {
       filter table of the ethernet MAC. */
   netif_mld_mac_filter_fn mld_mac_filter;
 #endif /* LWIP_IPV6 && LWIP_IPV6_MLD */
+#if LWIP_ACD
+  struct acd *acd_list;
+#endif /* LWIP_ACD */
 #if LWIP_NETIF_USE_HINTS
   struct netif_hint *hints;
 #endif /* LWIP_NETIF_USE_HINTS */
diff --git a/src/include/lwip/opt.h b/src/include/lwip/opt.h
index a14645fa9..fd617ecff 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
 
 /**
@@ -1009,6 +1009,31 @@
  * @}
  */
 
+/*
+   ------------------------------------
+   ----------- ACD options ------------
+   ------------------------------------
+*/
+/**
+ * @defgroup lwip_opts_acd ACD
+ * @ingroup lwip_opts_ipv4
+ * @{
+ */
+ /**
+  * LWIP_ACD==1: Enable ACD module. ACD module is needed when using AUTOIP.
+  */
+#if !defined LWIP_ACD || defined __DOXYGEN__
+#define LWIP_ACD                     (LWIP_AUTOIP || DHCP_DOES_ACD_CHECK)
+#endif
+#if !LWIP_IPV4
+/* disable ACD when IPv4 is disabled */
+#undef LWIP_ACD
+#define LWIP_ACD                     0
+#endif /* !LWIP_IPV4 */
+/**
+ * @}
+ */
+
 /*
    ----------------------------------
    ----- SNMP MIB2 support      -----
@@ -3448,6 +3473,13 @@
 #define AUTOIP_DEBUG                    LWIP_DBG_OFF
 #endif
 
+/**
+ * ACD_DEBUG: Enable debugging in acd.c.
+ */
+#if !defined ACD_DEBUG || defined __DOXYGEN__
+#define ACD_DEBUG                       LWIP_DBG_OFF
+#endif
+
 /**
  * DNS_DEBUG: Enable debugging for DNS.
  */
diff --git a/src/include/lwip/prot/acd.h b/src/include/lwip/prot/acd.h
new file mode 100644
index 000000000..ed5124910
--- /dev/null
+++ b/src/include/lwip/prot/acd.h
@@ -0,0 +1,91 @@
+/**
+ * @file
+ * ACD protocol definitions
+ */
+
+/*
+ *
+ * Copyright (c) 2007 Dominik Spies <kontakt@dspies.de>
+ * Copyright (c) 2018 Jasper Verschueren <jasper.verschueren@apart-audio.com>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ *    this list of conditions and the following disclaimer.
+ * 2. 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.
+ * 3. 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.
+ *
+ * Author: Jasper Verschueren <jasper.verschueren@apart-audio.com>
+ * Author: Dominik Spies <kontakt@dspies.de>
+ */
+
+#ifndef LWIP_HDR_PROT_ACD_H
+#define LWIP_HDR_PROT_ACD_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/* RFC 5227 and RFC 3927 Constants */
+#define PROBE_WAIT           1   /* second  (initial random delay)                    */
+#define PROBE_MIN            1   /* second  (minimum delay till repeated probe)       */
+#define PROBE_MAX            2   /* seconds (maximum delay till repeated probe)       */
+#define PROBE_NUM            3   /*         (number of probe packets)                 */
+#define ANNOUNCE_NUM         2   /*         (number of announcement packets)          */
+#define ANNOUNCE_INTERVAL    2   /* seconds (time between announcement packets)       */
+#define ANNOUNCE_WAIT        2   /* seconds (delay before announcing)                 */
+#define MAX_CONFLICTS        10  /*         (max conflicts before rate limiting)      */
+#define RATE_LIMIT_INTERVAL  60  /* seconds (delay between successive attempts)       */
+#define DEFEND_INTERVAL      10  /* seconds (minimum interval between defensive ARPs) */
+
+/* ACD states */
+typedef enum {
+  /* ACD is module is off */
+  ACD_STATE_OFF,
+  /* Waiting before probing can be started */
+  ACD_STATE_PROBE_WAIT,
+  /* Probing the ipaddr */
+  ACD_STATE_PROBING,
+  /* Waiting before announcing the probed ipaddr */
+  ACD_STATE_ANNOUNCE_WAIT,
+  /* Announcing the new ipaddr */
+  ACD_STATE_ANNOUNCING,
+  /* Performing ongoing conflict detection with one defend within defend inferval */
+  ACD_STATE_ONGOING,
+  /* Performing ongoing conflict detection but immediately back off and Release
+   * the address when a conflict occurs. This state is used for LL addresses
+   * that stay active even if the netif has a routable address selected.
+   * In such a case, we cannot defend our address */
+  ACD_STATE_PASSIVE_ONGOING,
+  /* To many conflicts occured, we need to wait before restarting the selection
+   * process */
+  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
+}
+#endif
+
+#endif /* LWIP_HDR_PROT_ACD_H */
diff --git a/src/include/lwip/prot/autoip.h b/src/include/lwip/prot/autoip.h
index fd3af8a9f..adbb1668e 100644
--- a/src/include/lwip/prot/autoip.h
+++ b/src/include/lwip/prot/autoip.h
@@ -51,24 +51,11 @@ extern "C" {
 /* 169.254.254.255 */
 #define AUTOIP_RANGE_END        (AUTOIP_NET | 0xFEFF)
 
-/* RFC 3927 Constants */
-#define PROBE_WAIT              1   /* second   (initial random delay)                 */
-#define PROBE_MIN               1   /* second   (minimum delay till repeated probe)    */
-#define PROBE_MAX               2   /* seconds  (maximum delay till repeated probe)    */
-#define PROBE_NUM               3   /*          (number of probe packets)              */
-#define ANNOUNCE_NUM            2   /*          (number of announcement packets)       */
-#define ANNOUNCE_INTERVAL       2   /* seconds  (time between announcement packets)    */
-#define ANNOUNCE_WAIT           2   /* seconds  (delay before announcing)              */
-#define MAX_CONFLICTS           10  /*          (max conflicts before rate limiting)   */
-#define RATE_LIMIT_INTERVAL     60  /* seconds  (delay between successive attempts)    */
-#define DEFEND_INTERVAL         10  /* seconds  (min. wait between defensive ARPs)     */
-
 /* AutoIP client states */
 typedef enum {
-  AUTOIP_STATE_OFF        = 0,
-  AUTOIP_STATE_PROBING    = 1,
-  AUTOIP_STATE_ANNOUNCING = 2,
-  AUTOIP_STATE_BOUND      = 3
+  AUTOIP_STATE_OFF,
+  AUTOIP_STATE_CHECKING,
+  AUTOIP_STATE_BOUND
 } autoip_state_enum_t;
 
 #ifdef __cplusplus
-- 
2.18.0

