Rewrite from scratch a TCP/IP stack for mbed. So far the following parts are usable: Drivers: - EMAC driver (from CMSIS 2.0) Protocols: - Ethernet protocol - ARP over ethernet for IPv4 - IPv4 over Ethernet - ICMPv4 over IPv4 - UDPv4 over IPv4 APIs: - Sockets for UDPv4 The structure of this stack is designed to be very modular. Each protocol can register one or more protocol to handle its payload, and in each protocol, an API can be hooked (like Sockets for example). This is an early release.

ARP.cpp

Committer:
Benoit
Date:
2011-06-26
Revision:
7:8e12f7357b9f
Parent:
5:3cd83fcb1467

File content as of revision 7:8e12f7357b9f:

/*
 * $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, NetPacket_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, NetPacket_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  */
/* Not an IPv4 ARP, ignore */
	if (protocol != ETHERNET_PROTO_IPV4) goto Exit;

	ipv4ARP = (ARP_IPv4Data_t *)(arpHeader + 1);

	switch(operation)
	{
		case ARP_OPERATION_REQUEST:
/* Does it match our hw address? */
			if (ipv4ARP->ipDest.addr == netIF->ipv4Address.addr)
			{
				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
			));
	}
}