Benoît Locher / mbedNet
Revision:
1:f4040665bc61
Child:
5:3cd83fcb1467
diff -r 19f5f51584de -r f4040665bc61 ARP.cpp
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/ARP.cpp	Sun Jun 12 19:17:11 2011 +0000
@@ -0,0 +1,568 @@
+/*
+ * $Id: ARP.c 29 2011-06-11 14:53:08Z benoit $
+ * $Author: benoit $
+ * $Date: 2011-06-11 16:53:08 +0200 (sam., 11 juin 2011) $
+ * $Rev: 29 $
+ * 
+ * 
+ * 
+ * 
+ * 
+ */
+ 
+#include "ARP.h"
+#include "Ethernet.h"
+#include "Debug.h"
+#include "IPv4.h"
+#include <string.h>
+
+
+#define    DEBUG_CURRENT_MODULE_NAME    "ARP"
+#define    DEBUG_CURRENT_MODULE_ID        DEBUG_MODULE_ARP
+
+
+enum ARP_EntryStatus
+{
+    ARP_Entry_Free = 0,
+    ARP_Entry_PendingReply,
+    ARP_Entry_Dynamic,
+    ARP_Entry_Expired,
+    ARP_Entry_Static,
+    ARP_Entry_Count,
+};
+typedef enum ARP_EntryStatus ARP_EntryStatus_t;
+
+
+const char *arpEntryStatusText[ARP_Entry_Count] = 
+{
+    "free",
+    "pending",
+    "dynamic",
+    "expired",
+    "static",
+};
+
+struct ARP_CacheEntry
+{
+    IPv4_Addr_t            ipv4Addr;
+    Ethernet_Addr_t        ethernetAddr;
+    NetIF_t             *netIF;
+    uint16_t            age;
+    ARP_EntryStatus_t    status;
+    RTOS_Mutex_t        mutex;
+};
+typedef struct ARP_CacheEntry ARP_CacheEntry_t;
+
+
+#pragma push
+#pragma pack(1)
+static struct 
+{
+    Ethernet_Header_t    ethernetHeader;
+    ARP_Header_t        arpHeader;
+    ARP_IPv4Data_t        ipv4ARPData;
+} arp_FullIPv4Packet;
+#pragma pop
+
+
+static void Init(void);
+static void Handler(NetIF_t *netIF, Packet_t *packet);
+static void PeriodicFunction(void);
+static ARP_CacheEntry_t *GetReusableEntry(void);
+static ARP_CacheEntry_t *GetEntryByIPv4Address(IPv4_Addr_t address);
+
+
+static ARP_CacheEntry_t        arp_CacheTable[ARP_CACHE_MAX_ENTRIES];
+
+
+Protocol_Handler_t arp = 
+{
+    PROTOCOL_INDEX_NOT_INITIALIZED,                /* Always PROTOCOL_INDEX_NOT_INITIALIZED at initialization */
+    Protocol_ID_ARP,                             /* Protocol ID */
+    htons(ETHERNET_PROTO_ARP),                     /* Protocol number */
+    Init,                                         /* Protocol initialisation function */
+    Handler,                                    /* Protocol handler */
+    NULL,                                        /* Protocol registration function */
+    NULL,                                        /* API registration function */
+};
+
+
+static void Init(void)
+{
+    int32_t                index;
+    
+    DEBUG_MODULE(DEBUG_LEVEL_INFO, ("Initializing ARP layer"));
+    memset(arp_CacheTable, 0, sizeof(arp_CacheTable));
+    for (index = 0; index < ARP_CACHE_MAX_ENTRIES; index++)
+    {
+        arp_CacheTable[index].mutex = RTOS_MUTEX_CREATE();
+    }
+    NetIF_RegisterPeriodicFunction("ARP cache", PeriodicFunction, ARP_FUNCTION_PERIOD);
+}
+
+
+static void Handler(NetIF_t *netIF, Packet_t *packet)
+{
+    static ARP_Type_t			type;
+    static ARP_Protocol_t		protocol;
+    static ARP_Operation_t		operation;
+    static ARP_IPv4Data_t		*ipv4ARP;
+    static ARP_Header_t			*arpHeader;
+    static Ethernet_Addr_t		*ourHWAddress;
+    static Ethernet_Header_t	*ethernetHeader;
+    static ARP_CacheEntry_t		*entry;
+
+	arpHeader = (ARP_Header_t *)packet->data;
+    type = ntohs(arpHeader->type);
+    protocol = ntohs(arpHeader->protocol);
+    operation = ntohs(arpHeader->operation);
+
+    if (type != ARP_HW_TYPE_ENET) goto Exit;            /* not an ethernet ARP, ignore  */
+    if (protocol != ETHERNET_PROTO_IPV4) goto Exit;        /* Not an IPv4 ARP, ignore */
+    
+    ipv4ARP = (ARP_IPv4Data_t *)(arpHeader + 1);
+    
+    switch(operation)
+    {
+        case ARP_OPERATION_REQUEST:
+            if (ipv4ARP->ipDest.addr == netIF->ipv4Address.addr)     /* Does it match our hw address? */
+            {
+                DEBUG_BLOCK(DEBUG_LEVEL_VERBOSE0)
+                {
+                    ARP_DumpHeader("Got ", arpHeader);
+                }
+                
+                ourHWAddress = (Ethernet_Addr_t *)netIF->driverParameter;
+                
+                arpHeader->operation = htons(ARP_OPERATION_REPLY);
+                
+                ipv4ARP->hwDest = ipv4ARP->hwSource;
+                ipv4ARP->ipDest.addr = ipv4ARP->ipSource.addr;
+                ipv4ARP->hwSource = *ourHWAddress;
+                ipv4ARP->ipSource = netIF->ipv4Address;
+                
+                NetIF_ProtoPop(packet);
+
+                ethernetHeader = (Ethernet_Header_t *)packet->data;
+                ethernetHeader->destination = ethernetHeader->source;
+                ethernetHeader->source = *ourHWAddress;
+                
+                DEBUG_BLOCK(DEBUG_LEVEL_VERBOSE0)
+                {                
+                    ARP_DumpHeader("Replying ", arpHeader);
+                }
+                                
+                netIF->driver->Write(packet->data, packet->length);
+            }
+            
+            break;
+            
+        case ARP_OPERATION_REPLY:
+            /* Check if it matches an entry we requested */
+            DEBUG_BLOCK(DEBUG_LEVEL_VERBOSE0)
+            {
+                ARP_DumpHeader("Got ", arpHeader);
+            }
+            
+            entry = GetEntryByIPv4Address(ipv4ARP->ipSource);
+            if (entry == NULL) break;    /* fake arp request */
+            
+            entry->status = ARP_Entry_Dynamic;
+            entry->ethernetAddr = ipv4ARP->hwSource;
+            entry->netIF = netIF;
+            entry->age = 0;
+            
+            RTOS_MUTEX_UNLOCK(entry->mutex);
+            
+            DEBUG_BLOCK(DEBUG_LEVEL_VERBOSE0)
+            {
+                DEBUG_RAW(("Adding entry %d.%d.%d.%d at %02x:%02x:%02x:%02x:%02x:%02x",
+                    ipv4ARP->ipSource.IP0,
+                    ipv4ARP->ipSource.IP1,
+                    ipv4ARP->ipSource.IP2,
+                    ipv4ARP->ipSource.IP3,
+                    ipv4ARP->hwSource.MA0,
+                    ipv4ARP->hwSource.MA1,
+                    ipv4ARP->hwSource.MA2,
+                    ipv4ARP->hwSource.MA3,
+                    ipv4ARP->hwSource.MA4,
+                    ipv4ARP->hwSource.MA5
+                ));
+            }
+            break;
+    }
+    
+Exit:
+    return;
+}
+
+
+static void PeriodicFunction(void)
+{
+    int32_t        index;
+    ARP_CacheEntry_t    *entry;
+    
+    for (index = 0; index < ARP_CACHE_MAX_ENTRIES; index++)
+    {
+        entry = arp_CacheTable + index;
+        RTOS_MUTEX_LOCK(entry->mutex);
+        switch(entry->status)
+        {
+            case ARP_Entry_Dynamic:
+                entry->age += ARP_FUNCTION_PERIOD;
+                if (entry->age > ARP_MAX_ENTRY_AGE)
+                {
+                    entry->status = ARP_Entry_Expired;
+                    entry->age = 0;
+                }
+                break;
+                
+            case ARP_Entry_PendingReply:
+                entry->age += ARP_FUNCTION_PERIOD;
+                if (entry->age > ARP_MAX_ENTRY_AGE)
+                {
+                    entry->status = ARP_Entry_Free;
+                    entry->age = 0;
+                }
+                break;
+        }
+        RTOS_MUTEX_UNLOCK(entry->mutex);
+    }
+}
+
+
+static ARP_CacheEntry_t *GetReusableEntry(void)
+{
+    int32_t                index,
+                        oldestEntryIndex, oldestEntryAge;
+    ARP_CacheEntry_t    *entry;
+    
+    /* First look for a free entry */
+    for (index = 0; index < ARP_CACHE_MAX_ENTRIES; index++)
+    {
+        entry = arp_CacheTable + index;
+        RTOS_MUTEX_LOCK(entry->mutex);
+        
+        if (entry->status == ARP_Entry_Free)
+        {
+            break;
+        }
+
+        RTOS_MUTEX_UNLOCK(entry->mutex);
+        entry = NULL;
+    }
+    
+    if (entry != NULL) goto Exit;    /* A free entry was found, return it */
+    
+    /* Now look for an expired entry */
+    oldestEntryIndex = -1;
+    oldestEntryAge = -1;
+    for (index = 0; index < ARP_CACHE_MAX_ENTRIES; index++)
+    {
+        entry = arp_CacheTable + index;
+        RTOS_MUTEX_LOCK(entry->mutex);
+        
+        if (entry->age > oldestEntryAge)
+        {
+            oldestEntryIndex = index;
+            oldestEntryAge = entry->age;
+        }
+        
+        if (entry->status == ARP_Entry_Expired)
+        {
+            break;
+        }
+
+        RTOS_MUTEX_UNLOCK(entry->mutex);
+        entry = NULL;
+    }
+
+    if (entry != NULL) goto Exit;    /* An expired entry was found, return it */
+
+    /* Last possibility, return the oldest non static entry */    
+    entry = arp_CacheTable + oldestEntryIndex;
+    RTOS_MUTEX_LOCK(entry->mutex);
+
+Exit:
+    return entry;
+}
+
+
+static ARP_CacheEntry_t *GetEntryByIPv4Address(IPv4_Addr_t address)
+{
+    int32_t                index;
+    ARP_CacheEntry_t    *entry = NULL;
+    
+    for (index = 0; index < ARP_CACHE_MAX_ENTRIES; index++)
+    {
+        entry = arp_CacheTable + index;
+        RTOS_MUTEX_LOCK(entry->mutex);
+        
+        if (entry->ipv4Addr.addr == address.addr)
+        {
+            break;
+        }
+        RTOS_MUTEX_UNLOCK(entry->mutex);
+        entry = NULL;
+    }
+    
+    return entry;
+}
+
+
+int32_t ARP_ResolveIPv4Address(NetIF_t *netIF, IPv4_Addr_t address, Ethernet_Addr_t *ethernetAddr)
+{
+    int32_t                result = -1;
+    Ethernet_Addr_t        *hwAddress;
+    ARP_CacheEntry_t    *entry;
+    
+    DEBUG_MODULE(DEBUG_LEVEL_INFO, ("Resolving %d.%d.%d.%d",
+        address.IP0,
+        address.IP1,
+        address.IP2,
+        address.IP3
+    ));
+    
+    /* Look if entry is already available in table */
+    entry = GetEntryByIPv4Address(address);
+    if (entry != NULL)                /* Found entry, look its status */
+    {
+        switch(entry->status)
+        {
+            case ARP_Entry_Static:
+                DEBUG_MODULE(DEBUG_LEVEL_VERBOSE0, ("Found static entry"));
+                if (ethernetAddr != NULL) *ethernetAddr = entry->ethernetAddr;
+                RTOS_MUTEX_UNLOCK(entry->mutex);
+                result = 0;
+                break;
+                
+            case ARP_Entry_Dynamic:
+                DEBUG_MODULE(DEBUG_LEVEL_VERBOSE0, ("Found dynamic entry"));
+                if (ethernetAddr != NULL) *ethernetAddr = entry->ethernetAddr;
+                entry->age = 0;
+                RTOS_MUTEX_UNLOCK(entry->mutex);
+                result = 0;
+                break;
+                
+            case ARP_Entry_Expired:
+                DEBUG_MODULE(DEBUG_LEVEL_VERBOSE0, ("Found expired entry, reactivating it"));
+                if (ethernetAddr != NULL) *ethernetAddr = entry->ethernetAddr;
+                entry->status = ARP_Entry_Dynamic;
+                entry->age = 0;
+                RTOS_MUTEX_UNLOCK(entry->mutex);
+                result = 0;
+                break;
+                
+            case ARP_Entry_PendingReply:
+                DEBUG_MODULE(DEBUG_LEVEL_VERBOSE0, ("Found pending entry"));
+                entry->age = 0;
+                RTOS_MUTEX_UNLOCK(entry->mutex);
+                break;
+                
+            default:
+                DEBUG_MODULE(DEBUG_LEVEL_INFO, ("Default?!"));
+                break;
+        }
+    }
+    
+    if (result == 0) goto Exit;        /* Resolution was successfull, exit */
+    
+    
+    /* Entry not found, send a request */
+    result = -1;
+    DEBUG_MODULE(DEBUG_LEVEL_INFO, ("Sending ARP resolution request for %d.%d.%d.%d",
+        address.IP0,
+        address.IP1,
+        address.IP2,
+        address.IP3
+    ));
+    
+    /* Update entry, setting its status to Pending reply */
+    entry = GetReusableEntry();
+    if (entry != NULL)
+    {
+        entry->status = ARP_Entry_PendingReply;
+        entry->ipv4Addr.addr = address.addr;
+        entry->netIF = netIF;
+        entry->age = 0;
+        RTOS_MUTEX_UNLOCK(entry->mutex);
+    }
+    /* Send ARP who-has */    
+    hwAddress = (Ethernet_Addr_t *)netIF->driverParameter;
+
+    arp_FullIPv4Packet.ethernetHeader.destination = ethernet_Addr_Broadcast;
+    arp_FullIPv4Packet.ethernetHeader.source = *hwAddress;
+    arp_FullIPv4Packet.ethernetHeader.protocol = htons(ETHERNET_PROTO_ARP);
+    
+    arp_FullIPv4Packet.arpHeader.type = htons(ARP_HW_TYPE_ENET);
+    arp_FullIPv4Packet.arpHeader.protocol = htons(ETHERNET_PROTO_IPV4);
+    arp_FullIPv4Packet.arpHeader.operation = htons(ARP_OPERATION_REQUEST);
+    arp_FullIPv4Packet.arpHeader.hardAddrLen = 6;
+    arp_FullIPv4Packet.arpHeader.protoAddrLen = 4;
+    
+    arp_FullIPv4Packet.ipv4ARPData.hwSource = *hwAddress;
+    arp_FullIPv4Packet.ipv4ARPData.ipSource = netIF->ipv4Address;
+    arp_FullIPv4Packet.ipv4ARPData.hwDest = ethernet_Addr_Null;
+    arp_FullIPv4Packet.ipv4ARPData.ipDest = address;
+    
+    DEBUG_BLOCK(DEBUG_LEVEL_VERBOSE0)
+    {
+        ARP_DumpHeader("Sending ", &arp_FullIPv4Packet.arpHeader);
+    }
+    
+    netIF->driver->Write((uint8_t *)&arp_FullIPv4Packet, sizeof(arp_FullIPv4Packet));
+    
+Exit:
+    return result;
+}
+
+
+int32_t ARP_AddStaticEntry(NetIF_t *netIF, IPv4_Addr_t address, const Ethernet_Addr_t *ethernetAddr)
+{
+    int32_t                result = 0;
+    ARP_CacheEntry_t    *entry;
+    
+    entry = GetReusableEntry();
+    if (entry == NULL)
+    {
+        result = -1;
+        goto Exit;
+    }
+    entry->netIF = netIF;
+    entry->status = ARP_Entry_Static;
+    entry->ipv4Addr.addr = address.addr;
+    entry->ethernetAddr = *ethernetAddr;
+    entry->age = 0;
+    RTOS_MUTEX_UNLOCK(entry->mutex);
+
+Exit:
+    return result;
+}
+
+
+int32_t ARP_RemoveEntry(const NetIF_t *netIF, IPv4_Addr_t address)
+{
+    int32_t        result = 0;
+    
+    
+    
+    return result;
+}
+
+
+void ARP_FlushCache(Bool_t flushStaticEntries)
+{
+    int32_t                index;
+    ARP_CacheEntry_t    *entry;
+    
+    for (index = 0; index < ARP_CACHE_MAX_ENTRIES; index++)
+    {
+        entry = arp_CacheTable + index;
+        RTOS_MUTEX_LOCK(entry->mutex);
+        if ((entry->status == ARP_Entry_Static) && (!flushStaticEntries)) 
+        {
+            RTOS_MUTEX_UNLOCK(entry->mutex);
+            continue;
+        }
+        entry->status = ARP_Entry_Free;
+		entry->ipv4Addr = ipv4_Addr_Any;
+		entry->ethernetAddr = ethernet_Addr_Null;
+		entry->age = 0;
+        RTOS_MUTEX_UNLOCK(entry->mutex);
+    }
+}
+
+
+void ARP_DisplayCache(void)
+{
+    int32_t                index;
+    ARP_CacheEntry_t    *entry = NULL;
+    
+    DEBUG_RAW(("ARP cache"));
+    DEBUG_RAW(("index dev   MAC address            type  age  IPv4 address"));
+    DEBUG_RAW(("----------------------------------------------------------"));
+      /*      en0   00:11:22:33:44:55  dyn    10 163.157.128.131 */
+    for (index = 0; index < ARP_CACHE_MAX_ENTRIES; index++)
+    {
+        entry = arp_CacheTable + index;
+    
+        DEBUG_RAW(("%2d    %s%c   %02x:%02x:%02x:%02x:%02x:%02x  %8s  %4d  %d.%d.%d.%d",
+            index, 
+            entry->status != ARP_Entry_Free ? entry->netIF->name : "--", 
+            entry->status != ARP_Entry_Free ? entry->netIF->index + '0' : '-', 
+            entry->ethernetAddr.MA0,
+            entry->ethernetAddr.MA1,
+            entry->ethernetAddr.MA2,
+            entry->ethernetAddr.MA3,
+            entry->ethernetAddr.MA4,
+            entry->ethernetAddr.MA5,
+                
+            arpEntryStatusText[entry->status],
+            entry->age,
+            
+            entry->ipv4Addr.IP0,
+            entry->ipv4Addr.IP1,
+            entry->ipv4Addr.IP2,
+            entry->ipv4Addr.IP3
+        ));    
+    }
+}
+
+void ARP_DumpHeader(const char *prefix, ARP_Header_t *arpHeader)
+{
+    ARP_IPv4Data_t    *ipv4ARP = NULL;
+    
+    if (arpHeader->protocol == htons(ETHERNET_PROTO_IPV4))
+    {
+        ipv4ARP = (ARP_IPv4Data_t *)(arpHeader + 1);
+        
+        switch(ntohs(arpHeader->operation))
+        {
+            case ARP_OPERATION_REQUEST:
+                DEBUG_RAW(("%sARP who-has %d.%d.%d.%d tell %02x:%02x:%02x:%02x:%02x:%02x",
+                    prefix != NULL ? prefix : "",
+            
+                    ipv4ARP->ipDest.IP0,
+                    ipv4ARP->ipDest.IP1,
+                    ipv4ARP->ipDest.IP2,
+                    ipv4ARP->ipDest.IP3,
+            
+                    ipv4ARP->hwSource.MA0,
+                    ipv4ARP->hwSource.MA1,
+                    ipv4ARP->hwSource.MA2,
+                    ipv4ARP->hwSource.MA3,
+                    ipv4ARP->hwSource.MA4,
+                    ipv4ARP->hwSource.MA5
+                ));
+                break;
+                
+            case ARP_OPERATION_REPLY:
+                DEBUG_RAW(("%sARP %d.%d.%d.%d is-at %02x:%02x:%02x:%02x:%02x:%02x",
+                    prefix != NULL ? prefix : "",
+            
+                    ipv4ARP->ipSource.IP0,
+                    ipv4ARP->ipSource.IP1,
+                    ipv4ARP->ipSource.IP2,
+                    ipv4ARP->ipSource.IP3,
+            
+                    ipv4ARP->hwSource.MA0,
+                    ipv4ARP->hwSource.MA1,
+                    ipv4ARP->hwSource.MA2,
+                    ipv4ARP->hwSource.MA3,
+                    ipv4ARP->hwSource.MA4,
+                    ipv4ARP->hwSource.MA5
+                ));
+                break;
+            
+            default:
+                break;
+        }
+    }
+    else
+    {
+        DEBUG_RAW(("%sARP: unsupported protocol %d",
+            prefix != NULL ? prefix : "",
+            arpHeader->protocol
+        ));
+    }
+}