1
0
mirror of https://github.com/FreeRTOS/FreeRTOS-Plus-TCP synced 2025-10-21 23:30:39 +08:00
Files
FreeRTOS-Plus-TCP/source/FreeRTOS_ARP.c
Hein Tibosch 7a58ae819c Remove xCheckLoopback function (#1098)
* Remove xCheckLoopback function because we now have a loopback endpoint.

* Remove xCheckLoopback CBMC proof

* Remove xCheckLoopback UTs

---------

Co-authored-by: tony-josi-aws <tonyjosi@amazon.com>
2024-02-10 23:45:36 +05:30

1548 lines
66 KiB
C

/*
* FreeRTOS+TCP <DEVELOPMENT BRANCH>
* Copyright (C) 2022 Amazon.com, Inc. or its affiliates. All Rights Reserved.
*
* SPDX-License-Identifier: MIT
*
* 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
*/
/**
* @file FreeRTOS_ARP.c
* @brief Implements the Address Resolution Protocol for the FreeRTOS+TCP network stack.
*/
/* 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_IP_Timers.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 "NetworkBufferManagement.h"
#include "NetworkInterface.h"
#include "FreeRTOS_Routing.h"
#include "FreeRTOS_ND.h"
/** @brief 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 ( 3U )
/** @brief The time between gratuitous ARPs. */
#ifndef arpGRATUITOUS_ARP_PERIOD
#define arpGRATUITOUS_ARP_PERIOD ( pdMS_TO_TICKS( 20000U ) )
#endif
/** @brief When there is another device which has the same IP address as the IP address
* of this device, a defensive ARP request should be sent out. However, according to
* RFC 5227 section 1.1, there must be a minimum interval of 10 seconds between
* consecutive defensive ARP packets. */
#ifndef arpIP_CLASH_RESET_TIMEOUT_MS
#define arpIP_CLASH_RESET_TIMEOUT_MS 10000U
#endif
/** @brief Maximum number of defensive ARPs to be sent for an ARP clash per
* arpIP_CLASH_RESET_TIMEOUT_MS period. The retries are limited to one as outlined
* by RFC 5227 section 2.4 part b.*/
#ifndef arpIP_CLASH_MAX_RETRIES
#define arpIP_CLASH_MAX_RETRIES 1U
#endif
#if ( ipconfigUSE_IPv4 != 0 )
static void vARPProcessPacketRequest( ARPPacket_t * pxARPFrame,
NetworkEndPoint_t * pxTargetEndPoint,
uint32_t ulSenderProtocolAddress );
static void vARPProcessPacketReply( const ARPPacket_t * pxARPFrame,
NetworkEndPoint_t * pxTargetEndPoint,
uint32_t ulSenderProtocolAddress );
/*
* Lookup an MAC address in the ARP cache from the IP address.
*/
static eARPLookupResult_t prvCacheLookup( uint32_t ulAddressToLookup,
MACAddress_t * const pxMACAddress,
NetworkEndPoint_t ** ppxEndPoint );
static eARPLookupResult_t eARPGetCacheEntryGateWay( uint32_t * pulIPAddress,
MACAddress_t * const pxMACAddress,
struct xNetworkEndPoint ** ppxEndPoint );
#endif /* ( ipconfigUSE_IPv4 != 0 ) */
static BaseType_t prvFindCacheEntry( const MACAddress_t * pxMACAddress,
const uint32_t ulIPAddress,
struct xNetworkEndPoint * pxEndPoint,
CacheLocation_t * pxLocation );
/*-----------------------------------------------------------*/
/** @brief The ARP cache. */
_static ARPCacheRow_t xARPCache[ ipconfigARP_CACHE_ENTRIES ];
/*
* 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
* gratuitous 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 gratuitous ARP message. */
BaseType_t xARPHadIPClash;
/* MAC-address of the other device containing the same IP-address. */
MACAddress_t xARPClashMacAddress;
#endif /* ipconfigARP_USE_CLASH_DETECTION */
/*-----------------------------------------------------------*/
#if ( ipconfigUSE_IPv4 != 0 )
/** @brief 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 = 0U;
/**
* @brief Process the ARP packets.
*
* @param[in] pxNetworkBuffer The network buffer with the packet to be processed.
*
* @return An enum which says whether to return the frame or to release it.
*/
eFrameProcessingResult_t eARPProcessPacket( const NetworkBufferDescriptor_t * pxNetworkBuffer )
{
/* MISRA Ref 11.3.1 [Misaligned access] */
/* More details at: https://github.com/FreeRTOS/FreeRTOS-Plus-TCP/blob/main/MISRA.md#rule-113 */
/* coverity[misra_c_2012_rule_11_3_violation] */
ARPPacket_t * pxARPFrame = ( ( ARPPacket_t * ) pxNetworkBuffer->pucEthernetBuffer );
eFrameProcessingResult_t eReturn = eReleaseBuffer;
const ARPHeader_t * pxARPHeader;
uint32_t ulTargetProtocolAddress, ulSenderProtocolAddress;
/* memcpy() helper variables for MISRA Rule 21.15 compliance*/
const void * pvCopySource;
void * pvCopyDest;
NetworkEndPoint_t * pxTargetEndPoint = pxNetworkBuffer->pxEndPoint;
/* Next defensive request must not be sent for arpIP_CLASH_RESET_TIMEOUT_MS
* period. */
static TickType_t uxARPClashTimeoutPeriod = pdMS_TO_TICKS( arpIP_CLASH_RESET_TIMEOUT_MS );
/* This local variable is used to keep track of number of ARP requests sent and
* also to limit the requests to arpIP_CLASH_MAX_RETRIES per arpIP_CLASH_RESET_TIMEOUT_MS
* period. */
static UBaseType_t uxARPClashCounter = 0U;
/* The time at which the last ARP clash was sent. */
static TimeOut_t xARPClashTimeOut;
pxARPHeader = &( pxARPFrame->xARPHeader );
/* Only Ethernet hardware type is supported.
* Only IPv4 address can be present in the ARP packet.
* The hardware length (the MAC address) must be 6 bytes. And,
* The Protocol address length must be 4 bytes as it is IPv4. */
if( ( pxARPHeader->usHardwareType == ipARP_HARDWARE_TYPE_ETHERNET ) &&
( pxARPHeader->usProtocolType == ipARP_PROTOCOL_TYPE ) &&
( pxARPHeader->ucHardwareAddressLength == ipMAC_ADDRESS_LENGTH_BYTES ) &&
( pxARPHeader->ucProtocolAddressLength == ipIP_ADDRESS_LENGTH_BYTES ) )
{
/* The field ucSenderProtocolAddress is badly aligned, copy byte-by-byte. */
/*
* Use helper variables for memcpy() to remain
* compliant with MISRA Rule 21.15. These should be
* optimized away.
*/
pvCopySource = pxARPHeader->ucSenderProtocolAddress;
pvCopyDest = &ulSenderProtocolAddress;
( void ) memcpy( pvCopyDest, pvCopySource, sizeof( ulSenderProtocolAddress ) );
/* The field ulTargetProtocolAddress is well-aligned, a 32-bits copy. */
ulTargetProtocolAddress = pxARPHeader->ulTargetProtocolAddress;
if( uxARPClashCounter != 0U )
{
/* Has the timeout been reached? */
if( xTaskCheckForTimeOut( &xARPClashTimeOut, &uxARPClashTimeoutPeriod ) == pdTRUE )
{
/* We have waited long enough, reset the counter. */
uxARPClashCounter = 0;
}
}
/* Check whether the lowest bit of the highest byte is 1 to check for
* multicast address or even a broadcast address (FF:FF:FF:FF:FF:FF). */
if( ( pxARPHeader->xSenderHardwareAddress.ucBytes[ 0 ] & 0x01U ) == 0x01U )
{
/* Senders address is a multicast OR broadcast address which is not
* allowed for an ARP packet. Drop the packet. See RFC 1812 section
* 3.3.2. */
iptraceDROPPED_INVALID_ARP_PACKET( pxARPHeader );
}
else if( ( ipFIRST_LOOPBACK_IPv4 <= ( FreeRTOS_ntohl( ulSenderProtocolAddress ) ) ) &&
( ( FreeRTOS_ntohl( ulSenderProtocolAddress ) ) < ipLAST_LOOPBACK_IPv4 ) )
{
/* The local loopback addresses must never appear outside a host. See RFC 1122
* section 3.2.1.3. */
iptraceDROPPED_INVALID_ARP_PACKET( pxARPHeader );
}
/* Check whether there is a clash with another device for this IP address. */
else if( ( pxTargetEndPoint != NULL ) && ( ulSenderProtocolAddress == pxTargetEndPoint->ipv4_settings.ulIPAddress ) )
{
if( uxARPClashCounter < arpIP_CLASH_MAX_RETRIES )
{
/* Increment the counter. */
uxARPClashCounter++;
/* Send out a defensive ARP request. */
FreeRTOS_OutputARPRequest( pxTargetEndPoint->ipv4_settings.ulIPAddress );
/* Since an ARP Request for this IP was just sent, do not send a gratuitous
* ARP for arpGRATUITOUS_ARP_PERIOD. */
xLastGratuitousARPTime = xTaskGetTickCount();
/* Note the time at which this request was sent. */
vTaskSetTimeOutState( &xARPClashTimeOut );
/* Reset the time-out period to the given value. */
uxARPClashTimeoutPeriod = pdMS_TO_TICKS( arpIP_CLASH_RESET_TIMEOUT_MS );
}
/* Process received ARP frame to see if there is a clash. */
#if ( ipconfigARP_USE_CLASH_DETECTION != 0 )
{
NetworkEndPoint_t * pxSourceEndPoint = FreeRTOS_FindEndPointOnIP_IPv4( ulSenderProtocolAddress, 2 );
if( ( pxSourceEndPoint != NULL ) && ( pxSourceEndPoint->ipv4_settings.ulIPAddress == ulSenderProtocolAddress ) )
{
xARPHadIPClash = pdTRUE;
/* Remember the MAC-address of the other device which has the same IP-address. */
( void ) memcpy( xARPClashMacAddress.ucBytes, pxARPHeader->xSenderHardwareAddress.ucBytes, sizeof( xARPClashMacAddress.ucBytes ) );
}
}
#endif /* ipconfigARP_USE_CLASH_DETECTION */
}
else
{
traceARP_PACKET_RECEIVED();
/* Some extra logging while still testing. */
#if ( ipconfigHAS_DEBUG_PRINTF != 0 )
if( pxARPHeader->usOperation == ( uint16_t ) ipARP_REPLY )
{
FreeRTOS_debug_printf( ( "ipARP_REPLY from %xip to %xip end-point %xip\n",
( unsigned ) FreeRTOS_ntohl( ulSenderProtocolAddress ),
( unsigned ) FreeRTOS_ntohl( ulTargetProtocolAddress ),
( unsigned ) FreeRTOS_ntohl( ( pxTargetEndPoint != NULL ) ? pxTargetEndPoint->ipv4_settings.ulIPAddress : 0U ) ) );
}
#endif /* ( ipconfigHAS_DEBUG_PRINTF != 0 ) */
#if ( ipconfigHAS_DEBUG_PRINTF != 0 )
if( ( pxARPHeader->usOperation == ( uint16_t ) ipARP_REQUEST ) &&
( ulSenderProtocolAddress != ulTargetProtocolAddress ) &&
( pxTargetEndPoint != NULL ) )
{
FreeRTOS_debug_printf( ( "ipARP_REQUEST from %xip to %xip end-point %xip\n",
( unsigned ) FreeRTOS_ntohl( ulSenderProtocolAddress ),
( unsigned ) FreeRTOS_ntohl( ulTargetProtocolAddress ),
( unsigned ) ( FreeRTOS_ntohl( ( pxTargetEndPoint != NULL ) ? pxTargetEndPoint->ipv4_settings.ulIPAddress : 0U ) ) ) );
}
#endif /* ( ipconfigHAS_DEBUG_PRINTF != 0 ) */
/* ulTargetProtocolAddress won't be used unless logging is enabled. */
( void ) ulTargetProtocolAddress;
/* Don't do anything if the local IP address is zero because
* that means a DHCP request has not completed. */
if( ( pxTargetEndPoint != NULL ) && ( pxTargetEndPoint->bits.bEndPointUp != pdFALSE_UNSIGNED ) )
{
switch( pxARPHeader->usOperation )
{
case ipARP_REQUEST:
if( ulTargetProtocolAddress == pxTargetEndPoint->ipv4_settings.ulIPAddress )
{
if( memcmp( pxTargetEndPoint->xMACAddress.ucBytes,
pxARPHeader->xSenderHardwareAddress.ucBytes,
ipMAC_ADDRESS_LENGTH_BYTES ) != 0 )
{
vARPProcessPacketRequest( pxARPFrame, pxTargetEndPoint, ulSenderProtocolAddress );
eReturn = eReturnEthernetFrame;
}
}
/* Check if its a Gratuitous ARP request and verify if it belongs to same subnet mask. */
else if( ( ulSenderProtocolAddress == ulTargetProtocolAddress ) &&
( ( ulSenderProtocolAddress & pxTargetEndPoint->ipv4_settings.ulNetMask ) == ( pxTargetEndPoint->ipv4_settings.ulNetMask & pxTargetEndPoint->ipv4_settings.ulIPAddress ) ) )
{
const MACAddress_t xGARPTargetAddress = { { 0, 0, 0, 0, 0, 0 } };
/* Make sure target MAC address is either ff:ff:ff:ff:ff:ff or 00:00:00:00:00:00 and senders MAC
* address is not matching with the endpoint MAC address. */
if( ( ( memcmp( pxARPHeader->xTargetHardwareAddress.ucBytes, xBroadcastMACAddress.ucBytes, ipMAC_ADDRESS_LENGTH_BYTES ) == 0 ) ||
( ( memcmp( pxARPHeader->xTargetHardwareAddress.ucBytes, xGARPTargetAddress.ucBytes, ipMAC_ADDRESS_LENGTH_BYTES ) == 0 ) ) ) &&
( memcmp( pxTargetEndPoint->xMACAddress.ucBytes, pxARPHeader->xSenderHardwareAddress.ucBytes, ipMAC_ADDRESS_LENGTH_BYTES ) != 0 ) )
{
MACAddress_t xHardwareAddress;
NetworkEndPoint_t * pxCachedEndPoint;
pxCachedEndPoint = NULL;
/* The request is a Gratuitous ARP message.
* Refresh the entry if it already exists. */
/* Determine the ARP cache status for the requested IP address. */
if( eARPGetCacheEntry( &( ulSenderProtocolAddress ), &( xHardwareAddress ), &( pxCachedEndPoint ) ) == eARPCacheHit )
{
/* Check if the endpoint matches with the one present in the ARP cache */
if( pxCachedEndPoint == pxTargetEndPoint )
{
vARPRefreshCacheEntry( &( pxARPHeader->xSenderHardwareAddress ), ulSenderProtocolAddress, pxTargetEndPoint );
}
}
}
}
else
{
/* do nothing, coverity happy */
}
break;
case ipARP_REPLY:
vARPProcessPacketReply( pxARPFrame, pxTargetEndPoint, ulSenderProtocolAddress );
break;
default:
/* Invalid. */
break;
}
}
}
}
else
{
iptraceDROPPED_INVALID_ARP_PACKET( pxARPHeader );
}
return eReturn;
}
/*-----------------------------------------------------------*/
/**
* @brief Process an ARP request packets.
*
* @param[in] pxARPFrame the complete ARP-frame.
* @param[in] pxTargetEndPoint the end-point that handles the peer's address.
* @param[in] ulSenderProtocolAddress the IP-address of the sender.
*
*/
static void vARPProcessPacketRequest( ARPPacket_t * pxARPFrame,
NetworkEndPoint_t * pxTargetEndPoint,
uint32_t ulSenderProtocolAddress )
{
ARPHeader_t * pxARPHeader = &( pxARPFrame->xARPHeader );
/* memcpy() helper variables for MISRA Rule 21.15 compliance*/
const void * pvCopySource;
void * pvCopyDest;
/* The packet contained an ARP request. Was it for the IP
* address of one of the end-points? */
/* It has been confirmed that pxTargetEndPoint is not NULL. */
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, pxTargetEndPoint );
/* Generate a reply payload in the same buffer. */
pxARPHeader->usOperation = ( uint16_t ) ipARP_REPLY;
/* A double IP address cannot be detected here, it is taken care in the Process ARP Packets path */
/*
* Use helper variables for memcpy() to remain
* compliant with MISRA Rule 21.15. These should be
* optimized away.
*/
pvCopySource = pxARPHeader->xSenderHardwareAddress.ucBytes;
pvCopyDest = pxARPHeader->xTargetHardwareAddress.ucBytes;
( void ) memcpy( pvCopyDest, pvCopySource, sizeof( MACAddress_t ) );
pxARPHeader->ulTargetProtocolAddress = ulSenderProtocolAddress;
/*
* Use helper variables for memcpy() to remain
* compliant with MISRA Rule 21.15. These should be
* optimized away.
*/
pvCopySource = pxTargetEndPoint->xMACAddress.ucBytes;
pvCopyDest = pxARPHeader->xSenderHardwareAddress.ucBytes;
( void ) memcpy( pvCopyDest, pvCopySource, sizeof( MACAddress_t ) );
pvCopySource = &( pxTargetEndPoint->ipv4_settings.ulIPAddress );
pvCopyDest = pxARPHeader->ucSenderProtocolAddress;
( void ) memcpy( pvCopyDest, pvCopySource, sizeof( pxARPHeader->ucSenderProtocolAddress ) );
}
/*-----------------------------------------------------------*/
/**
* @brief A device has sent an ARP reply, process it.
* @param[in] pxARPFrame The ARP packet received.
* @param[in] pxTargetEndPoint The end-point on which it is received.
* @param[in] ulSenderProtocolAddress The IPv4 address involved.
*/
static void vARPProcessPacketReply( const ARPPacket_t * pxARPFrame,
NetworkEndPoint_t * pxTargetEndPoint,
uint32_t ulSenderProtocolAddress )
{
const ARPHeader_t * pxARPHeader = &( pxARPFrame->xARPHeader );
uint32_t ulTargetProtocolAddress = pxARPHeader->ulTargetProtocolAddress;
/* If the packet is meant for this device or if the entry already exists. */
if( ( ulTargetProtocolAddress == pxTargetEndPoint->ipv4_settings.ulIPAddress ) ||
( xIsIPInARPCache( ulSenderProtocolAddress ) == pdTRUE ) )
{
iptracePROCESSING_RECEIVED_ARP_REPLY( ulTargetProtocolAddress );
vARPRefreshCacheEntry( &( pxARPHeader->xSenderHardwareAddress ), ulSenderProtocolAddress, pxTargetEndPoint );
}
if( ( pxARPWaitingNetworkBuffer != NULL ) &&
( uxIPHeaderSizePacket( pxARPWaitingNetworkBuffer ) == ipSIZE_OF_IPv4_HEADER ) )
{
/* MISRA Ref 11.3.1 [Misaligned access] */
/* More details at: https://github.com/FreeRTOS/FreeRTOS-Plus-TCP/blob/main/MISRA.md#rule-113 */
/* coverity[misra_c_2012_rule_11_3_violation] */
const IPPacket_t * pxARPWaitingIPPacket = ( ( IPPacket_t * ) pxARPWaitingNetworkBuffer->pucEthernetBuffer );
const IPHeader_t * pxARPWaitingIPHeader = &( pxARPWaitingIPPacket->xIPHeader );
if( ulSenderProtocolAddress == pxARPWaitingIPHeader->ulSourceIPAddress )
{
IPStackEvent_t xEventMessage;
const TickType_t xDontBlock = ( TickType_t ) 0;
xEventMessage.eEventType = eNetworkRxEvent;
xEventMessage.pvData = ( void * ) pxARPWaitingNetworkBuffer;
if( xSendEventStructToIPTask( &xEventMessage, xDontBlock ) != pdPASS )
{
/* Failed to send the message, so release the network buffer. */
vReleaseNetworkBufferAndDescriptor( pxARPWaitingNetworkBuffer );
}
/* Clear the buffer. */
pxARPWaitingNetworkBuffer = NULL;
/* Found an ARP resolution, disable ARP resolution timer. */
vIPSetARPResolutionTimerEnableState( pdFALSE );
iptrace_DELAYED_ARP_REQUEST_REPLIED();
}
}
}
/*-----------------------------------------------------------*/
#endif /* ( ipconfigUSE_IPv4 != 0 ) */
/**
* @brief Check whether an IP address is in the ARP cache.
*
* @param[in] ulAddressToLookup The 32-bit representation of an IP address to
* check for.
*
* @return When the IP-address is found: pdTRUE, else pdFALSE.
*/
BaseType_t xIsIPInARPCache( uint32_t ulAddressToLookup )
{
BaseType_t x, xReturn = pdFALSE;
/* 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 )
{
xReturn = pdTRUE;
/* A matching valid entry was found. */
if( xARPCache[ x ].ucValid == ( uint8_t ) pdFALSE )
{
/* This entry is waiting an ARP reply, so is not valid. */
xReturn = pdFALSE;
}
break;
}
}
return xReturn;
}
/**
* @brief Check whether a packet needs ARP resolution if it is on local subnet. If required send an ARP request.
*
* @param[in] pxNetworkBuffer The network buffer with the packet to be checked.
*
* @return pdTRUE if the packet needs ARP resolution, pdFALSE otherwise.
*/
BaseType_t xCheckRequiresARPResolution( const NetworkBufferDescriptor_t * pxNetworkBuffer )
{
BaseType_t xNeedsARPResolution = pdFALSE;
switch( uxIPHeaderSizePacket( pxNetworkBuffer ) )
{
#if ( ipconfigUSE_IPv4 != 0 )
case ipSIZE_OF_IPv4_HEADER:
{
/* MISRA Ref 11.3.1 [Misaligned access] */
/* More details at: https://github.com/FreeRTOS/FreeRTOS-Plus-TCP/blob/main/MISRA.md#rule-113 */
/* coverity[misra_c_2012_rule_11_3_violation] */
const IPPacket_t * pxIPPacket = ( ( const IPPacket_t * ) pxNetworkBuffer->pucEthernetBuffer );
const IPHeader_t * pxIPHeader = &( pxIPPacket->xIPHeader );
const IPV4Parameters_t * pxIPv4Settings = &( pxNetworkBuffer->pxEndPoint->ipv4_settings );
if( ( pxIPHeader->ulSourceIPAddress & pxIPv4Settings->ulNetMask ) == ( pxIPv4Settings->ulIPAddress & pxIPv4Settings->ulNetMask ) )
{
/* If the IP is on the same subnet and we do not have an ARP entry already,
* then we should send out ARP for finding the MAC address. */
if( xIsIPInARPCache( pxIPHeader->ulSourceIPAddress ) == pdFALSE )
{
FreeRTOS_OutputARPRequest( pxIPHeader->ulSourceIPAddress );
/* This packet needs resolution since this is on the same subnet
* but not in the ARP cache. */
xNeedsARPResolution = pdTRUE;
}
}
break;
}
#endif /* ( ipconfigUSE_IPv4 != 0 ) */
#if ( ipconfigUSE_IPv6 != 0 )
case ipSIZE_OF_IPv6_HEADER:
{
/* MISRA Ref 11.3.1 [Misaligned access] */
/* More details at: https://github.com/FreeRTOS/FreeRTOS-Plus-TCP/blob/main/MISRA.md#rule-113 */
/* coverity[misra_c_2012_rule_11_3_violation] */
IPPacket_IPv6_t * pxIPPacket = ( ( IPPacket_IPv6_t * ) pxNetworkBuffer->pucEthernetBuffer );
IPHeader_IPv6_t * pxIPHeader = &( pxIPPacket->xIPHeader );
IPv6_Address_t * pxIPAddress = &( pxIPHeader->xSourceAddress );
uint8_t ucNextHeader = pxIPHeader->ucNextHeader;
if( ( ucNextHeader == ipPROTOCOL_TCP ) ||
( ucNextHeader == ipPROTOCOL_UDP ) )
{
IPv6_Type_t eType = xIPv6_GetIPType( ( const IPv6_Address_t * ) pxIPAddress );
FreeRTOS_debug_printf( ( "xCheckRequiresARPResolution: %pip type %s\n",
( void * ) pxIPAddress->ucBytes,
( eType == eIPv6_Global ) ? "Global" :
( eType == eIPv6_LinkLocal ) ? "LinkLocal" :
( eType == eIPv6_Loopback ) ? "Loopback" :
"other" ) );
if( eType == eIPv6_LinkLocal )
{
MACAddress_t xMACAddress;
NetworkEndPoint_t * pxEndPoint;
eARPLookupResult_t eResult;
char pcName[ 80 ];
( void ) memset( &( pcName ), 0, sizeof( pcName ) );
eResult = eNDGetCacheEntry( pxIPAddress, &xMACAddress, &pxEndPoint );
FreeRTOS_printf( ( "xCheckRequiresARPResolution: eResult %s with EP %s\n", ( eResult == eARPCacheMiss ) ? "Miss" : ( eResult == eARPCacheHit ) ? "Hit" : "Error", pcEndpointName( pxEndPoint, pcName, sizeof pcName ) ) );
if( eResult == eARPCacheMiss )
{
NetworkBufferDescriptor_t * pxTempBuffer;
size_t uxNeededSize;
uxNeededSize = sizeof( ICMPPacket_IPv6_t );
pxTempBuffer = pxGetNetworkBufferWithDescriptor( BUFFER_FROM_WHERE_CALL( 199 ) uxNeededSize, 0U );
if( pxTempBuffer != NULL )
{
pxTempBuffer->pxEndPoint = pxNetworkBuffer->pxEndPoint;
pxTempBuffer->pxInterface = pxNetworkBuffer->pxInterface;
vNDSendNeighbourSolicitation( pxTempBuffer, pxIPAddress );
}
xNeedsARPResolution = pdTRUE;
}
}
}
break;
}
#endif /* ( ipconfigUSE_IPv6 != 0 ) */
default:
/* Shouldn't reach here */
/* MISRA 16.4 Compliance */
break;
}
return xNeedsARPResolution;
}
#if ( ipconfigUSE_ARP_REMOVE_ENTRY != 0 )
/**
* @brief Remove an ARP cache entry that matches with .pxMACAddress.
*
* @param[in] pxMACAddress Pointer to the MAC address whose entry shall
* be removed.
* @return When the entry was found and remove: the IP-address, otherwise zero.
*/
uint32_t ulARPRemoveCacheEntryByMac( const MACAddress_t * pxMACAddress )
{
BaseType_t x;
uint32_t lResult = 0;
configASSERT( pxMACAddress != NULL );
/* 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 */
/*-----------------------------------------------------------*/
/**
* @brief Look for an IP-MAC couple in ARP cache and reset the 'age' field. If no match
* is found then no action will be taken.
*
* @param[in] pxMACAddress Pointer to the MAC address whose entry needs to be updated.
* @param[in] ulIPAddress the IP address whose corresponding entry needs to be updated.
*/
void vARPRefreshCacheEntryAge( const MACAddress_t * pxMACAddress,
const uint32_t ulIPAddress )
{
BaseType_t x;
if( pxMACAddress != NULL )
{
/* Loop through each entry in the ARP cache. */
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 )
{
/* Does this cache entry have the same MAC address? */
if( memcmp( xARPCache[ x ].xMACAddress.ucBytes, pxMACAddress->ucBytes, sizeof( pxMACAddress->ucBytes ) ) == 0 )
{
/* The IP address and the MAC matched, update this entry age. */
xARPCache[ x ].ucAge = ( uint8_t ) ipconfigMAX_ARP_AGE;
break;
}
}
}
}
}
/*-----------------------------------------------------------*/
/**
* @brief Add/update the ARP cache entry MAC-address to IP-address mapping.
*
* @param[in] pxMACAddress Pointer to the MAC address whose mapping is being
* updated.
* @param[in] ulIPAddress 32-bit representation of the IP-address whose mapping
* is being updated.
* @param[in] pxEndPoint The end-point stored in the table.
*/
void vARPRefreshCacheEntry( const MACAddress_t * pxMACAddress,
const uint32_t ulIPAddress,
struct xNetworkEndPoint * pxEndPoint )
{
#if ( ipconfigARP_STORES_REMOTE_ADDRESSES == 0 )
/* Only process the IP address if it is on the local network. */
BaseType_t xAddressIsLocal = ( FreeRTOS_FindEndPointOnNetMask( ulIPAddress, 2 ) != NULL ) ? 1 : 0; /* ARP remote address. */
/* Only process the IP address if it matches with one of the end-points. */
if( xAddressIsLocal != 0 )
#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
{
CacheLocation_t xLocation;
BaseType_t xReady;
xReady = prvFindCacheEntry( pxMACAddress, ulIPAddress, pxEndPoint, &( xLocation ) );
if( xReady == pdFALSE )
{
if( xLocation.xMacEntry >= 0 )
{
xLocation.xUseEntry = xLocation.xMacEntry;
if( xLocation.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[ xLocation.xIpEntry ] ), 0, sizeof( ARPCacheRow_t ) );
}
}
else if( xLocation.xIpEntry >= 0 )
{
/* An entry containing the IP-address was found, but it had a different MAC address */
xLocation.xUseEntry = xLocation.xIpEntry;
}
else
{
/* No matching entry found. */
}
/* If the entry was not found, we use the oldest entry and set the IPaddress */
xARPCache[ xLocation.xUseEntry ].ulIPAddress = ulIPAddress;
if( pxMACAddress != NULL )
{
( void ) memcpy( xARPCache[ xLocation.xUseEntry ].xMACAddress.ucBytes, pxMACAddress->ucBytes, sizeof( pxMACAddress->ucBytes ) );
iptraceARP_TABLE_ENTRY_CREATED( ulIPAddress, ( *pxMACAddress ) );
/* And this entry does not need immediate attention */
xARPCache[ xLocation.xUseEntry ].ucAge = ( uint8_t ) ipconfigMAX_ARP_AGE;
xARPCache[ xLocation.xUseEntry ].ucValid = ( uint8_t ) pdTRUE;
xARPCache[ xLocation.xUseEntry ].pxEndPoint = pxEndPoint;
}
else if( xLocation.xIpEntry < 0 )
{
xARPCache[ xLocation.xUseEntry ].ucAge = ( uint8_t ) ipconfigMAX_ARP_RETRANSMISSIONS;
xARPCache[ xLocation.xUseEntry ].ucValid = ( uint8_t ) pdFALSE;
}
else
{
/* Nothing will be stored. */
}
}
}
}
/*-----------------------------------------------------------*/
/**
* @brief The results of an ARP look-up shall be stored in the ARP cache.
* This helper function looks up the location.
* @param[in] pxMACAddress The MAC-address belonging to the IP-address.
* @param[in] ulIPAddress The IP-address of the entry.
* @param[in] pxEndPoint The end-point that will stored in the table.
* @param[out] pxLocation The results of this search are written in this struct.
*/
static BaseType_t prvFindCacheEntry( const MACAddress_t * pxMACAddress,
const uint32_t ulIPAddress,
struct xNetworkEndPoint * pxEndPoint,
CacheLocation_t * pxLocation )
{
BaseType_t x = 0;
uint8_t ucMinAgeFound = 0U;
BaseType_t xReturn = pdFALSE;
#if ( ipconfigARP_STORES_REMOTE_ADDRESSES != 0 )
BaseType_t xAddressIsLocal = ( FreeRTOS_FindEndPointOnNetMask( ulIPAddress, 2 ) != NULL ) ? 1 : 0; /* ARP remote address. */
#endif
/* Start with the maximum possible number. */
ucMinAgeFound--;
pxLocation->xIpEntry = -1;
pxLocation->xMacEntry = -1;
pxLocation->xUseEntry = 0;
/* For each entry in the ARP cache table. */
for( x = 0; x < ipconfigARP_CACHE_ENTRIES; x++ )
{
BaseType_t xMatchingMAC = pdFALSE;
if( pxMACAddress != NULL )
{
if( memcmp( xARPCache[ x ].xMACAddress.ucBytes, pxMACAddress->ucBytes, sizeof( pxMACAddress->ucBytes ) ) == 0 )
{
xMatchingMAC = pdTRUE;
}
}
/* 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". */
pxLocation->xIpEntry = x;
break;
}
/* See if the MAC-address also matches. */
if( xMatchingMAC != pdFALSE )
{
/* This function will be called for each received packet
* This is by far the most common path. */
xARPCache[ x ].ucAge = ( uint8_t ) ipconfigMAX_ARP_AGE;
xARPCache[ x ].ucValid = ( uint8_t ) pdTRUE;
xARPCache[ x ].pxEndPoint = pxEndPoint;
/* Indicate to the caller that the entry is updated. */
xReturn = pdTRUE;
break;
}
/* 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. */
pxLocation->xIpEntry = x;
}
else if( xMatchingMAC != pdFALSE )
{
/* 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 xOtherIsLocal = ( FreeRTOS_FindEndPointOnNetMask( xARPCache[ x ].ulIPAddress, 3 ) != NULL ) ? 1 : 0; /* ARP remote address. */
if( xAddressIsLocal == xOtherIsLocal )
{
pxLocation->xMacEntry = x;
}
}
#else /* if ( ipconfigARP_STORES_REMOTE_ADDRESSES != 0 ) */
{
pxLocation->xMacEntry = x;
}
#endif /* if ( ipconfigARP_STORES_REMOTE_ADDRESSES != 0 ) */
}
/* _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;
pxLocation->xUseEntry = x;
}
else
{
/* Nothing happens to this cache entry for now. */
}
} /* for( x = 0; x < ipconfigARP_CACHE_ENTRIES; x++ ) */
return xReturn;
}
/*-----------------------------------------------------------*/
#if ( ipconfigUSE_ARP_REVERSED_LOOKUP == 1 )
/**
* @brief Retrieve an entry from the cache table
*
* @param[in] pxMACAddress The MAC-address of the entry of interest.
* @param[out] pulIPAddress set to the IP-address found, or unchanged when not found.
*
* @return Either eARPCacheMiss or eARPCacheHit.
*/
eARPLookupResult_t eARPGetCacheEntryByMac( const MACAddress_t * const pxMACAddress,
uint32_t * pulIPAddress,
struct xNetworkInterface ** ppxInterface )
{
BaseType_t x;
eARPLookupResult_t eReturn = eARPCacheMiss;
configASSERT( pxMACAddress != NULL );
configASSERT( pulIPAddress != NULL );
if( ppxInterface != NULL )
{
*( ppxInterface ) = NULL;
}
/* 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( pxMACAddress->ucBytes, xARPCache[ x ].xMACAddress.ucBytes, sizeof( MACAddress_t ) ) == 0 )
{
*pulIPAddress = xARPCache[ x ].ulIPAddress;
if( ( ppxInterface != NULL ) &&
( xARPCache[ x ].pxEndPoint != NULL ) )
{
*( ppxInterface ) = xARPCache[ x ].pxEndPoint->pxNetworkInterface;
}
eReturn = eARPCacheHit;
break;
}
}
return eReturn;
}
#endif /* ipconfigUSE_ARP_REVERSED_LOOKUP */
/*-----------------------------------------------------------*/
#if ( ipconfigUSE_IPv4 != 0 )
/**
* @brief Look for ulIPAddress in the ARP cache.
*
* @param[in,out] pulIPAddress Pointer to the IP-address to be queried to the ARP cache.
* @param[in,out] pxMACAddress Pointer to a MACAddress_t variable where the MAC address
* will be stored, if found.
* @param[out] ppxEndPoint Pointer to the end-point of the gateway will be stored.
*
* @return If the IP address exists, copy the associated MAC address into pxMACAddress,
* refresh the ARP cache entry's age, and return eARPCacheHit. If the IP
* address does not exist in the ARP cache return eARPCacheMiss. If the packet
* cannot be sent for any reason (maybe DHCP is still in process, or the
* addressing needs a gateway but there isn't a gateway defined) then return
* eCantSendPacket.
*/
eARPLookupResult_t eARPGetCacheEntry( uint32_t * pulIPAddress,
MACAddress_t * const pxMACAddress,
struct xNetworkEndPoint ** ppxEndPoint )
{
eARPLookupResult_t eReturn;
uint32_t ulAddressToLookup;
NetworkEndPoint_t * pxEndPoint = NULL;
configASSERT( pxMACAddress != NULL );
configASSERT( pulIPAddress != NULL );
configASSERT( ppxEndPoint != NULL );
*( ppxEndPoint ) = NULL;
ulAddressToLookup = *pulIPAddress;
pxEndPoint = FreeRTOS_FindEndPointOnIP_IPv4( ulAddressToLookup, 0 );
if( xIsIPv4Multicast( ulAddressToLookup ) != 0 )
{
/* Get the lowest 23 bits of the IP-address. */
vSetMultiCastIPv4MacAddress( ulAddressToLookup, pxMACAddress );
eReturn = eCantSendPacket;
pxEndPoint = FreeRTOS_FirstEndPoint( NULL );
for( ;
pxEndPoint != NULL;
pxEndPoint = FreeRTOS_NextEndPoint( NULL, pxEndPoint ) )
{
if( pxEndPoint->bits.bIPv6 == 0U ) /*NULL End Point is checked in the for loop, no need for an extra check */
{
/* For multi-cast, use the first IPv4 end-point. */
*( ppxEndPoint ) = pxEndPoint;
eReturn = eARPCacheHit;
break;
}
}
}
else if( ( FreeRTOS_htonl( ulAddressToLookup ) & 0xffU ) == 0xffU ) /* Is this a broadcast address like x.x.x.255 ? */
{
/* This is a broadcast so it uses the broadcast MAC address. */
( void ) memcpy( pxMACAddress->ucBytes, xBroadcastMACAddress.ucBytes, sizeof( MACAddress_t ) );
pxEndPoint = FreeRTOS_FindEndPointOnNetMask( ulAddressToLookup, 4 );
if( pxEndPoint != NULL )
{
*( ppxEndPoint ) = pxEndPoint;
}
eReturn = eARPCacheHit;
}
else
{
eReturn = eARPGetCacheEntryGateWay( pulIPAddress, pxMACAddress, ppxEndPoint );
}
return eReturn;
}
/*-----------------------------------------------------------*/
/**
* @brief The IPv4 address is apparently a web-address. Find a gateway..
* @param[in] pulIPAddress The target IP-address. It may be replaced with the IP
* address of a gateway.
* @param[in] pxMACAddress In case the MAC-address is found in cache, it will be
* stored to the buffer provided.
* @param[out] ppxEndPoint The end-point of the gateway will be copy to the pointee.
*/
static eARPLookupResult_t eARPGetCacheEntryGateWay( uint32_t * pulIPAddress,
MACAddress_t * const pxMACAddress,
struct xNetworkEndPoint ** ppxEndPoint )
{
eARPLookupResult_t eReturn = eARPCacheMiss;
uint32_t ulAddressToLookup = *( pulIPAddress );
NetworkEndPoint_t * pxEndPoint;
uint32_t ulOriginal = *pulIPAddress;
/* It is assumed that devices with the same netmask are on the same
* LAN and don't need a gateway. */
pxEndPoint = FreeRTOS_FindEndPointOnNetMask( ulAddressToLookup, 4 );
if( pxEndPoint == NULL )
{
/* No matching end-point is found, look for a gateway. */
#if ( ipconfigARP_STORES_REMOTE_ADDRESSES == 1 )
eReturn = prvCacheLookup( ulAddressToLookup, pxMACAddress, ppxEndPoint );
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. */
*( ppxEndPoint ) = FreeRTOS_FindGateWay( ( BaseType_t ) ipTYPE_IPv4 );
if( *( ppxEndPoint ) != NULL )
{
/* 'ipv4_settings' can be accessed safely, because 'ipTYPE_IPv4' was provided. */
ulAddressToLookup = ( *ppxEndPoint )->ipv4_settings.ulGatewayAddress;
}
else
{
ulAddressToLookup = *pulIPAddress;
}
}
}
else
{
/* The IP address is on the local network, so lookup the requested
* IP address directly. */
ulAddressToLookup = *pulIPAddress;
*ppxEndPoint = pxEndPoint;
}
#if ( ipconfigARP_STORES_REMOTE_ADDRESSES == 1 )
if( eReturn == eARPCacheMiss )
#endif
{
if( ulAddressToLookup == 0U )
{
/* The address is not on the local network, and there is not a
* router. */
eReturn = eCantSendPacket;
}
else
{
eReturn = prvCacheLookup( ulAddressToLookup, pxMACAddress, ppxEndPoint );
if( ( eReturn != eARPCacheHit ) || ( ulOriginal != ulAddressToLookup ) )
{
FreeRTOS_debug_printf( ( "ARP %xip %s using %xip\n",
( unsigned ) FreeRTOS_ntohl( ulOriginal ),
( eReturn == eARPCacheHit ) ? "hit" : "miss",
( unsigned ) FreeRTOS_ntohl( ulAddressToLookup ) ) );
}
/* It might be that the ARP has to go to the gateway. */
*pulIPAddress = ulAddressToLookup;
}
}
return eReturn;
}
/*-----------------------------------------------------------*/
/**
* @brief Lookup an IP address in the ARP cache.
*
* @param[in] ulAddressToLookup The 32-bit representation of an IP address to
* lookup.
* @param[out] pxMACAddress A pointer to MACAddress_t variable where, if there
* is an ARP cache hit, the MAC address corresponding to
* the IP address will be stored.
* @param[in,out] ppxEndPoint a pointer to the end-point will be stored.
*
* @return When the IP-address is found: eARPCacheHit, when not found: eARPCacheMiss,
* and when waiting for a ARP reply: eCantSendPacket.
*/
static eARPLookupResult_t prvCacheLookup( uint32_t ulAddressToLookup,
MACAddress_t * const pxMACAddress,
NetworkEndPoint_t ** ppxEndPoint )
{
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( pxMACAddress->ucBytes, xARPCache[ x ].xMACAddress.ucBytes, sizeof( MACAddress_t ) );
/* ppxEndPoint != NULL was tested in the only caller eARPGetCacheEntry(). */
*( ppxEndPoint ) = xARPCache[ x ].pxEndPoint;
eReturn = eARPCacheHit;
}
break;
}
}
return eReturn;
}
/*-----------------------------------------------------------*/
/**
* @brief A call to this function will update (or 'Age') the ARP cache entries.
* The function will also try to prevent a removal of entry by sending
* an ARP query. It will also check whether we are waiting on an ARP
* reply - if we are, then an ARP request will be re-sent.
* In case an ARP entry has 'Aged' to 0, it will be removed from the ARP
* cache.
*/
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 = 0U;
}
}
}
xTimeNow = xTaskGetTickCount();
if( ( xLastGratuitousARPTime == ( TickType_t ) 0 ) || ( ( xTimeNow - xLastGratuitousARPTime ) > ( TickType_t ) arpGRATUITOUS_ARP_PERIOD ) )
{
NetworkEndPoint_t * pxEndPoint = pxNetworkEndPoints;
while( pxEndPoint != NULL )
{
if( ( pxEndPoint->bits.bEndPointUp != pdFALSE_UNSIGNED ) && ( pxEndPoint->ipv4_settings.ulIPAddress != 0U ) )
{
/* Case default is never toggled because IPv6 flag can be TRUE or FALSE */
switch( pxEndPoint->bits.bIPv6 ) /* LCOV_EXCL_BR_LINE */
{
#if ( ipconfigUSE_IPv4 != 0 )
case pdFALSE_UNSIGNED:
FreeRTOS_OutputARPRequest( pxEndPoint->ipv4_settings.ulIPAddress );
break;
#endif /* ( ipconfigUSE_IPv4 != 0 ) */
#if ( ipconfigUSE_IPv6 != 0 )
case pdTRUE_UNSIGNED:
FreeRTOS_OutputAdvertiseIPv6( pxEndPoint );
break;
#endif /* ( ipconfigUSE_IPv6 != 0 ) */
default: /* LCOV_EXCL_LINE */
/* Shouldn't reach here */
/* MISRA 16.4 Compliance */
break; /* LCOV_EXCL_LINE */
}
}
pxEndPoint = pxEndPoint->pxNext;
}
xLastGratuitousARPTime = xTimeNow;
}
}
/*-----------------------------------------------------------*/
/**
* @brief Send a Gratuitous ARP packet to allow this node to announce the IP-MAC
* mapping to the entire network.
*/
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 );
}
/*-----------------------------------------------------------*/
/**
* @brief Create and send an ARP request packet.
*
* @param[in] ulIPAddress A 32-bit representation of the IP-address whose
* physical (MAC) address is required.
*/
void FreeRTOS_OutputARPRequest( uint32_t ulIPAddress )
{
NetworkBufferDescriptor_t * pxNetworkBuffer;
NetworkEndPoint_t * pxEndPoint;
/* Send an ARP request to every end-point which has the type IPv4,
* and which already has an IP-address assigned. */
for( pxEndPoint = FreeRTOS_FirstEndPoint( NULL );
pxEndPoint != NULL;
pxEndPoint = FreeRTOS_NextEndPoint( NULL, pxEndPoint ) )
{
if( ( pxEndPoint->bits.bIPv6 == pdFALSE_UNSIGNED ) &&
( pxEndPoint->ipv4_settings.ulIPAddress != 0U ) )
{
/* 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 ) 0U );
if( pxNetworkBuffer != NULL )
{
pxNetworkBuffer->xIPAddress.ulIP_IPv4 = ulIPAddress;
pxNetworkBuffer->pxEndPoint = pxEndPoint;
pxNetworkBuffer->pxInterface = pxEndPoint->pxNetworkInterface;
vARPGenerateRequestPacket( pxNetworkBuffer );
#if ( ipconfigETHERNET_MINIMUM_PACKET_BYTES > 0 )
{
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( ipconfigETHERNET_MINIMUM_PACKET_BYTES > 0 ) */
if( xIsCallingFromIPTask() != pdFALSE )
{
iptraceNETWORK_INTERFACE_OUTPUT( pxNetworkBuffer->xDataLength, pxNetworkBuffer->pucEthernetBuffer );
/* Only the IP-task is allowed to call this function directly. */
if( pxEndPoint->pxNetworkInterface != NULL )
{
( void ) pxEndPoint->pxNetworkInterface->pfOutput( pxEndPoint->pxNetworkInterface, pxNetworkBuffer, pdTRUE );
}
}
else
{
IPStackEvent_t xSendEvent;
/* Send a message to the IP-task to send this ARP packet. */
xSendEvent.eEventType = eNetworkTxEvent;
xSendEvent.pvData = pxNetworkBuffer;
if( xSendEventStructToIPTask( &xSendEvent, ( TickType_t ) portMAX_DELAY ) == pdFAIL )
{
/* Failed to send the message, so release the network buffer. */
vReleaseNetworkBufferAndDescriptor( pxNetworkBuffer );
}
}
}
}
}
}
/*-----------------------------------------------------------*/
/**
* @brief Wait for address resolution: look-up the IP-address in the ARP-cache, and if
* needed send an ARP request, and wait for a reply. This function is useful when
* called before FreeRTOS_sendto().
*
* @param[in] ulIPAddress The IP-address to look-up.
* @param[in] uxTicksToWait The maximum number of clock ticks to wait for a reply.
*
* @return Zero when successful.
*/
BaseType_t xARPWaitResolution( uint32_t ulIPAddress,
TickType_t uxTicksToWait )
{
BaseType_t xResult = -pdFREERTOS_ERRNO_EADDRNOTAVAIL;
TimeOut_t xTimeOut;
MACAddress_t xMACAddress;
eARPLookupResult_t xLookupResult;
NetworkEndPoint_t * pxEndPoint;
size_t uxSendCount = ipconfigMAX_ARP_RETRANSMISSIONS;
uint32_t ulIPAddressCopy = ulIPAddress;
/* The IP-task is not supposed to call this function. */
configASSERT( xIsCallingFromIPTask() == pdFALSE );
xLookupResult = eARPGetCacheEntry( &( ulIPAddressCopy ), &( xMACAddress ), &( pxEndPoint ) );
if( xLookupResult == eARPCacheMiss )
{
const TickType_t uxSleepTime = pdMS_TO_TICKS( 250U );
/* We might use ipconfigMAX_ARP_RETRANSMISSIONS here. */
vTaskSetTimeOutState( &xTimeOut );
while( uxSendCount > 0U )
{
FreeRTOS_OutputARPRequest( ulIPAddressCopy );
vTaskDelay( uxSleepTime );
xLookupResult = eARPGetCacheEntry( &( ulIPAddressCopy ), &( xMACAddress ), &( pxEndPoint ) );
if( ( xTaskCheckForTimeOut( &( xTimeOut ), &( uxTicksToWait ) ) == pdTRUE ) ||
( xLookupResult != eARPCacheMiss ) )
{
break;
}
/* Decrement the count. */
uxSendCount--;
}
}
if( xLookupResult == eARPCacheHit )
{
xResult = 0;
}
return xResult;
}
/*-----------------------------------------------------------*/
/**
* @brief Generate an ARP request packet by copying various constant details to
* the buffer.
*
* @param[in,out] pxNetworkBuffer Pointer to the buffer which has to be filled with
* the ARP request packet details.
*/
void vARPGenerateRequestPacket( NetworkBufferDescriptor_t * const pxNetworkBuffer )
{
/* 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. */
};
ARPPacket_t * pxARPPacket;
/* memcpy() helper variables for MISRA Rule 21.15 compliance*/
const void * pvCopySource;
void * pvCopyDest;
/* 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 ) );
configASSERT( pxNetworkBuffer->pxEndPoint != NULL );
/* MISRA Ref 11.3.1 [Misaligned access] */
/* More details at: https://github.com/FreeRTOS/FreeRTOS-Plus-TCP/blob/main/MISRA.md#rule-113 */
/* coverity[misra_c_2012_rule_11_3_violation] */
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;
*/
/*
* Use helper variables for memcpy() to remain
* compliant with MISRA Rule 21.15. These should be
* optimized away.
*/
pvCopySource = xDefaultPartARPPacketHeader;
pvCopyDest = pxARPPacket;
( void ) memcpy( pvCopyDest, pvCopySource, sizeof( xDefaultPartARPPacketHeader ) );
pvCopySource = pxNetworkBuffer->pxEndPoint->xMACAddress.ucBytes;
pvCopyDest = pxARPPacket->xEthernetHeader.xSourceAddress.ucBytes;
( void ) memcpy( pvCopyDest, pvCopySource, ipMAC_ADDRESS_LENGTH_BYTES );
pvCopySource = pxNetworkBuffer->pxEndPoint->xMACAddress.ucBytes;
pvCopyDest = pxARPPacket->xARPHeader.xSenderHardwareAddress.ucBytes;
( void ) memcpy( pvCopyDest, pvCopySource, ipMAC_ADDRESS_LENGTH_BYTES );
pvCopySource = &( pxNetworkBuffer->pxEndPoint->ipv4_settings.ulIPAddress );
pvCopyDest = pxARPPacket->xARPHeader.ucSenderProtocolAddress;
( void ) memcpy( pvCopyDest, pvCopySource, sizeof( pxARPPacket->xARPHeader.ucSenderProtocolAddress ) );
pxARPPacket->xARPHeader.ulTargetProtocolAddress = pxNetworkBuffer->xIPAddress.ulIP_IPv4;
pxNetworkBuffer->xDataLength = sizeof( ARPPacket_t );
iptraceCREATING_ARP_REQUEST( pxNetworkBuffer->xIPAddress.ulIP_IPv4 );
}
/*-----------------------------------------------------------*/
#endif /* ( ipconfigUSE_IPv4 != 0 ) */
/**
* @brief A call to this function will clear the ARP cache.
* @param[in] pxEndPoint only clean entries with this end-point, or when NULL,
* clear the entire ARP cache.
*/
void FreeRTOS_ClearARP( const struct xNetworkEndPoint * pxEndPoint )
{
if( pxEndPoint != NULL )
{
BaseType_t x;
for( x = 0; x < ipconfigARP_CACHE_ENTRIES; x++ )
{
if( xARPCache[ x ].pxEndPoint == pxEndPoint )
{
( void ) memset( &( xARPCache[ x ] ), 0, sizeof( ARPCacheRow_t ) );
}
}
}
else
{
( void ) memset( xARPCache, 0, sizeof( xARPCache ) );
}
}
/*-----------------------------------------------------------*/
#if ( ipconfigHAS_PRINTF != 0 ) || ( ipconfigHAS_DEBUG_PRINTF != 0 )
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 != 0U ) && ( xARPCache[ x ].ucAge > ( uint8_t ) 0U ) )
{
/* See if the MAC-address also matches, and we're all happy */
FreeRTOS_printf( ( "ARP %2d: %3u - %16xip : %02x:%02x:%02x : %02x:%02x:%02x\n",
( int ) x,
xARPCache[ x ].ucAge,
( unsigned ) FreeRTOS_ntohl( 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 ) */