diff --git a/src/core/init.c b/src/core/init.c index b3737a35..7a312a97 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 00000000..e1d597cc --- /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 + * Copyright (c) 2018 Jasper Verschueren + * 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 + * Author: Dominik Spies + */ + +#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 9f7139bc..1901f10e 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 -/** 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 534574fe..d2cbc875 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 442aac08..b3b7c73c 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, ðbroadcast); } +#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, ðbroadcast, + (struct eth_addr *)netif->hwaddr, IP4_ADDR_ANY4, ðzero, + 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, ðbroadcast, + (struct eth_addr *)netif->hwaddr, ipaddr, ðzero, + ipaddr, ARP_REQUEST); +} +#endif /* LWIP_ACD */ + #endif /* LWIP_IPV4 && LWIP_ARP */ diff --git a/src/core/netif.c b/src/core/netif.c index 15200a27..56967770 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 f37acfec..81e8450d 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 00000000..e8ded9c9 --- /dev/null +++ b/src/include/lwip/acd.h @@ -0,0 +1,117 @@ +/** + * @file + * + * ACD IPv4 Address Conflict Detection + */ + +/* + * + * Copyright (c) 2007 Dominik Spies + * Copyright (c) 2018 Jasper Verschueren + * 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 + * Author: Dominik Spies + */ + +#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 1d85bccf..b7558eb0 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 c78aa0ba..df33bb2d 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 2036b246..48a1d229 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 911196ab..9e2007a6 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 82c420c1..4c7b91dd 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 ----- @@ -3464,6 +3489,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 00000000..ed512491 --- /dev/null +++ b/src/include/lwip/prot/acd.h @@ -0,0 +1,91 @@ +/** + * @file + * ACD protocol definitions + */ + +/* + * + * Copyright (c) 2007 Dominik Spies + * Copyright (c) 2018 Jasper Verschueren + * 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 + * Author: Dominik Spies + */ + +#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 fd3af8a9..adbb1668 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