www.pudn.com > tcpip5151.rar > Arp.c
//----------------------------------------------------------------------------- // Net ARP.C // // This module handles ARP messages and ARP resolution and manages // the ARP cache. Refer to RFC 826 and RFC 1122 //----------------------------------------------------------------------------- #include#include #include #include #include "net.h" #include "eth.h" #include "serial.h" #include "ip.h" #include "arp.h" extern WAIT xdata wait; extern UCHAR code my_hwaddr[]; extern UCHAR code broadcast_hwaddr[]; extern ULONG code my_ipaddr; extern ULONG code my_subnet; extern ULONG code gateway_ipaddr; extern UCHAR idata debug; ARP_CACHE xdata arp_cache[CACHESIZE]; UCHAR waiting_for_arp; void init_arp(void) { memset(arp_cache, 0, sizeof(arp_cache)); memset(&wait, 0, sizeof(wait)); waiting_for_arp = FALSE; } //------------------------------------------------------------------------ // This is called every 60 seconds to age the ARP cache // If an entry times out then it is deleted from the cache // See "TCP/IP Illustrated, Volume 1" Sect 4.3 //------------------------------------------------------------------------ void age_arp_cache(void) { UCHAR i; for (i=0; i < CACHESIZE; i++) { if ((arp_cache[i].ipaddr != 0) && (arp_cache[i].timer)) { arp_cache[i].timer--; if (arp_cache[i].timer == 0) { // Timed out so clear out cache entry // Do not need to zero hwaddr arp_cache[i].ipaddr = 0; if (debug) printf("ARP: Aged out a cache entry\n"); } } } } //------------------------------------------------------------------------ // This allocates memory for the entire outgoing message, // including eth and ip headers, then builds an outgoing // ARP response message // See "TCP/IP Illustrated, Volume 1" Sect 4.4 //------------------------------------------------------------------------ void arp_send(UCHAR * hwaddr, ULONG ipaddr, UCHAR msg_type) { UCHAR xdata * outbuf; ARP_HEADER xdata * arp; // Allocate memory for entire outgoing message including // eth header. Always 42 bytes outbuf = (UCHAR xdata *)malloc(42); if (outbuf == NULL) { if (debug) printf("ARP: Oops, out of memory\r"); return; } // Allow 14 bytes for the ethernet header arp = (ARP_HEADER xdata *)(outbuf + 14); arp->hardware_type = DIX_ETHERNET; arp->protocol_type = IP_PACKET; arp->hwaddr_len = 6; arp->ipaddr_len = 4; arp->message_type = (UINT)msg_type; // My hardware address and IP addresses memcpy(arp->source_hwaddr, my_hwaddr, 6); arp->source_ipaddr = my_ipaddr; // Destination hwaddr and dest IP addr if (msg_type == ARP_REQUEST) memset(arp->dest_hwaddr, 0, 6); else memcpy(arp->dest_hwaddr, hwaddr, 6); arp->dest_ipaddr = ipaddr; // If request then the message is a brodcast, if a response then // send to specified hwaddr // ARP payload size is always 28 bytes if (msg_type == ARP_REQUEST) eth_send(outbuf, broadcast_hwaddr, ARP_PACKET, 28); else eth_send(outbuf, hwaddr, ARP_PACKET, 28); } //------------------------------------------------------------------------ // This re-sends an ARP request if there was no response to // the first one. It is called every 0.5 seconds. If there // is no response after 2 re-tries, the datagram that IP was // trying to send is deleted //----------------------------------------------------------------------- void arp_retransmit(void) { static UCHAR idata retries = 0; if ((waiting_for_arp) && (wait.timer)) { wait.timer--; if (wait.timer == 0) { retries++; if (retries <= 2) { if (debug) printf("ARP: Re-sending ARP broadcast\n"); arp_send(NULL, wait.ipaddr, ARP_REQUEST); wait.timer = ARP_TIMEOUT; } else { if (debug) printf("ARP: Gave up waiting for response\n"); wait.timer = 0; waiting_for_arp = 0; free(wait.buf); } } } } //------------------------------------------------------------------------ // Find the ethernet hardware address for the given ip address // If destination IP is on my subnet then we want the eth // address of destination, otherwise we want eth addr of gateway. // Look in ARP cache first. If not found there, send ARP request. // Return pointer to the hardware address or NULL if not found // See "TCP/IP Illustrated, Volume 1" Sect 4.5 //------------------------------------------------------------------------ UCHAR xdata * arp_resolve(ULONG dest_ipaddr) { UCHAR i; // If destination IP is not on my subnet then we really want eth addr // of gateway, not destination IP if ((dest_ipaddr ^ my_ipaddr) & my_subnet) { if (gateway_ipaddr == 0) { if (debug) printf("ARP: Error, gateway addr not set\n"); return (NULL); } else dest_ipaddr = gateway_ipaddr; } // See if IP addr of interest is in ARP cache for (i=0; i < CACHESIZE; i++) { if (arp_cache[i].ipaddr == dest_ipaddr) return (&arp_cache[i].hwaddr[0]); } if (debug) printf("ARP: IP addr not found in cache\n"); if (debug) printf("ARP: Sending an ARP broadcast\n"); // Not in cache so broadcast ARP request arp_send(NULL, dest_ipaddr, ARP_REQUEST); // Set a flag to indicate that an IP datagram is waiting // to be sent waiting_for_arp = TRUE; // Null means that we have sent an ARP request return (NULL); } //------------------------------------------------------------------------ // This handles incoming ARP messages // See "TCP/IP Illustrated, Volume 1" Sect 4.4 // Todo: Resolve problem of trying to add to a full cache //------------------------------------------------------------------------ void arp_rcve(UCHAR xdata * inbuf) { UCHAR idata i, cached, oldest; UINT idata minimum; ARP_HEADER xdata * arp; arp = (ARP_HEADER xdata *)(inbuf + 14); cached = FALSE; // Print message if (debug) { if (arp->message_type == ARP_REQUEST) printf("ARP: Request rcvd\n"); else printf("ARP: Response rcvd\n"); } // Validate incoming frame if ((arp->hardware_type != DIX_ETHERNET) || (arp->protocol_type != IP_PACKET)) return; // Search ARP cache for senders IP address // If found, update entry and restart timer for (i=0; i < CACHESIZE; i++) { if (arp_cache[i].ipaddr == arp->source_ipaddr) { memcpy(&arp_cache[i].hwaddr[0], &arp->source_hwaddr[0], 6); arp_cache[i].timer = CACHETIME; cached = TRUE; if (debug) printf("ARP: Cache entry updated\n"); break; } } if (arp->dest_ipaddr != my_ipaddr) return; // At this point we know the the frame is addressed to me // If not already in cache then add entry and start timer if (cached == FALSE) { // Find first blank space and add entry // Blank entries are indicated by ip addr = 0 for (i=0; i < CACHESIZE; i++) { if (arp_cache[i].ipaddr == 0) { arp_cache[i].ipaddr = arp->source_ipaddr; memcpy(&arp_cache[i].hwaddr[0], &arp->source_hwaddr[0], 6); arp_cache[i].timer = CACHETIME; if (debug) printf("ARP: New cache entry added\n"); break; } } // If no blank entries in arp cache then sort cache // to find oldest entry and replace it if (i == CACHESIZE) { // Oldest entry is the one with lowest timer value minimum = 0xFFFF; for (i=0; i < CACHESIZE; i++) { if (arp_cache[i].timer < minimum) { minimum = arp_cache[i].timer; oldest = i; } } // "oldest" is now index of oldest entry, so replace it arp_cache[oldest].ipaddr = arp->source_ipaddr; memcpy(&arp_cache[oldest].hwaddr[0], &arp->source_hwaddr[0], 6); arp_cache[oldest].timer = CACHETIME; if (debug) printf("ARP: Cache full, so replaced oldest\n"); } } // If we are waiting for an arp response and the arp response // that just came in is addressed to me and is from the host // we are waiting for, then send the message-in-waiting if (arp->message_type == ARP_RESPONSE) { if ((waiting_for_arp) && (wait.ipaddr == arp->source_ipaddr)) { waiting_for_arp = FALSE; ip_send(wait.buf, wait.ipaddr, wait.proto_id, wait.len); } } else if (arp->message_type == ARP_REQUEST) { // Send ARP response if (debug) printf("ARP: Sending response\n"); arp_send(arp->source_hwaddr, arp->source_ipaddr, ARP_RESPONSE); } }