changeset 3522:3159a8c0ddb6 draft

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].
author Roy Marples <roy@marples.name>
date Wed, 20 Apr 2016 08:29:08 +0000
parents 6fce403d7a88
children f574f4ded79b
files arp.c arp.h ipv4ll.c
diffstat 3 files changed, 21 insertions(+), 8 deletions(-) [+]
line wrap: on
line diff
--- a/arp.c	Mon Apr 18 12:49:35 2016 +0000
+++ b/arp.c	Wed Apr 20 08:29:08 2016 +0000
@@ -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];
--- a/arp.h	Mon Apr 18 12:49:35 2016 +0000
+++ b/arp.h	Wed Apr 20 08:29:08 2016 +0000
@@ -77,6 +77,7 @@
 	((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 *);
--- a/ipv4ll.c	Mon Apr 18 12:49:35 2016 +0000
+++ b/ipv4ll.c	Wed Apr 20 08:29:08 2016 +0000
@@ -259,26 +259,38 @@
 	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);