diff options
Diffstat (limited to 'cfe/cfe/net/net_arp.c')
-rwxr-xr-x | cfe/cfe/net/net_arp.c | 865 |
1 files changed, 865 insertions, 0 deletions
diff --git a/cfe/cfe/net/net_arp.c b/cfe/cfe/net/net_arp.c new file mode 100755 index 0000000..1c12457 --- /dev/null +++ b/cfe/cfe/net/net_arp.c @@ -0,0 +1,865 @@ +/* ********************************************************************* + * Broadcom Common Firmware Environment (CFE) + * + * Address Resolution Protocol File: net_arp.c + * + * This module implements RFC826, the Address Resolution Protocol. + * + * Author: Mitch Lichtenberg (mpl@broadcom.com) + * + ********************************************************************* + * + * Copyright 2000,2001,2002,2003 + * Broadcom Corporation. All rights reserved. + * + * This software is furnished under license and may be used and + * copied only in accordance with the following terms and + * conditions. Subject to these conditions, you may download, + * copy, install, use, modify and distribute modified or unmodified + * copies of this software in source and/or binary form. No title + * or ownership is transferred hereby. + * + * 1) Any source code used, modified or distributed must reproduce + * and retain this copyright notice and list of conditions + * as they appear in the source file. + * + * 2) No right is granted to use any trade name, trademark, or + * logo of Broadcom Corporation. The "Broadcom Corporation" + * name may not be used to endorse or promote products derived + * from this software without the prior written permission of + * Broadcom Corporation. + * + * 3) THIS SOFTWARE IS PROVIDED "AS-IS" AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING BUT NOT LIMITED TO, ANY IMPLIED + * WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR + * PURPOSE, OR NON-INFRINGEMENT ARE DISCLAIMED. IN NO EVENT + * SHALL BROADCOM BE LIABLE FOR ANY DAMAGES WHATSOEVER, AND IN + * PARTICULAR, BROADCOM SHALL NOT BE LIABLE FOR 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), EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + ********************************************************************* */ + + + +#include "lib_types.h" +#include "lib_string.h" +#include "lib_queue.h" +#include "lib_malloc.h" +#include "lib_printf.h" + +#include "net_ebuf.h" +#include "net_ether.h" + +#include "net_ip.h" +#include "net_ip_internal.h" + + +/* ********************************************************************* + * Prototypes + ********************************************************************* */ + +static int arp_tx_query(ip_info_t *ipi,uint8_t *destaddr); +static void arp_delete(arpentry_t *ae); +static void arp_tx_waiting(arpentry_t *ae); +static arpentry_t *arp_new_entry(ip_info_t *ipi); +static arpentry_t *arp_find_entry(ip_info_t *ipi,uint8_t *ipaddr); +static int arp_rx_query(ip_info_t *ipi,uint8_t *srcaddr, + uint8_t *targethw,uint8_t *targetip); +static int arp_rx_response(ip_info_t *ipi,uint8_t *senderhw, + uint8_t *senderip); +static int arp_rx_callback(ebuf_t *buf,void *ref); + + +/* ********************************************************************* + * arp_tx_query(ipi,destaddr) + * + * Transmit an ARP QUERY message. ARP QUERY messages are sent + * to the Ethernet broadcast address. + * + * Input parameters: + * ipi - IP information + * destaddr - IP address to query + * + * Return value: + * 0 - success + * <0 - failure + ********************************************************************* */ + +static int arp_tx_query(ip_info_t *ipi,uint8_t *destaddr) +{ + ebuf_t *buf; + uint8_t hwaddr[ENET_ADDR_LEN]; + + /* + * Get a buffer. + */ + + buf = eth_alloc(ipi->eth_info,ipi->arp_port); + if (!buf) return -1; + + /* + * fill in the fields + */ + + ebuf_append_u16_be(buf,ARP_HWADDRSPACE_ETHERNET); + ebuf_append_u16_be(buf,PROTOSPACE_IP); + ebuf_append_u8(buf,ENET_ADDR_LEN); + ebuf_append_u8(buf,IP_ADDR_LEN); + ebuf_append_u16_be(buf,ARP_OPCODE_REQUEST); + ebuf_append_bytes(buf,ipi->arp_hwaddr,ENET_ADDR_LEN); + ebuf_append_bytes(buf,ipi->net_info.ip_addr,IP_ADDR_LEN); + memset(hwaddr,0,ENET_ADDR_LEN); + ebuf_append_bytes(buf,hwaddr,ENET_ADDR_LEN); + ebuf_append_bytes(buf,destaddr,IP_ADDR_LEN); + + /* + * Transmit the packet + */ + + eth_send(buf,(uint8_t *)eth_broadcast); + eth_free(buf); + + return 0; +} + + +/* ********************************************************************* + * arp_delete(ae) + * + * Delete an ARP entry. The usual reason for calling this routine + * is to reclaim unused ARP entries, but an ARP entry may be + * manually deleted as well. + * + * Input parameters: + * ae - arp entry + * + * Return value: + * nothing + ********************************************************************* */ + +static void arp_delete(arpentry_t *ae) +{ + ebuf_t *buf; + + /* + * Free any buffers associated with the ARP entry. + */ + + while ((buf = (ebuf_t *) q_deqnext(&(ae->ae_txqueue)))) { + eth_free(buf); + } + + /* + * Reset the important fields + */ + + ae->ae_timer = 0; + ae->ae_usage = 0; + ae->ae_retries = 0; + ae->ae_state = ae_unused; +} + + +/* ********************************************************************* + * _arp_add(ipi,destip,desthw) + * + * Add a new ARP entry to the ARP table [internal routine]. If + * there is no room in the table, some other entry is kicked + * out. + * + * Input parameters: + * ipi - IP information + * destip - target IP address + * desthw - target hardware address + * + * Return value: + * nothing + ********************************************************************* */ + +void _arp_add(ip_info_t *ipi,uint8_t *destip,uint8_t *desthw) +{ + arpentry_t *ae; + + ae = arp_find_entry(ipi,desthw); + if (!ae) { + ae = arp_new_entry(ipi); + } + + memcpy(ae->ae_ipaddr,destip,IP_ADDR_LEN); + memcpy(ae->ae_ethaddr,desthw,ENET_ADDR_LEN); + + ae->ae_retries = 0; + ae->ae_timer = 0; /* keep forever */ + ae->ae_permanent = TRUE; + ae->ae_state = ae_established; + + arp_tx_waiting(ae); +} + + +/* ********************************************************************* + * _arp_lookup(ipi,destip) + * + * Look up an ARP entry [internal routine]. Given an IP address, + * return the hardware address to send the packets to, or + * NULL if no ARP entry exists. + * + * Input parameters: + * ipi - IP information + * destip - destination IP address + * + * Return value: + * pointer to Ethernet hardware address, or NULL if not found + ********************************************************************* */ + +uint8_t *_arp_lookup(ip_info_t *ipi,uint8_t *destip) +{ + arpentry_t *ae; + + ae = arp_find_entry(ipi,destip); + if (ae == NULL) return NULL; + + return ae->ae_ethaddr; +} + +/* ********************************************************************* + * _arp_lookup_and_send(ipi,buf,dest) + * + * Transmit a packet [internal routine]. This routine is called + * by the IP layer when it wants to send a packet. We look + * through the ARP table to find a suitable destination host and + * transmit the packet. If there is no ARP entry, an ARP request + * is transmitted and the packet is saved on a queue for when + * the address is resolved. + * + * Input parameters: + * ipi - IP information + * buf - ebuf to transmit + * dest - destination IP address + * + * Return value: + * 0 if ok + * <0 if error + ********************************************************************* */ + +int _arp_lookup_and_send(ip_info_t *ipi,ebuf_t *buf,uint8_t *dest) +{ + arpentry_t *ae; + + ae = arp_find_entry(ipi,dest); + + if (ae == NULL) { + /* + * No ARP entry yet, create one and send the query + */ + ae = arp_new_entry(ipi); + memcpy(ae->ae_ipaddr,dest,IP_ADDR_LEN); + q_enqueue(&(ae->ae_txqueue),(queue_t *) buf); + ae->ae_retries = ARP_QUERY_RETRIES; + ae->ae_timer = ARP_QUERY_TIMER; + ae->ae_state = ae_arping; + arp_tx_query(ipi,ae->ae_ipaddr); + } + else { + /* + * have an ARP entry. If established, just send the + * packet now. Otherwise, queue on arp queue if there's room. + */ + if (ae->ae_state == ae_established) { + ae->ae_usage++; + if (!ae->ae_permanent) { + ae->ae_timer = ARP_KEEP_TIMER; + } + eth_send(buf,ae->ae_ethaddr); + eth_free(buf); + } + else { + if (q_count(&(ae->ae_txqueue)) < ARP_TXWAIT_MAX) { + q_enqueue(&(ae->ae_txqueue),(queue_t *) buf); + } + else { + /* no room, silently drop */ + eth_free(buf); + } + } + } + + return 0; +} + +/* ********************************************************************* + * arp_tx_waiting(ae) + * + * Transmit all pending packets on the specified ARP entry's + * queue. Packets get queued to an ARP entry when the address + * has not completed resolution. Once resolved, this routine + * is called to flush the packets out. + * + * Input parameters: + * ae - arp entry + * + * Return value: + * nothing + ********************************************************************* */ + +static void arp_tx_waiting(arpentry_t *ae) +{ + ebuf_t *buf; + + while ((buf = (ebuf_t *) q_deqnext(&(ae->ae_txqueue)))) { + eth_send(buf,ae->ae_ethaddr); + eth_free(buf); + } +} + + +/* ********************************************************************* + * arp_new_entry(ipi) + * + * Create a new ARP entry, deleting an active entry if necessary. + * + * Input parameters: + * ipi - IP information + * + * Return value: + * arp entry pointer + ********************************************************************* */ + +static arpentry_t *arp_new_entry(ip_info_t *ipi) +{ + arpentry_t *ae; + arpentry_t *victim = NULL; + int idx; + int minusage = 0x7FFFFFFF; + + /* + * First scan the table and find an empty entry. + */ + + ae = ipi->arp_table; + for (idx = 0; idx < ARP_TABLE_SIZE; idx++,ae++) { + if (ae->ae_state == ae_unused) { + return ae; + } + } + + /* + * If all entries are in use, pick the one with the + * lowest usage count. This isn't very scientific, + * and perhaps should use a timer of some sort. + */ + + ae = ipi->arp_table; + for (idx = 0; idx < ARP_TABLE_SIZE; idx++,ae++) { + if (ae->ae_usage < minusage) { + victim = ae; + minusage = ae->ae_usage; + } + } + + /* + * In the highly unlikely event that all entries have + * overflow values in their usage counters, just take the + * first table entry. + */ + + if (victim == NULL) victim = ipi->arp_table; + + /* + * Clear out the old entry and use it. + */ + + arp_delete(victim); + + return victim; +} + +/* ********************************************************************* + * arp_find_entry(ipi,ipaddr) + * + * Find an ARP entry in the table. Given an IP address, this + * routine locates the corresponding ARP table entry. We also + * reset the expiration timer for the ARP entry, to prevent + * it from from being deleted. + * + * Input parameters: + * ipi - IP info + * ipaddr - IP address we're looking for + * + * Return value: + * arp entry pointer, or NULL if not found + ********************************************************************* */ + +static arpentry_t *arp_find_entry(ip_info_t *ipi,uint8_t *ipaddr) +{ + arpentry_t *ae; + int idx; + + ae = ipi->arp_table; + + for (idx = 0; idx < ARP_TABLE_SIZE; idx++,ae++) { + if (ae->ae_state != ae_unused) { + if (memcmp(ae->ae_ipaddr,ipaddr,IP_ADDR_LEN) == 0) { + if (ae->ae_state == ae_established) + ae->ae_timer = ARP_KEEP_TIMER; + return ae; + } + } + } + return NULL; +} + + +/* ********************************************************************* + * arp_rx_query(ipi,srcaddr,targethw,targetip) + * + * Process a received ARP QUERY message. When we get an ARP, + * transmit a reply to the sender. + * + * Input parameters: + * ipi - IP information + * srcaddr - source IP address + * targethw - target hardware address + * targetip - target IP address (should be our address) + * + * Input parameters: + * 0 if ok + * else <0 = error + ********************************************************************* */ + +static int arp_rx_query(ip_info_t *ipi,uint8_t *srcaddr, + uint8_t *targethw,uint8_t *targetip) +{ + ebuf_t *txbuf; + + /* + * Allocate a packet and form the reply + */ + + txbuf = eth_alloc(ipi->eth_info,ipi->arp_port); + if (!txbuf) return -1; + + ebuf_append_u16_be(txbuf,ARP_HWADDRSPACE_ETHERNET); + ebuf_append_u16_be(txbuf,PROTOSPACE_IP); + ebuf_append_u8(txbuf,ENET_ADDR_LEN); + ebuf_append_u8(txbuf,IP_ADDR_LEN); + ebuf_append_u16_be(txbuf,ARP_OPCODE_REPLY); + + ebuf_append_bytes(txbuf,ipi->arp_hwaddr,ENET_ADDR_LEN); + ebuf_append_bytes(txbuf,ipi->net_info.ip_addr,IP_ADDR_LEN); + + ebuf_append_bytes(txbuf,targethw,ENET_ADDR_LEN); + ebuf_append_bytes(txbuf,targetip,IP_ADDR_LEN); + + eth_send(txbuf,srcaddr); + eth_free(txbuf); + + return 0; +} + +/* ********************************************************************* + * arp_rx_response(ipi,senderhw,senderip) + * + * Process a received ARP RESPONSE packet. This packet contains + * the hardware address of some host we were querying. Fill + * in the rest of the entries in the ARP table and + * transmit any pending packets. + * + * Input parameters: + * ipi - IP information + * senderhw - sender's hardware address + * senderip - sender's IP address + * + * Return value: + * 0 + ********************************************************************* */ + +static int arp_rx_response(ip_info_t *ipi,uint8_t *senderhw,uint8_t *senderip) +{ + int idx; + arpentry_t *ae; + + ae = ipi->arp_table; + + for (idx = 0; idx < ARP_TABLE_SIZE; idx++,ae++) { + if (ae->ae_state != ae_unused) { + if (memcmp(ae->ae_ipaddr,senderip,IP_ADDR_LEN) == 0) { + memcpy(ae->ae_ethaddr,senderhw,ENET_ADDR_LEN); + ae->ae_state = ae_established; + ae->ae_timer = ARP_KEEP_TIMER; + ae->ae_retries = 0; + ae->ae_permanent = FALSE; + arp_tx_waiting(ae); + } + } + } + + return 0; +} + +/* ********************************************************************* + * arp_rx_callback(buf,ref) + * + * Callback for ARP protocol packets. This routine is called + * by the datalink layer when we receive an ARP packet. Parse + * the packet and call any packet-specific processing routines + * + * Input parameters: + * buf - ebuf that we received + * ref - reference data when we opened the port. This is + * our IP information structure + * + * Return value: + * ETH_DROP or ETH_KEEP. + ********************************************************************* */ + +static int arp_rx_callback(ebuf_t *buf,void *ref) +{ + ip_info_t *ipi = ref; + uint16_t t16; + uint8_t t8; + uint16_t opcode; + uint8_t senderip[IP_ADDR_LEN]; + uint8_t senderhw[ENET_ADDR_LEN]; + uint8_t targetip[IP_ADDR_LEN]; + uint8_t targethw[ENET_ADDR_LEN]; + + /* + * ARP packets have to be at least 28 bytes + */ + + if (ebuf_length(buf) < 28) goto drop; + + /* + * We only do the Ethernet hardware space + */ + + ebuf_get_u16_be(buf,t16); + if (t16 != ARP_HWADDRSPACE_ETHERNET) goto drop; + + /* + * We only do the IP protocol space + */ + + ebuf_get_u16_be(buf,t16); + if (t16 != PROTOSPACE_IP) goto drop; + + /* + * The IP and Ethernet address lengths had better be right. + */ + + ebuf_get_u8(buf,t8); + if (t8 != ENET_ADDR_LEN) goto drop; + + ebuf_get_u8(buf,t8); + if (t8 != IP_ADDR_LEN) goto drop; + + /* + * Get the opcode and other fields. + */ + + ebuf_get_u16_be(buf,opcode); + + ebuf_get_bytes(buf,senderhw,ENET_ADDR_LEN); + ebuf_get_bytes(buf,senderip,IP_ADDR_LEN); + ebuf_get_bytes(buf,targethw,ENET_ADDR_LEN); + ebuf_get_bytes(buf,targetip,IP_ADDR_LEN); + + /* + * If it's not for us, just drop it. + */ + + if (memcmp(targetip,ipi->net_info.ip_addr,IP_ADDR_LEN) != 0) goto drop; + + /* + * Dispatch to an appropriate routine. + */ + + switch (opcode) { + case ARP_OPCODE_REQUEST: + arp_rx_query(ipi,ebuf_srcaddr(buf),senderhw,senderip); + break; + case ARP_OPCODE_REPLY: + arp_rx_response(ipi,senderhw,senderip); + break; + } + +drop: + return ETH_DROP; +} + +/* ********************************************************************* + * _arp_timer_tick(ipi) + * + * ARP timer processing [internal routine]. This routine + * counts down timer ticks in the ARP entries, causing retransmits + * or ARP entry expirations to happen. + * + * Input parameters: + * ipi - IP information + * + * Return value: + * nothing + ********************************************************************* */ + +void _arp_timer_tick(ip_info_t *ipi) +{ + int idx; + arpentry_t *ae; + + ae = ipi->arp_table; + + /* + * Walk through the ARP table. + */ + + for (idx = 0; idx < ARP_TABLE_SIZE; idx++,ae++) { + + switch (ae->ae_state) { + case ae_unused: + /* + * Unused entry. Do nothing. + */ + break; + + case ae_arping: + /* + * Entry is arping. Count down the timer, and retransmit + * the ARP message. + */ + ae->ae_timer--; + if (ae->ae_timer <= 0) { + if (ae->ae_retries == 0) { + arp_delete(ae); + } + else { + ae->ae_retries--; + ae->ae_timer = ARP_QUERY_TIMER; + arp_tx_query(ipi,ae->ae_ipaddr); + } + } + break; + + case ae_established: + /* + * Established entry. Count down the timer and + * delete the ARP entry. If the timer is zero + * already, it's a permanent ARP entry. + */ + if (ae->ae_timer == 0) break; + ae->ae_timer--; + if (ae->ae_timer == 0) arp_delete(ae); + break; + } + } + +} + +/* ********************************************************************* + * _arp_send_gratuitous(ipi) + * + * Transmit the "gratuitous arp" (an ARP for our own IP address). + * This is done customarily when an interface is initialized. + * + * Input parameters: + * ipi - IP information + * + * Return value: + * nothing + ********************************************************************* */ + +void _arp_send_gratuitous(ip_info_t *ipi) +{ + if (!ip_addriszero(ipi->net_info.ip_addr)) { + arp_tx_query(ipi,ipi->net_info.ip_addr); + } +} + +/* ********************************************************************* + * _arp_init(ipi) + * + * Initialize the ARP layer [internal routine] + * + * Input parameters: + * ipi - IP information + * + * Return value: + * 0 if ok + * else error code + ********************************************************************* */ + +int _arp_init(ip_info_t *ipi) +{ + int8_t arpproto[2]; + int idx; + arpentry_t *ae; + + /* + * Allocate space for the ARP table + */ + + ipi->arp_table = KMALLOC(ARP_TABLE_SIZE*sizeof(arpentry_t),0); + + if (ipi->arp_table == NULL) return NULL; + + /* + * Initialize the ARP table. + */ + + ae = ipi->arp_table; + for (idx = 0; idx < ARP_TABLE_SIZE; idx++) { + ae->ae_state = ae_unused; + ae->ae_timer = 0; + ae->ae_usage = 0; + ae->ae_retries = 0; + ae->ae_permanent = 0; + q_init(&(ae->ae_txqueue)); + ae++; + } + + /* + * Open the Ethernet portal for ARP packets + */ + + arpproto[0] = (PROTOSPACE_ARP >> 8) & 0xFF; + arpproto[1] = (PROTOSPACE_ARP & 0xFF); + ipi->arp_port = eth_open(ipi->eth_info,ETH_PTYPE_DIX,arpproto,arp_rx_callback,ipi); + + if (ipi->arp_port < 0) { + KFREE(ipi->arp_table); + ipi->arp_table = NULL; + return -1; + } + + /* + * Remember our hardware address + */ + + eth_gethwaddr(ipi->eth_info,ipi->arp_hwaddr); + + /* + * Send a query for ourselves if our IP address is set + */ + + _arp_send_gratuitous(ipi); + + return 0; + +} + + +/* ********************************************************************* + * _arp_uninit(ipi) + * + * Uninitialize the ARP interface. This is called when the + * network module is shut down. + * + * Input parameters: + * ipi - IP information + * + * Return value: + * nothing + ********************************************************************* */ + +void _arp_uninit(ip_info_t *ipi) +{ + int idx; + arpentry_t *ae; + + /* + * Close the Ethernet portal + */ + + eth_close(ipi->eth_info,ipi->arp_port); + + /* + * Clear out the ARP Table. + */ + + ae = ipi->arp_table; + for (idx = 0; idx < ARP_TABLE_SIZE; idx++) { + if (ae->ae_state != ae_unused) arp_delete(ae); + ae++; + } + + /* + * Free up the memory. + */ + + KFREE(ipi->arp_table); + ipi->arp_table = NULL; + ipi->arp_port = -1; +} + + +/* ********************************************************************* + * _arp_enumerate(ipi,entrynum,ipaddr,hwaddr) + * + * Enumerate the ARP table. This is used by user-interface + * routines to display the current contents of the ARP table. + * + * Input parameters: + * ipi - IP information + * entrynum - entry index + * ipaddr - buffer to copy entry's IP address to + * hwaddr - buffer to copy entry's hardware address to + * + * Return value: + * 0 if ok + * else error code + ********************************************************************* */ + +int _arp_enumerate(ip_info_t *ipi,int entrynum,uint8_t *ipaddr,uint8_t *hwaddr) +{ + arpentry_t *ae; + int idx; + + ae = ipi->arp_table; + for (idx = 0; idx < ARP_TABLE_SIZE; idx++) { + if (ae->ae_state != ae_unused) { + if (entrynum == 0) { + memcpy(ipaddr,ae->ae_ipaddr,IP_ADDR_LEN); + memcpy(hwaddr,ae->ae_ethaddr,ENET_ADDR_LEN); + return 0; + } + entrynum--; + } + ae++; + } + + return -1; +} + + +/* ********************************************************************* + * _arp_delete(ipi,ipaddr) + * + * Delete an ARP entry. This routine takes an IP address, looks + * up its ARP table entry, and removes it from the table. + * + * Input parameters: + * ipi - IP information + * ipaddr - IP address whose entry to delete + * + * Return value: + * 0 if entry was deleted + * <0 if entry was not found + ********************************************************************* */ + +int _arp_delete(ip_info_t *ipi,uint8_t *ipaddr) +{ + arpentry_t *ae; + + ae = arp_find_entry(ipi,ipaddr); + + if (ae) { + arp_delete(ae); + return 0; + } + return -1; +} |