Important changes to repositories hosted on mbed.com
Mbed hosted mercurial repositories are deprecated and are due to be permanently deleted in July 2026.
To keep a copy of this software download the repository Zip archive or clone locally using Mercurial.
It is also possible to export all your personal repositories from the account settings page.
Diff: ARP.cpp
- 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
+ ));
+ }
+}