Problem: When a FreeBSD host is sending transmitting at a high packet rate to another host, it's packets may be dropped at the sender when the ARP cache entry times out. When the ARP entry has timed out, and ARP request is sent for the receiving machine on the local network. In the period between the request and the response it elicits, outbound packets are pushed into a buffer that provides storage for just one packet. Therefore, if N packets are pushed down for transmission between the ARP request and it's response, N-1 of them are dropped at the sender. Proposed solution: Outbound packets pass through arpresolve (netinet/if_ether.c). This function makes the decision about whether a packet should be forwarded or buffered awaiting an arp response. It does this on the basis on whether there is a valid ARP cache entry for the receiving machine on the local network. Since it fetches the ARP cache entry and the ARP cache entry has field stating when it will expire, we can trigger an ARP request packet if we are within a finite interval of the expiry time. The response for this pre-expiry ARP request will update the ARP cache entry and push back the expiration time. The additional ARP requests are data-driven, so pre-expiration requests are only sent for hosts that we are sending data to. Entries in the ARP cache that the host is not currently sending to are timed out in the usual manner. The patch referenced below uses a counter to send ARP requests triggered by data packets. The counter ensures that we send at most 1 ARP packet per arpt_down (default = 20) seconds. Thus, if an ARP response is lost or corrupted we have several well spaced attempts at updating the ARP cache entry before it expires. The number of pre-expiry attempts is set to the number of regular ARP request attempts (arp_maxtries, default = 5). The counter variable used is the same counter ARP uses for sending initial ARP requests. It's state is already reset when ARP responses are received so there is no problem in re-using the variable for entries that have not yet expired. A solution very similar to this is discussed in the last three paragraphs of the original ARP RFC (RFC826) and by Casner in kern/25517. Index: if_ether.c =================================================================== RCS file: /home/ncvs/src/sys/netinet/if_ether.c,v retrieving revision 1.64.2.17 diff -u -r1.64.2.17 if_ether.c --- if_ether.c 20 Feb 2002 23:34:09 -0000 1.64.2.17 +++ if_ether.c 9 Mar 2002 21:40:02 -0000 @@ -432,6 +431,21 @@ */ if ((rt->rt_expire == 0 || rt->rt_expire > time_second) && sdl->sdl_family == AF_LINK && sdl->sdl_alen != 0) { + /* + * If entry has an expiry time and it is approaching, + * see if we need to send an ARP request within this + * arpt_down interval. + */ + if ((rt->rt_expire != 0) && + (time_second + (arp_maxtries - la->la_asked) * arpt_down > + rt->rt_expire)) { + arprequest(ifp, + &SIN(rt->rt_ifa->ifa_addr)->sin_addr, + &SIN(dst)->sin_addr, + IF_LLADDR(ifp)); + la->la_asked++; + } + bcopy(LLADDR(sdl), desten, sdl->sdl_alen); return 1; }