Friday, October 23, 2009

21.10 arpresolve Function

Team-Fly
 

 

TCP/IP Illustrated, Volume 2: The Implementation
By
Gary R. Wright, W. Richard Stevens
Table of Contents
Chapter 21. 
ARP: Address Resolution Protocol


21.10 arpresolve Function


We saw in Figure 4.16 that ether_output calls arpresolve to obtain the Ethernet address for an IP address. arpresolve returns 1 if the destination Ethernet address is known, allowing ether_output to queue the IP datagram on the interface's output queue. A return value of 0 means arpresolve does not know the Ethernet address. The datagram is "held" by arpresolve (using the la_hold member of the llinfo_arp structure) and an ARP request is sent. If and when an ARP reply is received, in_arpinput completes the ARP entry and sends the held datagram.


arpresolve must also avoid ARP flooding, that is, it must not repeatedly send ARP requests at a high rate when an ARP reply is not received. This can happen when several datagrams are sent to the same unresolved IP address before an ARP reply is received, or when a datagram destined for an unresolved address is fragmented, since each fragment is sent to ether_output as a separate packet. Section 11.9 of Volume 1 contains an example of ARP flooding caused by fragmentation, and discusses the associated problems. Figure 21.23 shows the first half of arpresolve.



Figure 21.23. arpresolve function: find ARP entry if required.


252-261

dst is a pointer to a sockaddr_in containing the destination IP address and desten is an array of 6 bytes that is filled in with the corresponding Ethernet address, if known.


Handle broadcast and multicast destinations


262-270

If the M_BCAST flag of the mbuf is set, the destination is filled in with the Ethernet broadcast address and the function returns 1. If the M_MCAST flag is set, the ETHER_MAP_IP_MULTICAST macro (Figure 12.6) converts the class D address into the corresponding Ethernet address.



Get pointer to llinfo_arp structure


271-276

The destination address is a unicast address. If a pointer to a routing table entry is passed by the caller, la is set to the corresponding llinfo_arp structure. Otherwise arplookup searches the routing table for the specified IP address. The second argument is 1, telling arplookup to create the entry if it doesn't already exist; the third argument is 0, which means don't look for a proxy ARP entry.


277-281

If either rt or la are null pointers, one of the allocations failed, since arplookup should have created an entry if one didn't exist. An error message is logged, the packet released, and the function returns 0.


Figure 21.24 contains the last half of arpresolve. It checks whether the ARP entry is still valid, and, if not, sends an ARP request.



Figure 21.24. arpresolve function: check if ARP entry valid, send ARP request if not.



Check ARP entry for validity


282-291

Even though an ARP entry is located, it must be checked for validity. The entry is valid if the following conditions are all true:


  1. the entry is permanent (the expiration time is 0) or the expiration time is greater than the current time, and

  2. the family of the socket address structure pointed to by rt_gateway is AF_LINK, and

  3. the link-level address length (sdl_alen) is nonzero.


Recall that arptfree invalidated an ARP entry that was still referenced by setting sdl_alen to 0. If the entry is valid, the Ethernet address contained in the sockaddr_dl is copied into desten and the function returns 1.



Hold only most recent IP datagram


292-299

At this point an ARP entry exists but it does not contain a valid Ethernet address. An ARP request must be sent. First the pointer to the mbuf chain is saved in la_hold, after releasing any mbuf chain that was already pointed to by la_hold. This means that if multiple IP datagrams are sent quickly to a given destination, and an ARP entry does not already exist for the destination, during the time it takes to send an ARP request and receive a reply only the last datagram is held, and all prior ones are discarded. An example that generates this condition is NFS. If NFS sends an 8500-byte IP datagram that is fragmented into six IP fragments, and if all six fragments are sent by ip_output to ether_output in the time it takes to send an ARP request and receive a reply, the first five fragments are discarded and only the final fragment is sent when the reply is received. This in turn causes an NFS timeout, and a retransmission of all six fragments.



