From: Roy Marples Date: Wed, 20 Apr 2016 08:29:08 +0000 (+0000) Subject: RFC 3927 Section 2.5 says a defence of an address should ARP announce it. X-Git-Tag: v6.10.3~1 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=cb1bbd36fffe12dbab59e2671fbc0e8bfa4737a5;p=thirdparty%2Fdhcpcd.git RFC 3927 Section 2.5 says a defence of an address should ARP announce it. This is unicast, whereas the default kernel action is to unicast a stock reply, so the client that tried to claim the address will receive two replies. Fixes [a19bb0eae6]. --- diff --git a/arp.c b/arp.c index a17b5f1a..49d392db 100644 --- a/arp.c +++ b/arp.c @@ -55,7 +55,7 @@ #define ARP_LEN \ (sizeof(struct arphdr) + (2 * sizeof(uint32_t)) + (2 * HWADDR_LEN)) -static ssize_t +ssize_t arp_request(const struct interface *ifp, in_addr_t sip, in_addr_t tip) { uint8_t arp_buffer[ARP_LEN]; diff --git a/arp.h b/arp.h index 6eb3808c..c63cc730 100644 --- a/arp.h +++ b/arp.h @@ -77,6 +77,7 @@ struct iarp_state { ((const struct iarp_state *)(ifp)->if_data[IF_DATA_ARP]) #ifdef INET +ssize_t arp_request(const struct interface *, in_addr_t, in_addr_t); void arp_report_conflicted(const struct arp_state *, const struct arp_msg *); void arp_announce(struct arp_state *); void arp_probe(struct arp_state *); diff --git a/ipv4ll.c b/ipv4ll.c index 7be32d64..d33762d9 100644 --- a/ipv4ll.c +++ b/ipv4ll.c @@ -259,26 +259,38 @@ ipv4ll_conflicted(struct arp_state *astate, const struct arp_msg *amsg) if (astate->failed.s_addr == state->addr.s_addr) { struct timespec now, defend; - /* RFC 3927 Section 2.5 */ + /* RFC 3927 Section 2.5 says a defence should + * broadcast an ARP announcement. + * Because the kernel will also unicast a reply to the + * hardware address which requested the IP address + * the other IPv4LL client will receieve two ARP + * messages. + * If another conflict happens within DEFEND_INTERVAL + * then we must drop our address and negotiate a new one. */ defend.tv_sec = state->defend.tv_sec + DEFEND_INTERVAL; defend.tv_nsec = state->defend.tv_nsec; clock_gettime(CLOCK_MONOTONIC, &now); - if (timespeccmp(&defend, &now, >)) { + if (timespeccmp(&defend, &now, >)) logger(ifp->ctx, LOG_WARNING, "%s: IPv4LL %d second defence failed for %s", ifp->name, DEFEND_INTERVAL, inet_ntoa(state->addr)); - ipv4_deladdr(ifp, &state->addr, &inaddr_llmask, 1); - state->down = 1; - script_runreason(ifp, "IPV4LL"); - state->addr.s_addr = INADDR_ANY; - } else { + else if (arp_request(ifp, + state->addr.s_addr, state->addr.s_addr) == -1) + logger(ifp->ctx, LOG_ERR, + "%s: arp_request: %m", __func__); + else { logger(ifp->ctx, LOG_DEBUG, "%s: defended IPv4LL address %s", ifp->name, inet_ntoa(state->addr)); state->defend = now; return; } + + ipv4_deladdr(ifp, &state->addr, &inaddr_llmask, 1); + state->down = 1; + script_runreason(ifp, "IPV4LL"); + state->addr.s_addr = INADDR_ANY; } arp_cancel(astate);