]> git.ipfire.org Git - thirdparty/dhcpcd.git/commitdiff
RFC 3927 Section 2.5 says a defence of an address should ARP announce it.
authorRoy Marples <roy@marples.name>
Wed, 20 Apr 2016 08:29:08 +0000 (08:29 +0000)
committerRoy Marples <roy@marples.name>
Wed, 20 Apr 2016 08:29:08 +0000 (08:29 +0000)
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].

arp.c
arp.h
ipv4ll.c

diff --git a/arp.c b/arp.c
index a17b5f1aaee65b31ac63c5bee257da0b09f349e3..49d392dbb19d15c5baeb2bdd5f094425d1f25650 100644 (file)
--- 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 6eb3808cc99c9a58ff0339d4d4cef7ab9f3e84a0..c63cc7309332a7dd9147d0587549bf7374e8f3dd 100644 (file)
--- 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 *);
index 7be32d64678269b1e7bbafd190dbdd12a7b8f223..d33762d96cc20aba92be78e5add120a04c4f1eec 100644 (file)
--- 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);