Send ARP request but avoid ARP flooding


300-314

RFC 1122 requires ARP to avoid sending ARP requests to a given destination at a high rate when a reply is not received. The technique used by Net/3 to avoid ARP flooding is as follows.


  • Net/3 never sends more than one ARP request in any given second to a destination.

  • If a reply is not received after five ARP requests (i.e., after about 5 seconds), the RTF_REJECT flag in the routing table is set and the expiration time is set for 20 seconds in the future. This causes ether_output to refuse to send IP datagrams to this destination for 20 seconds, returning EHOSTDOWN or EHOSTUNREACH instead (Figure 4.15).

  • After the 20-second pause in ARP requests, arpresolve will send ARP requests to that destination again.


If the expiration time is nonzero (i.e., this is not a permanent entry) the RTF_REJECT flag is cleared, in case it had been set earlier to avoid flooding. The counter la_asked counts the number of consecutive times an ARP request has been sent to this destination. If the counter is 0 or if the expiration time does not equal the current time (looking only at the seconds portion of the current time), an ARP request might be sent. This comparison avoids sending more than one ARP request during any second. The expiration time is then set to the current time in seconds (i.e., the microseconds portion, time.tv_usec is ignored).


The counter is compared to the limit of 5 (arp_maxtries) and then incremented. If the value was less than 5, arpwhohas sends the request. If the request equals 5, however, ARP has reached its limit: the RTF_REJECT flag is set, the expiration time is set to 20 seconds in the future, and the counter la_asked is reset to 0.


Figure 21.25 shows an example to explain further the algorithm used by arpresolve and ether_output to avoid ARP flooding.



Figure 21.25. Algorithm used to avoid ARP flooding.


We show 26 seconds of time, labeled 10 through 36. We assume a process is sending an IP datagram every one-half second, causing two datagrams to be sent every second. The datagrams are numbered 1 through 52. We also assume that the destination host is down, so there are no replies to the ARP requests. The following actions take place:


  • We assume la_asked is 0 when datagram 1 is written by the process. la_hold is set to point to datagram 1, rt_expire is set to the current time (10), la_asked becomes 1, and an ARP request is sent. The function returns 0.

  • When datagram 2 is written by the process, datagram 1 is discarded and la_hold is set to point to datagram 2. Since rt_expire equals the current time (10), nothing else happens (an ARP request is not sent) and the function returns 0.

  • When datagram 3 is written, datagram 2 is discarded and la_hold is set to point to datagram 3. The current time (11) does not equal rt_expire (10), so rt_expire is set to 11. la_asked is less than 5, so la_asked becomes 2 and an ARP request is sent.

  • When datagram 4 is written, datagram 3 is discarded and la_hold is set to point to datagram 4. Since rt_expire equals the current time (11), nothing else happens and the function returns 0.

  • Similar actions occur for datagrams 5 through 10. After datagram 9 causes an ALP request to be sent, la_asked is 5.

  • When datagram 11 is written, datagram 10 is discarded and la_hold is set to point to datagram 11. The current time (15) does not equal rt_expire (14), so rt_expire is set to 15. la_asked is no longer less than 5, so the ARP flooding avoidance algorithm takes place: RTF_REJECT flag is set, rt_expire is set to 35 (20 seconds in the future), and la_asked is reset to 0. The function returns 0.

  • When datagram 12 is written, ether_output notices that the RTF_REJECT flag is set and that the current time is less than rt_expire (35) causing EHOSTDOWN to be returned to the sender (normally ip_output).

  • The EHOSTDOWN error is returned for datagrams 13 through 50.

  • When datagram 51 is written, even though the RTF_REJECT flag is set ether_output does not return the error because the current time (35) is no longer less than rt_expire (35). arpresolve is called and the entire process starts over again: five ARP requests are sent in 5 seconds, followed by a 20-second pause. This continues until the sending process gives up or the destination host responds to an ARP request.





    Team-Fly
     

     
    Top
     


    No comments:

    Post a Comment