code: plan9front

Download patch

ref: 7b186b0779561d1d63d291d10143e8cdfc6e3064
parent: 14aea8266dbbbdcfe9947451e10db4889e3cbcb7
author: cinap_lenrek <cinap_lenrek@felloff.net>
date: Tue Sep 27 16:44:10 EDT 2022

arp: make sure arpresolve() only returns single packet

Due to locking changes, it is possible for arpresolve()
to return multiple packets in the multicast case,
resulting in etherbwrite() to fail assert(bp->list == nil).

Ensure that arpresolve() always returns the first packet
from the hold chain and frees the rest if any.

For arpenter(), we want to transmit the whole chain of
packets, so we detach them from the arp entry before
calling arpresolve() and ignoring its result.

--- a/sys/src/9/ip/arp.c
+++ b/sys/src/9/ip/arp.c
@@ -283,7 +283,7 @@
 
 /*
  * Copy out the mac address from the Arpent.  Return the
- * block waiting to get sent to this mac address.
+ * first block waiting to get sent to this mac address.
  *
  * called with arp locked
  */
@@ -305,6 +305,10 @@
 		rh->a = a;
 	wunlock(arp);
 
+	if(bp != nil){
+		freeblistchain(bp->list);
+		bp->list = nil;
+	}
 	return bp;
 }
 
@@ -350,7 +354,9 @@
 		}
 		a = newarpent(arp, ip, ifc);
 	}
-	bp = arpresolve(arp, a, mac, &rh);	/* unlocks arp */
+	bp = a->hold;
+	a->hold = a->last = nil;
+	arpresolve(arp, a, mac, &rh);	/* unlocks arp */
 	if(version == V4)
 		ip += IPv4off;
 	for(; bp != nil; bp = next){