text: /*
   * FreeRTOS+TCP V2.2.1
   * Copyright (C) 2017 Amazon.com, Inc. or its affiliates.  All Rights Reserved.
   *
   * Permission is hereby granted, free of charge, to any person obtaining a copy of
   * this software and associated documentation files (the "Software"), to deal in
   * the Software without restriction, including without limitation the rights to
   * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
   * the Software, and to permit persons to whom the Software is furnished to do so,
   * subject to the following conditions:
   *
   * The above copyright notice and this permission notice shall be included in all
   * copies or substantial portions of the Software.
   *
   * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
   * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
   * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
   * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
   * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
   * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
   *
   * http://aws.amazon.com/freertos
   * http://www.FreeRTOS.org
   */
  
  /* Standard includes. */
  #include <stdint.h>
  #include <stdio.h>
  
  /* FreeRTOS includes. */
  #include "FreeRTOS.h"
  #include "task.h"
  #include "queue.h"
  #include "semphr.h"
  
  /* FreeRTOS+TCP includes. */
  #include "FreeRTOS_IP.h"
  #include "FreeRTOS_Sockets.h"
  #include "FreeRTOS_IP_Private.h"
  #include "FreeRTOS_ARP.h"
  #include "FreeRTOS_UDP_IP.h"
  #include "FreeRTOS_DHCP.h"
  #if( ipconfigUSE_LLMNR == 1 )
      #include "FreeRTOS_DNS.h"
  #endif /* ipconfigUSE_LLMNR */
  #include "NetworkInterface.h"
  #include "NetworkBufferManagement.h"
  
  
  /* When the age of an entry in the ARP table reaches this value (it counts down
   * to zero, so this is an old entry) an ARP request will be sent to see if the
   * entry is still valid and can therefore be refreshed. */
  #define arpMAX_ARP_AGE_BEFORE_NEW_ARP_REQUEST		( 3 )
  
  /* The time between gratuitous ARPs. */
  #ifndef arpGRATUITOUS_ARP_PERIOD
      #define arpGRATUITOUS_ARP_PERIOD					( pdMS_TO_TICKS( 20000 ) )
  #endif
  
  /*-----------------------------------------------------------*/
  
  /*
   * Lookup an MAC address in the ARP cache from the IP address.
   */
  static eARPLookupResult_t prvCacheLookup( uint32_t ulAddressToLookup, MACAddress_t * const pxMACAddress );
  
  /*-----------------------------------------------------------*/
  
  /* The ARP cache. */
  static ARPCacheRow_t xARPCache[ ipconfigARP_CACHE_ENTRIES ];
  
  /* The time at which the last gratuitous ARP was sent.  Gratuitous ARPs are used
   * to ensure ARP tables are up to date and to detect IP address conflicts. */
  static TickType_t xLastGratuitousARPTime = ( TickType_t ) 0;
  
  /*
   * IP-clash detection is currently only used internally. When DHCP doesn't respond, the
   * driver can try out a random LinkLayer IP address (169.254.x.x).  It will send out a
   * gratuitos ARP message and, after a period of time, check the variables here below:
   */
  #if( ipconfigARP_USE_CLASH_DETECTION != 0 )
  	/* Becomes non-zero if another device responded to a gratuitos ARP message. */
  	BaseType_t xARPHadIPClash;
  	/* MAC-address of the other device containing the same IP-address. */
  	MACAddress_t xARPClashMacAddress;
  #endif /* ipconfigARP_USE_CLASH_DETECTION */
  
  /*-----------------------------------------------------------*/
  
  eFrameProcessingResult_t eARPProcessPacket( ARPPacket_t * const pxARPFrame )
  {
  eFrameProcessingResult_t eReturn = eReleaseBuffer;
  ARPHeader_t *pxARPHeader;
  uint32_t ulTargetProtocolAddress, ulSenderProtocolAddress;
  
  	pxARPHeader = &( pxARPFrame->xARPHeader );
  
  	/* The field ulSenderProtocolAddress is badly aligned, copy byte-by-byte. */
  	( void ) memcpy( ( void * )&( ulSenderProtocolAddress ), ( const void * ) pxARPHeader->ucSenderProtocolAddress, sizeof( ulSenderProtocolAddress ) );
  	/* The field ulTargetProtocolAddress is well-aligned, a 32-bits copy. */
  	ulTargetProtocolAddress = pxARPHeader->ulTargetProtocolAddress;
  
  	traceARP_PACKET_RECEIVED();
  
  	/* Don't do anything if the local IP address is zero because
  	 * that means a DHCP request has not completed. */
  	if( *ipLOCAL_IP_ADDRESS_POINTER != 0UL )
  	{
  		switch( pxARPHeader->usOperation )
  		{
  			case ipARP_REQUEST :
  				/* The packet contained an ARP request.  Was it for the IP
  				 * address of the node running this code? */
  				if( ulTargetProtocolAddress == *ipLOCAL_IP_ADDRESS_POINTER )
  				{
  					iptraceSENDING_ARP_REPLY( ulSenderProtocolAddress );
  
  					/* The request is for the address of this node.  Add the
  					 * entry into the ARP cache, or refresh the entry if it
  					 * already exists. */
  					vARPRefreshCacheEntry( &( pxARPHeader->xSenderHardwareAddress ), ulSenderProtocolAddress );
  
  					/* Generate a reply payload in the same buffer. */
  					pxARPHeader->usOperation = ( uint16_t )ipARP_REPLY;
  					if( ulTargetProtocolAddress == ulSenderProtocolAddress )
  					{
  						/* A double IP address is detected! */
  						/* Give the sources MAC address the value of the broadcast address, will be swapped later */
  						( void ) memcpy( ( void * ) pxARPFrame->xEthernetHeader.xSourceAddress.ucBytes, ( const void * ) xBroadcastMACAddress.ucBytes, sizeof( xBroadcastMACAddress ) );
  						( void ) memset( pxARPHeader->xTargetHardwareAddress.ucBytes, 0, sizeof( MACAddress_t ) );
  						pxARPHeader->ulTargetProtocolAddress = 0UL;
  					}
  					else
  					{
  						( void ) memcpy( ( void * ) pxARPHeader->xTargetHardwareAddress.ucBytes, ( const void * ) pxARPHeader->xSenderHardwareAddress.ucBytes, sizeof( MACAddress_t ) );
  						pxARPHeader->ulTargetProtocolAddress = ulSenderProtocolAddress;
  					}
  					( void ) memcpy( ( void * ) pxARPHeader->xSenderHardwareAddress.ucBytes, ( const void * ) ipLOCAL_MAC_ADDRESS, sizeof( MACAddress_t ) );
  					( void ) memcpy( ( void * ) pxARPHeader->ucSenderProtocolAddress, ( const void * ) ipLOCAL_IP_ADDRESS_POINTER, sizeof( pxARPHeader->ucSenderProtocolAddress ) );
  
  					eReturn = eReturnEthernetFrame;
  				}
  				break;
  
  			case ipARP_REPLY :
  				iptracePROCESSING_RECEIVED_ARP_REPLY( ulTargetProtocolAddress );
  				vARPRefreshCacheEntry( &( pxARPHeader->xSenderHardwareAddress ), ulSenderProtocolAddress );
  				/* Process received ARP frame to see if there is a clash. */
  				#if( ipconfigARP_USE_CLASH_DETECTION != 0 )
  				{
  					if( ulSenderProtocolAddress == *ipLOCAL_IP_ADDRESS_POINTER )
  					{
  						xARPHadIPClash = pdTRUE;
  						( void ) memcpy( ( void * ) xARPClashMacAddress.ucBytes, ( const void * ) pxARPHeader->xSenderHardwareAddress.ucBytes, sizeof( xARPClashMacAddress.ucBytes ) );
  					}
  				}
  				#endif /* ipconfigARP_USE_CLASH_DETECTION */
  				break;
  
  			default :
  				/* Invalid. */
  				break;
  		}
  	}
  
  	return eReturn;
  }
  /*-----------------------------------------------------------*/
  
  #if( ipconfigUSE_ARP_REMOVE_ENTRY != 0 )
  
  	uint32_t ulARPRemoveCacheEntryByMac( const MACAddress_t * pxMACAddress )
  	{
  	BaseType_t x;
  	uint32_t lResult = 0;
  
  		/* For each entry in the ARP cache table. */
  		for( x = 0; x < ipconfigARP_CACHE_ENTRIES; x++ )
  		{
  			if( ( memcmp( xARPCache[ x ].xMACAddress.ucBytes, pxMACAddress->ucBytes, sizeof( pxMACAddress->ucBytes ) ) == 0 ) )
  			{
  				lResult = xARPCache[ x ].ulIPAddress;
  				( void ) memset( &xARPCache[ x ], 0, sizeof( xARPCache[ x ] ) );
  				break;
  			}
  		}
  
  		return lResult;
  	}
  
  #endif	/* ipconfigUSE_ARP_REMOVE_ENTRY != 0 */
  /*-----------------------------------------------------------*/
  
  void vARPRefreshCacheEntry( const MACAddress_t * pxMACAddress, const uint32_t ulIPAddress )
  {
  BaseType_t x = 0;
  BaseType_t xIpEntry = -1;
  BaseType_t xMacEntry = -1;
  BaseType_t xUseEntry = 0;
  uint8_t ucMinAgeFound = 0U;
  
  	#if( ipconfigARP_STORES_REMOTE_ADDRESSES == 0 )
  		/* Only process the IP address if it is on the local network.
  		 * Unless: when '*ipLOCAL_IP_ADDRESS_POINTER' equals zero, the IP-address
  		 * and netmask are still unknown. */
  		if( ( ( ulIPAddress & xNetworkAddressing.ulNetMask ) == ( ( *ipLOCAL_IP_ADDRESS_POINTER ) & xNetworkAddressing.ulNetMask ) ) ||
  			( *ipLOCAL_IP_ADDRESS_POINTER == 0UL ) )
  	#else
  		/* If ipconfigARP_STORES_REMOTE_ADDRESSES is non-zero, IP addresses with
  		 * a different netmask will also be stored.  After when replying to a UDP
  		 * message from a different netmask, the IP address can be looped up and a
  		 * reply sent.  This option is useful for systems with multiple gateways,
  		 * the reply will surely arrive.  If ipconfigARP_STORES_REMOTE_ADDRESSES is
  		 * zero the the gateway address is the only option. */
  		if( pdTRUE )
  	#endif
  	{
  		/* Start with the maximum possible number. */
  		ucMinAgeFound--;
  
  		/* For each entry in the ARP cache table. */
  		for( x = 0; x < ipconfigARP_CACHE_ENTRIES; x++ )
  		{
  			/* Does this line in the cache table hold an entry for the IP
  			 * address being queried? */
  			if( xARPCache[ x ].ulIPAddress == ulIPAddress )
  			{
  				if( pxMACAddress == NULL )
  				{
  					/* In case the parameter pxMACAddress is NULL, an entry will be reserved to
  					 * indicate that there is an outstanding ARP request, This entry will have
  					 * "ucValid == pdFALSE". */
  					xIpEntry = x;
  					break;
  				}
  
  				/* See if the MAC-address also matches. */
  				if( memcmp( xARPCache[ x ].xMACAddress.ucBytes, pxMACAddress->ucBytes, sizeof( pxMACAddress->ucBytes ) ) == 0 )
  				{
  					/* This function will be called for each received packet
  					 * As this is by far the most common path the coding standard
  					 * is relaxed in this case and a return is permitted as an
  					 * optimisation. */
  					xARPCache[ x ].ucAge = ( uint8_t ) ipconfigMAX_ARP_AGE;
  					xARPCache[ x ].ucValid = ( uint8_t ) pdTRUE;					
  					return;
  				}
  
  				/* Found an entry containing ulIPAddress, but the MAC address
  				 * doesn't match.  Might be an entry with ucValid==pdFALSE, waiting
  				 * for an ARP reply.  Still want to see if there is match with the
  				 * given MAC address.ucBytes.  If found, either of the two entries
  				 * must be cleared. */
  				xIpEntry = x;
  			}
  			else if( ( pxMACAddress != NULL ) && ( memcmp( xARPCache[ x ].xMACAddress.ucBytes, pxMACAddress->ucBytes, sizeof( pxMACAddress->ucBytes ) ) == 0 ) )
  			{
  				/* Found an entry with the given MAC-address, but the IP-address
  				 * is different.  Continue looping to find a possible match with
  				 * ulIPAddress. */
  				#if( ipconfigARP_STORES_REMOTE_ADDRESSES != 0 )
  					/* If ARP stores the MAC address of IP addresses outside the
  					 * network, than the MAC address of the gateway should not be
  					 * overwritten. */
  					BaseType_t bIsLocal[ 2 ];
  					bIsLocal[ 0 ] = ( ( xARPCache[x].ulIPAddress & xNetworkAddressing.ulNetMask ) == ( ( *ipLOCAL_IP_ADDRESS_POINTER ) & xNetworkAddressing.ulNetMask ) );
  					bIsLocal[ 1 ] = ( ( ulIPAddress & xNetworkAddressing.ulNetMask ) == ( ( *ipLOCAL_IP_ADDRESS_POINTER ) & xNetworkAddressing.ulNetMask ) );
  					if( bIsLocal[ 0 ] == bIsLocal[ 1 ] )
  					{
  						xMacEntry = x;
  					}
  				#else
  					xMacEntry = x;
  				#endif
  			}
  			/* _HT_
  			 * Shouldn't we test for xARPCache[ x ].ucValid == pdFALSE here ? */
  			else if( xARPCache[ x ].ucAge < ucMinAgeFound )
  			{
  				/* As the table is traversed, remember the table row that
  				 * contains the oldest entry (the lowest age count, as ages are
  				 * decremented to zero) so the row can be re-used if this function
  				 * needs to add an entry that does not already exist. */
  				ucMinAgeFound = xARPCache[ x ].ucAge;
  				xUseEntry = x;
  			}
  			else
  			{
  				/* Do nothing. */
  			}
  		}
  
  		if( xMacEntry >= 0 )
  		{
  			xUseEntry = xMacEntry;
  
  			if( xIpEntry >= 0 )
  			{
  				/* Both the MAC address as well as the IP address were found in
  				 * different locations: clear the entry which matches the
  				 * IP-address */
  				( void ) memset( &xARPCache[ xIpEntry ], 0, sizeof( xARPCache[ xIpEntry ] ) );
  			}
  		}
  		else if( xIpEntry >= 0 )
  		{
  			/* An entry containing the IP-address was found, but it had a different MAC address */
  			xUseEntry = xIpEntry;
  		}
  		else
  		{
  			/* Do nothing! xUseEntry retains its value */
  		}
  
  		/* If the entry was not found, we use the oldest entry and set the IPaddress */
  		xARPCache[ xUseEntry ].ulIPAddress = ulIPAddress;
  
  		if( pxMACAddress != NULL )
  		{
  			( void ) memcpy( ( void * ) xARPCache[ xUseEntry ].xMACAddress.ucBytes, ( const void * ) pxMACAddress->ucBytes, sizeof( pxMACAddress->ucBytes ) );
  
  			iptraceARP_TABLE_ENTRY_CREATED( ulIPAddress, ( *pxMACAddress ) );
  			/* And this entry does not need immediate attention */
  			xARPCache[ xUseEntry ].ucAge = ( uint8_t ) ipconfigMAX_ARP_AGE;
  			xARPCache[ xUseEntry ].ucValid = ( uint8_t ) pdTRUE;
  		}
  		else if( xIpEntry < 0 )
  		{
  			xARPCache[ xUseEntry ].ucAge = ( uint8_t ) ipconfigMAX_ARP_RETRANSMISSIONS;
  			xARPCache[ xUseEntry ].ucValid = ( uint8_t ) pdFALSE;
  		}
  		else
  		{
  			/* Do nothing. (pxMACAddress == NULL) and (xIPEntry > 0)
  			 * Implies that an IP address was found but we don't have
  			 * a MAC address for it. Therefore, no action required */
  		}
  	}
  }
  /*-----------------------------------------------------------*/
  
  #if( ipconfigUSE_ARP_REVERSED_LOOKUP == 1 )
  	eARPLookupResult_t eARPGetCacheEntryByMac( MACAddress_t * const pxMACAddress, uint32_t *pulIPAddress )
  	{
  	BaseType_t x;
  	eARPLookupResult_t eReturn = eARPCacheMiss;
  
  		/* Loop through each entry in the ARP cache. */
  		for( x = 0; x < ipconfigARP_CACHE_ENTRIES; x++ )
  		{
  			/* Does this row in the ARP cache table hold an entry for the MAC
  			 * address being searched? */
  			if( memcmp( ( void * ) pxMACAddress->ucBytes, ( const void * ) xARPCache[ x ].xMACAddress.ucBytes, sizeof( MACAddress_t ) ) == 0 )
  			{
  				*pulIPAddress = xARPCache[ x ].ulIPAddress;
  				eReturn = eARPCacheHit;
  				break;
  			}
  		}
  
  		return eReturn;
  	}
  #endif /* ipconfigUSE_ARP_REVERSED_LOOKUP */
  
  /*-----------------------------------------------------------*/
  
  eARPLookupResult_t eARPGetCacheEntry( uint32_t *pulIPAddress, MACAddress_t * const pxMACAddress )
  {
  eARPLookupResult_t eReturn;
  uint32_t ulAddressToLookup;
  
  #if( ipconfigUSE_LLMNR == 1 )
  	if( *pulIPAddress == ipLLMNR_IP_ADDR )	/* Is in network byte order. */
  	{
  		/* The LLMNR IP-address has a fixed virtual MAC address. */
  		( void ) memcpy( ( void * ) pxMACAddress->ucBytes, ( const void * ) xLLMNR_MacAdress.ucBytes, sizeof( MACAddress_t ) );
  		eReturn = eARPCacheHit;
  	}
  	else
  #endif
  	if( ( *pulIPAddress == ipBROADCAST_IP_ADDRESS ) ||	/* Is it the general broadcast address 255.255.255.255? */
  		( *pulIPAddress == xNetworkAddressing.ulBroadcastAddress ) )/* Or a local broadcast address, eg 192.168.1.255? */
  	{
  		/* This is a broadcast so uses the broadcast MAC address. */
  		( void ) memcpy( ( void * ) pxMACAddress->ucBytes, ( const void * ) xBroadcastMACAddress.ucBytes, sizeof( MACAddress_t ) );
  		eReturn = eARPCacheHit;
  	}
  	else if( *ipLOCAL_IP_ADDRESS_POINTER == 0UL )
  	{
  		/* The IP address has not yet been assigned, so there is nothing that
  		 * can be done. */
  		eReturn = eCantSendPacket;
  	}
  	else
  	{
  		eReturn = eARPCacheMiss;
  
  		if( ( *pulIPAddress & xNetworkAddressing.ulNetMask ) != ( ( *ipLOCAL_IP_ADDRESS_POINTER ) & xNetworkAddressing.ulNetMask ) )
  		{
  #if( ipconfigARP_STORES_REMOTE_ADDRESSES == 1 )
  			eReturn = prvCacheLookup( *pulIPAddress, pxMACAddress );
  
  			if( eReturn == eARPCacheHit )
  			{
  				/* The stack is configured to store 'remote IP addresses', i.e. addresses
  				 * belonging to a different the netmask.  prvCacheLookup() returned a hit, so
  				 * the MAC address is known */
  			}
  			else
  #endif
  			{
  				/* The IP address is off the local network, so look up the
  				 * hardware address of the router, if any. */
  				if( xNetworkAddressing.ulGatewayAddress != ( uint32_t )0u )
  				{
  					ulAddressToLookup = xNetworkAddressing.ulGatewayAddress;
  				}
  				else
  				{
  					ulAddressToLookup = *pulIPAddress;
  				}
  			}
  		}
  		else
  		{
  			/* The IP address is on the local network, so lookup the requested
  			 * IP address directly. */
  			ulAddressToLookup = *pulIPAddress;
  		}
  
  		if( eReturn == eARPCacheMiss )
  		{
  			if( ulAddressToLookup == 0UL )
  			{
  				/* The address is not on the local network, and there is not a
  				 * router. */
  				eReturn = eCantSendPacket;
  			}
  			else
  			{
  				eReturn = prvCacheLookup( ulAddressToLookup, pxMACAddress );
  
  				if( eReturn == eARPCacheMiss )
  				{
  					/* It might be that the ARP has to go to the gateway. */
  					*pulIPAddress = ulAddressToLookup;
  				}
  			}
  		}
  	}
  
  	return eReturn;
  }
  
  /*-----------------------------------------------------------*/
  
  static eARPLookupResult_t prvCacheLookup( uint32_t ulAddressToLookup, MACAddress_t * const pxMACAddress )
  {
  BaseType_t x;
  eARPLookupResult_t eReturn = eARPCacheMiss;
  
  	/* Loop through each entry in the ARP cache. */
  	for( x = 0; x < ipconfigARP_CACHE_ENTRIES; x++ )
  	{
  		/* Does this row in the ARP cache table hold an entry for the IP address
  		 * being queried? */
  		if( xARPCache[ x ].ulIPAddress == ulAddressToLookup )
  		{
  			/* A matching valid entry was found. */
  			if( xARPCache[ x ].ucValid == ( uint8_t )pdFALSE )
  			{
  				/* This entry is waiting an ARP reply, so is not valid. */
  				eReturn = eCantSendPacket;
  			}
  			else
  			{
  				/* A valid entry was found. */
  				( void ) memcpy( ( void * ) pxMACAddress->ucBytes, ( const void * ) xARPCache[ x ].xMACAddress.ucBytes, sizeof( MACAddress_t ) );
  				eReturn = eARPCacheHit;
  			}
  			break;
  		}
  	}
  
  	return eReturn;
  }
  /*-----------------------------------------------------------*/
  
  void vARPAgeCache( void )
  {
  BaseType_t x;
  TickType_t xTimeNow;
  
  	/* Loop through each entry in the ARP cache. */
  	for( x = 0; x < ipconfigARP_CACHE_ENTRIES; x++ )
  	{
  		/* If the entry is valid (its age is greater than zero). */
  		if( xARPCache[ x ].ucAge > 0U )
  		{
  			/* Decrement the age value of the entry in this ARP cache table row.
  			 * When the age reaches zero it is no longer considered valid. */
  			( xARPCache[ x ].ucAge )--;
  
  			/* If the entry is not yet valid, then it is waiting an ARP
  			 * reply, and the ARP request should be retransmitted. */
  			if( xARPCache[ x ].ucValid == ( uint8_t )pdFALSE )
  			{
  				FreeRTOS_OutputARPRequest( xARPCache[ x ].ulIPAddress );
  			}
  			else if( xARPCache[ x ].ucAge <= ( uint8_t )arpMAX_ARP_AGE_BEFORE_NEW_ARP_REQUEST )
  			{
  				/* This entry will get removed soon.  See if the MAC address is
  				 * still valid to prevent this happening. */
  				iptraceARP_TABLE_ENTRY_WILL_EXPIRE( xARPCache[ x ].ulIPAddress );
  				FreeRTOS_OutputARPRequest( xARPCache[ x ].ulIPAddress );
  			}
  			else
  			{
  				/* The age has just ticked down, with nothing to do. */
  			}
  
  			if( xARPCache[ x ].ucAge == 0u )
  			{
  				/* The entry is no longer valid.  Wipe it out. */
  				iptraceARP_TABLE_ENTRY_EXPIRED( xARPCache[ x ].ulIPAddress );
  				xARPCache[ x ].ulIPAddress = 0UL;
  			}
  		}
  	}
  
  	xTimeNow = xTaskGetTickCount();
  
  	if( ( xLastGratuitousARPTime == ( TickType_t ) 0 ) || ( ( xTimeNow - xLastGratuitousARPTime ) > ( TickType_t )arpGRATUITOUS_ARP_PERIOD ) )
  	{
  		FreeRTOS_OutputARPRequest( *ipLOCAL_IP_ADDRESS_POINTER );
  		xLastGratuitousARPTime = xTimeNow;
  	}
  }
  /*-----------------------------------------------------------*/
  
  void vARPSendGratuitous( void )
  {
  	/* Setting xLastGratuitousARPTime to 0 will force a gratuitous ARP the next
  	 * time vARPAgeCache() is called. */
  	xLastGratuitousARPTime = ( TickType_t )0;
  
  	/* Let the IP-task call vARPAgeCache(). */
  	( void )xSendEventToIPTask( eARPTimerEvent );
  }
  
  /*-----------------------------------------------------------*/
  void FreeRTOS_OutputARPRequest( uint32_t ulIPAddress )
  {
  NetworkBufferDescriptor_t *pxNetworkBuffer;
  
  	/* This is called from the context of the IP event task, so a block time
  	 * must not be used. */
  	pxNetworkBuffer = pxGetNetworkBufferWithDescriptor( sizeof( ARPPacket_t ), ( TickType_t )0 );
  
  	if( pxNetworkBuffer != NULL )
  	{
  		pxNetworkBuffer->ulIPAddress = ulIPAddress;
  		vARPGenerateRequestPacket( pxNetworkBuffer );
  
  		#if defined( ipconfigETHERNET_MINIMUM_PACKET_BYTES )
  		{
  			if( pxNetworkBuffer->xDataLength < ( size_t ) ipconfigETHERNET_MINIMUM_PACKET_BYTES )
  			{
  			BaseType_t xIndex;
  
  				for( xIndex = ( BaseType_t ) pxNetworkBuffer->xDataLength; xIndex < ( BaseType_t ) ipconfigETHERNET_MINIMUM_PACKET_BYTES; xIndex++ )
  				{
  					pxNetworkBuffer->pucEthernetBuffer[ xIndex ] = 0u;
  				}
  				pxNetworkBuffer->xDataLength = ( size_t ) ipconfigETHERNET_MINIMUM_PACKET_BYTES;
  			}
  		}
  		#endif
  		if( xIsCallingFromIPTask() != 0 )
  		{
  			/* Only the IP-task is allowed to call this function directly. */
  			if( xNetworkInterfaceOutput( pxNetworkBuffer, pdTRUE ) != pdTRUE )
  			{
  				/* Not sent? Bad news. Maybe link is down? */
  				#if ( ipconfigHAS_DEBUG_PRINTF != 0 )
  					FreeRTOS_printf( ( "xNetworkInterfaceOutput failed. Link down?\n" ) );
  				#else
  					; /* Do nothing */
  				#endif
  			}
  		}
  		else
  		{
  		IPStackEvent_t xSendEvent;
  
  			/* Send a message to the IP-task to send this ARP packet. */
  			xSendEvent.eEventType = eNetworkTxEvent;
  			xSendEvent.pvData = ( void * ) pxNetworkBuffer;
  			if( xSendEventStructToIPTask( &xSendEvent, ( TickType_t ) portMAX_DELAY ) == pdFAIL )
  			{
  				/* Failed to send the message, so release the network buffer. */
  				vReleaseNetworkBufferAndDescriptor( pxNetworkBuffer );
  			}
  		}
  	}
  }
  
  void vARPGenerateRequestPacket( NetworkBufferDescriptor_t * const pxNetworkBuffer )
  {
  ARPPacket_t *pxARPPacket;
  
  /* Part of the Ethernet and ARP headers are always constant when sending an IPv4
   * ARP packet.  This array defines the constant parts, allowing this part of the
   * packet to be filled in using a simple memcpy() instead of individual writes. */
  static const uint8_t xDefaultPartARPPacketHeader[] =
  {
  	0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 	/* Ethernet destination address. */
  	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 	/* Ethernet source address. */
  	0x08, 0x06, 				/* Ethernet frame type (ipARP_FRAME_TYPE). */
  	0x00, 0x01,				/* usHardwareType (ipARP_HARDWARE_TYPE_ETHERNET). */
  	0x08, 0x00,				/* usProtocolType. */
  	ipMAC_ADDRESS_LENGTH_BYTES,		/* ucHardwareAddressLength. */
  	ipIP_ADDRESS_LENGTH_BYTES,		/* ucProtocolAddressLength. */
  	0x00, 0x01,				/* usOperation (ipARP_REQUEST). */
  	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 	/* xSenderHardwareAddress. */
  	0x00, 0x00, 0x00, 0x00,			/* ulSenderProtocolAddress. */
  	0x00, 0x00, 0x00, 0x00, 0x00, 0x00	/* xTargetHardwareAddress. */
  };
  
  	/* Buffer allocation ensures that buffers always have space
  	 * for an ARP packet. See buffer allocation implementations 1
  	 * and 2 under portable/BufferManagement. */
  	configASSERT( pxNetworkBuffer != NULL );
  	configASSERT( pxNetworkBuffer->xDataLength >= sizeof( ARPPacket_t ) );
  
  	pxARPPacket = ( ARPPacket_t * ) pxNetworkBuffer->pucEthernetBuffer;
  
  	/* memcpy the const part of the header information into the correct
  	 * location in the packet.  This copies:
  	 * 	xEthernetHeader.ulDestinationAddress
  	 * 	xEthernetHeader.usFrameType;
  	 * 	xARPHeader.usHardwareType;
  	 * 	xARPHeader.usProtocolType;
  	 * 	xARPHeader.ucHardwareAddressLength;
  	 * 	xARPHeader.ucProtocolAddressLength;
  	 * 	xARPHeader.usOperation;
  	 * 	xARPHeader.xTargetHardwareAddress;
  	 */
  
  	( void ) memcpy( ( void * ) pxARPPacket, ( const void * )xDefaultPartARPPacketHeader, sizeof( xDefaultPartARPPacketHeader ) );
  	( void ) memcpy( ( void * ) pxARPPacket->xEthernetHeader.xSourceAddress.ucBytes, ( const void * ) ipLOCAL_MAC_ADDRESS, ( size_t )ipMAC_ADDRESS_LENGTH_BYTES );
  	( void ) memcpy( ( void * ) pxARPPacket->xARPHeader.xSenderHardwareAddress.ucBytes, ( const void * )ipLOCAL_MAC_ADDRESS, ( size_t )ipMAC_ADDRESS_LENGTH_BYTES );
  
  	( void ) memcpy( ( void * )pxARPPacket->xARPHeader.ucSenderProtocolAddress, ( const void * )ipLOCAL_IP_ADDRESS_POINTER, sizeof( pxARPPacket->xARPHeader.ucSenderProtocolAddress ) );
  	pxARPPacket->xARPHeader.ulTargetProtocolAddress = pxNetworkBuffer->ulIPAddress;
  
  	pxNetworkBuffer->xDataLength = sizeof( ARPPacket_t );
  
  	iptraceCREATING_ARP_REQUEST( pxNetworkBuffer->ulIPAddress );
  }
  /*-----------------------------------------------------------*/
  
  void FreeRTOS_ClearARP( void )
  {
  	( void ) memset( xARPCache, 0, sizeof( xARPCache ) );
  }
  /*-----------------------------------------------------------*/
  
  #if( ipconfigHAS_PRINTF != 0 ) || ( ipconfigHAS_DEBUG_PRINTF != 0 )
  
  	/* MISRA c 2012 rule 8.7 relaxed since this function can be
  	 * called from external location when debugging is enabled */
  	/* coverity[misra_c_2012_rule_8_7_violation] */
  	void FreeRTOS_PrintARPCache( void )
  	{
  	BaseType_t x, xCount = 0;
  
  		/* Loop through each entry in the ARP cache. */
  		for( x = 0; x < ipconfigARP_CACHE_ENTRIES; x++ )
  		{
  			if( ( xARPCache[ x ].ulIPAddress != 0UL ) && ( xARPCache[ x ].ucAge > 0U ) )
  			{
  				/* See if the MAC-address also matches, and we're all happy */
  				FreeRTOS_printf( ( "Arp %2ld: %3u - %16lxip : %02x:%02x:%02x : %02x:%02x:%02x\n",
  					x,
  					xARPCache[ x ].ucAge,
  					xARPCache[ x ].ulIPAddress,
  					xARPCache[ x ].xMACAddress.ucBytes[ 0 ],
  					xARPCache[ x ].xMACAddress.ucBytes[ 1 ],
  					xARPCache[ x ].xMACAddress.ucBytes[ 2 ],
  					xARPCache[ x ].xMACAddress.ucBytes[ 3 ],
  					xARPCache[ x ].xMACAddress.ucBytes[ 4 ],
  					xARPCache[ x ].xMACAddress.ucBytes[ 5 ] ) );
  				xCount++;
  			}
  		}
  
  		FreeRTOS_printf( ( "Arp has %ld entries\n", xCount ) );
  	}
  
  #endif /* ( ipconfigHAS_PRINTF != 0 ) || ( ipconfigHAS_DEBUG_PRINTF != 0 ) */
  