/** * \addtogroup uip * @{ */ /** * \defgroup uiparp uIP Address Resolution Protocol * @{ * * The Address Resolution Protocol ARP is used for mapping between IP * addresses and link level addresses such as the Ethernet MAC * addresses. ARP uses broadcast queries to ask for the link level * address of a known IP address and the host which is configured with * the IP address for which the query was meant, will respond with its * link level address. * * \note This ARP implementation only supports Ethernet. */ /** * \file * Implementation of the ARP Address Resolution Protocol. * \author Adam Dunkels * */ /* * Copyright (c) 2001-2003, Adam Dunkels. * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. The name of the author may not be used to endorse or promote * products derived from this software without specific prior * written permission. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * * This file is part of the uIP TCP/IP stack. * * */ #include "uip_pbuf.h" #include "uip_netif.h" #include "uip_arp.h" #include #if UIP_LOGGING == 1 #include #define UIP_LOG(m) uip_log(__FILE__,__LINE__,m) #else #define UIP_LOG(m) #endif /* UIP_LOGGING == 1 */ #if UIP_STATISTICS == 1 struct uip_stats uip_stat; #define UIP_STAT(s) s #else #define UIP_STAT(s) #endif /* UIP_STATISTICS == 1 */ #define ARP_TRY_HARD 0x01 #define ARP_MAXAGE 240 #define ARP_MAXPENDING 2 #define ARP_REQUEST 1 #define ARP_REPLY 2 #define ARP_HWTYPE_ETH 1 #define ARPH_HWLEN(hdr) (ntohs((hdr)->_hwlen_protolen) >> 8) #define ARPH_PROTOLEN(hdr) (ntohs((hdr)->_hwlen_protolen) & 0xff) #define ARPH_HWLEN_SET(hdr, len) (hdr)->_hwlen_protolen = htons(ARPH_PROTOLEN(hdr) | ((len) << 8)) #define ARPH_PROTOLEN_SET(hdr, len) (hdr)->_hwlen_protolen = htons((len) | (ARPH_HWLEN(hdr) << 8)) enum arp_state { ARP_STATE_EMPTY, ARP_STATE_PENDING, ARP_STATE_STABLE, ARP_STATE_EXPIRED }; struct arp_entry { struct uip_ip_addr ipaddr; struct uip_eth_addr ethaddr; enum arp_state state; u8_t time; }; static const struct uip_eth_addr ethbroadcast = {{0xff,0xff,0xff,0xff,0xff,0xff}}; static struct arp_entry arp_table[UIP_ARPTAB_SIZE]; /*-----------------------------------------------------------------------------------*/ /** * Initialize the ARP module. * */ /*-----------------------------------------------------------------------------------*/ void uip_arp_init(void) { s32_t i; for(i = 0; i < UIP_ARPTAB_SIZE; ++i) { arp_table[i].state = ARP_STATE_EMPTY; arp_table[i].time = 0; } } /*-----------------------------------------------------------------------------------*/ /** * Periodic ARP processing function. * * This function performs periodic timer processing in the ARP module * and should be called at regular intervals. The recommended interval * is 10 seconds between the calls. * */ /*-----------------------------------------------------------------------------------*/ void uip_arp_timer(void) { u8_t i; for(i=0;i=ARP_MAXAGE) { arp_table[i].state = ARP_STATE_EXPIRED; } else if(arp_table[i].state==ARP_STATE_PENDING) { if(arp_table[i].time>=ARP_MAXPENDING) arp_table[i].state = ARP_STATE_EXPIRED; } if(arp_table[i].state==ARP_STATE_EXPIRED) arp_table[i].state = ARP_STATE_EMPTY; } } static s8_t uip_arp_findentry(struct uip_ip_addr *ipaddr,u8_t flags) { s8_t old_pending = UIP_ARPTAB_SIZE, old_stable = UIP_ARPTAB_SIZE; s8_t empty = UIP_ARPTAB_SIZE; u8_t i = 0,age_pending = 0,age_stable = 0; /* Walk through the ARP mapping table and try to find an entry to update. If none is found, the IP -> MAC address mapping is inserted in the ARP table. */ for(i = 0; i < UIP_ARPTAB_SIZE; ++i) { if(empty==UIP_ARPTAB_SIZE && arp_table[i].state==ARP_STATE_EMPTY) { empty = i; } else if(arp_table[i].state==ARP_STATE_PENDING) { if(ipaddr && ip_addr_cmp(ipaddr,&arp_table[i].ipaddr)) return i; else if(arp_table[i].time>=age_pending) { old_pending = i; age_pending = arp_table[i].time; } } else if(arp_table[i].state==ARP_STATE_STABLE) { if(ipaddr && ip_addr_cmp(ipaddr,&arp_table[i].ipaddr)) return i; else if(arp_table[i].time>=age_stable) { old_stable = i; age_stable = arp_table[i].time; } } } if(empty==UIP_ARPTAB_SIZE && !(flags&ARP_TRY_HARD)) return UIP_ERR_MEM; if(emptyhwaddr_len;k++) arp_table[i].ethaddr.addr[k] = ethaddr->addr[k]; return UIP_ERR_OK; } /*-----------------------------------------------------------------------------------*/ /** * ARP processing for incoming IP packets * * This function should be called by the device driver when an IP * packet has been received. The function will check if the address is * in the ARP cache, and if so the ARP cache entry will be * refreshed. If no ARP cache entry was found, a new one is created. * * This function expects an IP packet with a prepended Ethernet header * in the uip_buf[] buffer, and the length of the packet in the global * variable uip_len. */ /*-----------------------------------------------------------------------------------*/ void uip_arp_ipin(struct uip_netif *netif,struct uip_pbuf *p) { struct uip_ethip_hdr *hdr; hdr = p->payload; if(!ip_addr_netcmp(&hdr->ip.src,&netif->ip_addr,&netif->netmask)) return; uip_arp_update(netif,&hdr->ip.src,&hdr->ethhdr.src,0); } /*-----------------------------------------------------------------------------------*/ /** * ARP processing for incoming ARP packets. * * This function should be called by the device driver when an ARP * packet has been received. The function will act differently * depending on the ARP packet type: if it is a reply for a request * that we previously sent out, the ARP cache will be filled in with * the values from the ARP reply. If the incoming ARP packet is an ARP * request for our IP address, an ARP reply packet is created and put * into the uip_buf[] buffer. * * When the function returns, the value of the global variable uip_len * indicates whether the device driver should send out a packet or * not. If uip_len is zero, no packet should be sent. If uip_len is * non-zero, it contains the length of the outbound packet that is * present in the uip_buf[] buffer. * * This function expects an ARP packet with a prepended Ethernet * header in the uip_buf[] buffer, and the length of the packet in the * global variable uip_len. */ /*-----------------------------------------------------------------------------------*/ void uip_arp_arpin(struct uip_netif *netif,struct uip_eth_addr *ethaddr,struct uip_pbuf *p) { u8_t i,for_us; struct uip_ip_addr sipaddr,dipaddr; struct uip_arp_hdr *hdr; if(p->tot_lenpayload; *(struct uip_ip_addr2*)((void*)&sipaddr) = hdr->sipaddr; *(struct uip_ip_addr2*)((void*)&dipaddr) = hdr->dipaddr; if(netif->ip_addr.addr==0) for_us = 0; else for_us = ip_addr_cmp(&dipaddr,&netif->ip_addr); if(for_us) uip_arp_update(netif,&sipaddr,&hdr->shwaddr,ARP_TRY_HARD); else uip_arp_update(netif,&sipaddr,&hdr->shwaddr,0); switch(htons(hdr->opcode)) { case ARP_REQUEST: if(for_us) { hdr->opcode = htons(ARP_REPLY); hdr->dipaddr = hdr->sipaddr; hdr->sipaddr = *(struct uip_ip_addr2*)((void*)&netif->ip_addr); for(i=0;ihwaddr_len;i++) { hdr->dhwaddr.addr[i] = hdr->shwaddr.addr[i]; hdr->shwaddr.addr[i] = ethaddr->addr[i]; hdr->ethhdr.dest.addr[i] = hdr->dhwaddr.addr[i]; hdr->ethhdr.src.addr[i] = ethaddr->addr[i]; } hdr->hwtype = htons(ARP_HWTYPE_ETH); ARPH_HWLEN_SET(hdr,netif->hwaddr_len); hdr->protocol = htons(UIP_ETHTYPE_IP); ARPH_PROTOLEN_SET(hdr,sizeof(struct uip_ip_addr)); netif->linkoutput(netif,p); } else { UIP_LOG("uip_arp_arpin: ip packet not for us.\n"); } break; case ARP_REPLY: break; default: UIP_LOG("uip_arp_arpin: ARP unknown opcode type.\n"); break; } uip_pbuf_free(p); } /*-----------------------------------------------------------------------------------*/ /** * Prepend Ethernet header to an outbound IP packet and see if we need * to send out an ARP request. * * This function should be called before sending out an IP packet. The * function checks the destination IP address of the IP packet to see * what Ethernet MAC address that should be used as a destination MAC * address on the Ethernet. * * If the destination IP address is in the local network (determined * by logical ANDing of netmask and our IP address), the function * checks the ARP cache to see if an entry for the destination IP * address is found. If so, an Ethernet header is prepended and the * function returns. If no ARP cache entry is found for the * destination IP address, the packet in the uip_buf[] is replaced by * an ARP request packet for the IP address. The IP packet is dropped * and it is assumed that they higher level protocols (e.g., TCP) * eventually will retransmit the dropped packet. * * If the destination IP address is not on the local network, the IP * address of the default router is used instead. * * When the function returns, a packet is present in the uip_buf[] * buffer, and the length of the packet is in the global variable * uip_len. */ /*-----------------------------------------------------------------------------------*/ s8_t uip_arp_out(struct uip_netif *netif,struct uip_ip_addr *ipaddr,struct uip_pbuf *q) { u8_t i; struct uip_eth_addr *dest,*srcaddr,mcastaddr; struct uip_eth_hdr *ethhdr; if(uip_pbuf_header(q,sizeof(struct uip_eth_hdr))!=0) { UIP_LOG("uip_arp_out: could not allocate room for header.\n"); return UIP_ERR_BUF; } dest = NULL; if(ip_addr_isbroadcast(ipaddr,netif)) { dest = (struct uip_eth_addr*)ðbroadcast; } else if(ip_addr_ismulticast(ipaddr)) { /* Hash IP multicast address to MAC address.*/ mcastaddr.addr[0] = 0x01; mcastaddr.addr[1] = 0x00; mcastaddr.addr[2] = 0x5e; mcastaddr.addr[3] = ip4_addr2(ipaddr) & 0x7f; mcastaddr.addr[4] = ip4_addr3(ipaddr); mcastaddr.addr[5] = ip4_addr4(ipaddr); /* destination Ethernet address is multicast */ dest = &mcastaddr; } else { if(!ip_addr_netcmp(ipaddr,&netif->ip_addr,&netif->netmask)) { if(netif->gw.addr!=0) ipaddr = &netif->gw; else return UIP_ERR_RTE; } return uip_arp_arpquery(netif,ipaddr,q); } srcaddr = (struct uip_eth_addr*)netif->hwaddr; ethhdr = q->payload; for(i=0;ihwaddr_len;i++) { ethhdr->dest.addr[i] = dest->addr[i]; ethhdr->src.addr[i] = srcaddr->addr[i]; } ethhdr->type = htons(UIP_ETHTYPE_IP); return netif->linkoutput(netif,q); } /*-----------------------------------------------------------------------------------*/ s8_t uip_arp_arpquery(struct uip_netif *netif,struct uip_ip_addr *ipaddr,struct uip_pbuf *q) { s8_t i,k; s8_t err = UIP_ERR_MEM; struct uip_eth_addr *srcaddr = (struct uip_eth_addr*)netif->hwaddr; if(ip_addr_isbroadcast(ipaddr,netif) || ip_addr_ismulticast(ipaddr) || ip_addr_isany(ipaddr)) return UIP_ERR_ARG; i = uip_arp_findentry(ipaddr,ARP_TRY_HARD); if(i<0) return i; if(arp_table[i].state==ARP_STATE_EMPTY) arp_table[i].state = ARP_STATE_PENDING; if(arp_table[i].state==ARP_STATE_PENDING || q==NULL) err = uip_arp_arprequest(netif,ipaddr); if(q!=NULL) { if(arp_table[i].state==ARP_STATE_STABLE) { struct uip_eth_hdr *hdr = q->payload; for(k=0;khwaddr_len;k++) { hdr->dest.addr[k] = arp_table[i].ethaddr.addr[k]; hdr->src.addr[k] = srcaddr->addr[k]; } hdr->type = htons(UIP_ETHTYPE_IP); err = netif->linkoutput(netif,q); } else if(arp_table[i].state==ARP_STATE_PENDING) { UIP_LOG("uip_arp_query: Ethernet destination address unknown, queueing disabled, packet dropped.\n"); } } return err; } s8_t uip_arp_arprequest(struct uip_netif *netif,struct uip_ip_addr *ipaddr) { s8_t k; s8_t err = UIP_ERR_MEM; struct uip_arp_hdr *hdr; struct uip_pbuf *p; struct uip_eth_addr *srcaddr = (struct uip_eth_addr*)netif->hwaddr; p = uip_pbuf_alloc(UIP_PBUF_LINK,sizeof(struct uip_arp_hdr),UIP_PBUF_RAM); if(p==NULL) return err; hdr = p->payload; hdr->opcode = htons(ARP_REQUEST); for(k=0;khwaddr_len;k++) { hdr->shwaddr.addr[k] = srcaddr->addr[k]; hdr->dhwaddr.addr[k] = 0; } hdr->dipaddr = *(struct uip_ip_addr2*)((void*)ipaddr); hdr->sipaddr = *(struct uip_ip_addr2*)((void*)&netif->ip_addr); hdr->hwtype = htons(ARP_HWTYPE_ETH); ARPH_HWLEN_SET(hdr,netif->hwaddr_len); hdr->protocol = htons(UIP_ETHTYPE_IP); ARPH_PROTOLEN_SET(hdr,sizeof(struct uip_ip_addr)); for(k=0;khwaddr_len;k++) { hdr->ethhdr.dest.addr[k] = 0xff; hdr->ethhdr.src.addr[k] = srcaddr->addr[k]; } hdr->ethhdr.type = htons(UIP_ETHTYPE_ARP); err = netif->linkoutput(netif,p); uip_pbuf_free(p); return err; } /** @} */ /** @} */