mirror of
https://github.com/FreeRTOS/FreeRTOS-Plus-TCP
synced 2025-10-20 22:10:04 +08:00

* Updating tcp utilities * Some more change in dev_integration_hein.v8 * In FreeRTOS_DNS_Parser.c : use 'ipUDP_PAYLOAD_OFFSET_IPv4' in stead of 'ipIP_PAYLOAD_OFFSET' * And a few more corrections * Changes to WinPCap network interface, removed debugging code * After applying uncrustify * Oops, I forgot the push changes in include files. * Now removing it, hopefully --------- Co-authored-by: Nikhil Kamath <110539926+amazonKamath@users.noreply.github.com> Co-authored-by: Monika Singh <108652024+moninom1@users.noreply.github.com>
1266 lines
56 KiB
C
1266 lines
56 KiB
C
/*
|
|
* FreeRTOS+TCP V2.3.1
|
|
* Copyright (C) 2020 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
|
|
*/
|
|
|
|
/**
|
|
* @file FreeRTOS_ND.c
|
|
* @brief Implements a few functions that handle Neighbour Discovery and other ICMPv6 messages.
|
|
*/
|
|
|
|
/* Standard includes. */
|
|
#include <stdint.h>
|
|
#include <stdio.h>
|
|
|
|
|
|
/* FreeRTOS includes. */
|
|
#include "FreeRTOS.h"
|
|
#include "task.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_Routing.h"
|
|
#include "FreeRTOS_ND.h"
|
|
#include "FreeRTOS_IP_Timers.h"
|
|
|
|
#if ( ipconfigUSE_LLMNR == 1 )
|
|
#include "FreeRTOS_DNS.h"
|
|
#endif /* ipconfigUSE_LLMNR */
|
|
#include "NetworkBufferManagement.h"
|
|
|
|
/* The entire module FreeRTOS_ND.c is skipped when IPv6 is not used. */
|
|
#if ( ipconfigUSE_IPv6 != 0 )
|
|
|
|
/** @brief Type of Neighbour Advertisement packets. */
|
|
#define ndICMPv6_FLAG_SOLICITED 0x40000000U
|
|
#define ndICMPv6_FLAG_UPDATE 0x20000000U
|
|
|
|
/** @brief A block time of 0 simply means "don't block". */
|
|
#define ndDONT_BLOCK ( ( TickType_t ) 0 )
|
|
|
|
/** @brief The character used to fill ICMP echo requests, and therefore also the
|
|
* character expected to fill ICMP echo replies.
|
|
*/
|
|
#define ndECHO_DATA_FILL_BYTE 'x'
|
|
|
|
/** @brief When ucAge becomes 3 or less, it is time for a new
|
|
* neighbour solicitation.
|
|
*/
|
|
#define ndMAX_CACHE_AGE_BEFORE_NEW_ND_SOLICITATION ( 3U )
|
|
|
|
/** @brief All nodes on the local network segment: IP- and MAC-address. */
|
|
/* MISRA Ref 8.9.1 [File scoped variables] */
|
|
/* More details at: https://github.com/FreeRTOS/FreeRTOS-Plus-TCP/blob/main/MISRA.md#rule-89 */
|
|
/* coverity[misra_c_2012_rule_8_9_violation] */
|
|
static const uint8_t pcLOCAL_ALL_NODES_MULTICAST_IP[ ipSIZE_OF_IPv6_ADDRESS ] = { 0xff, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01 }; /* ff02:1 */
|
|
static const uint8_t pcLOCAL_ALL_NODES_MULTICAST_MAC[ ipMAC_ADDRESS_LENGTH_BYTES ] = { 0x33, 0x33, 0x00, 0x00, 0x00, 0x01 };
|
|
|
|
/** @brief See if the MAC-address can be resolved because it is a multi-cast address. */
|
|
static eARPLookupResult_t prvMACResolve( const IPv6_Address_t * pxAddressToLookup,
|
|
MACAddress_t * const pxMACAddress,
|
|
NetworkEndPoint_t ** ppxEndPoint );
|
|
|
|
/** @brief Lookup an MAC address in the ND cache from the IP address. */
|
|
static eARPLookupResult_t prvNDCacheLookup( const IPv6_Address_t * pxAddressToLookup,
|
|
MACAddress_t * const pxMACAddress,
|
|
NetworkEndPoint_t ** ppxEndPoint );
|
|
|
|
#if ( ipconfigHAS_PRINTF == 1 )
|
|
static const char * pcMessageType( BaseType_t xType );
|
|
#endif
|
|
|
|
/** @brief Find the first end-point of type IPv6. */
|
|
static NetworkEndPoint_t * pxFindLocalEndpoint( void );
|
|
|
|
/** @brief The ND cache. */
|
|
static NDCacheRow_t xNDCache[ ipconfigND_CACHE_ENTRIES ];
|
|
|
|
/*-----------------------------------------------------------*/
|
|
|
|
/*
|
|
* ff02::1: All IPv6 devices
|
|
* ff02::2: All IPv6 routers
|
|
* ff02::5: All OSPFv3 routers
|
|
* ff02::a: All EIGRP (IPv6) routers
|
|
*/
|
|
|
|
/**
|
|
* @brief Find the first end-point of type IPv6.
|
|
*
|
|
* @return The first IPv6 end-point found.
|
|
*/
|
|
static NetworkEndPoint_t * pxFindLocalEndpoint( void )
|
|
{
|
|
NetworkEndPoint_t * pxEndPoint;
|
|
|
|
for( pxEndPoint = FreeRTOS_FirstEndPoint( NULL );
|
|
pxEndPoint != NULL;
|
|
pxEndPoint = FreeRTOS_NextEndPoint( NULL, pxEndPoint ) )
|
|
{
|
|
if( pxEndPoint->bits.bIPv6 == pdTRUE_UNSIGNED )
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
|
|
return pxEndPoint;
|
|
}
|
|
|
|
/**
|
|
* @brief See if the MAC-address can be resolved because it is a multi-cast address.
|
|
*
|
|
* @param[in] pxAddressToLookup: The IP-address to look-up.
|
|
* @param[out] pxMACAddress: The resulting MAC-address is stored here.
|
|
* @param[out] ppxEndPoint: A pointer to an end-point pointer where the end-point will be stored.
|
|
*
|
|
* @return An enum, either eARPCacheHit or eARPCacheMiss.
|
|
*/
|
|
static eARPLookupResult_t prvMACResolve( const IPv6_Address_t * pxAddressToLookup,
|
|
MACAddress_t * const pxMACAddress,
|
|
NetworkEndPoint_t ** ppxEndPoint )
|
|
{
|
|
eARPLookupResult_t eReturn;
|
|
|
|
/* Mostly used multi-cast address is ff02::. */
|
|
if( xIsIPv6Multicast( pxAddressToLookup ) != pdFALSE )
|
|
{
|
|
vSetMultiCastIPv6MacAddress( pxAddressToLookup, pxMACAddress );
|
|
|
|
if( ppxEndPoint != NULL )
|
|
{
|
|
*ppxEndPoint = pxFindLocalEndpoint();
|
|
}
|
|
|
|
eReturn = eARPCacheHit;
|
|
}
|
|
else
|
|
{
|
|
/* Not a multicast IP address. */
|
|
eReturn = eARPCacheMiss;
|
|
}
|
|
|
|
return eReturn;
|
|
}
|
|
/*-----------------------------------------------------------*/
|
|
|
|
/**
|
|
* @brief Find the MAC-address of an IPv6 address. It will first determine if is a multicast
|
|
* address, if not, it will check the ND cache.
|
|
*
|
|
* @param[in] pxIPAddress: The IPv6 address to be looked up.
|
|
* @param[out] pxMACAddress: The MAC-address found.
|
|
* @param[out] ppxEndPoint: A pointer to a pointer to an end-point, where the end-point will be stored.
|
|
*
|
|
* @return An enum which says whether the address was found: eARPCacheHit or eARPCacheMiss.
|
|
*/
|
|
eARPLookupResult_t eNDGetCacheEntry( IPv6_Address_t * pxIPAddress,
|
|
MACAddress_t * const pxMACAddress,
|
|
struct xNetworkEndPoint ** ppxEndPoint )
|
|
{
|
|
eARPLookupResult_t eReturn;
|
|
NetworkEndPoint_t * pxEndPoint;
|
|
|
|
/* Multi-cast addresses can be resolved immediately. */
|
|
eReturn = prvMACResolve( pxIPAddress, pxMACAddress, ppxEndPoint );
|
|
|
|
if( eReturn == eARPCacheMiss )
|
|
{
|
|
/* See if the IP-address has an entry in the cache. */
|
|
eReturn = prvNDCacheLookup( pxIPAddress, pxMACAddress, ppxEndPoint );
|
|
}
|
|
|
|
if( eReturn == eARPCacheMiss )
|
|
{
|
|
FreeRTOS_printf( ( "eNDGetCacheEntry: lookup %pip miss\n", pxIPAddress->ucBytes ) );
|
|
}
|
|
|
|
if( eReturn == eARPCacheMiss )
|
|
{
|
|
IPv6_Type_t eIPType = xIPv6_GetIPType( pxIPAddress );
|
|
|
|
pxEndPoint = FreeRTOS_FindEndPointOnIP_IPv6( pxIPAddress );
|
|
|
|
if( pxEndPoint != NULL )
|
|
{
|
|
if( ppxEndPoint != NULL )
|
|
{
|
|
*( ppxEndPoint ) = pxEndPoint;
|
|
}
|
|
|
|
FreeRTOS_printf( ( "eNDGetCacheEntry: FindEndPointOnIP failed for %pip (endpoint %pip)\n",
|
|
pxIPAddress->ucBytes,
|
|
pxEndPoint->ipv6_settings.xIPAddress.ucBytes ) );
|
|
}
|
|
else
|
|
{
|
|
if( eIPType == eIPv6_LinkLocal )
|
|
{
|
|
for( pxEndPoint = FreeRTOS_FirstEndPoint( NULL );
|
|
pxEndPoint != NULL;
|
|
pxEndPoint = FreeRTOS_NextEndPoint( NULL, pxEndPoint ) )
|
|
{
|
|
IPv6_Type_t eMyType = xIPv6_GetIPType( &( pxEndPoint->ipv6_settings.xIPAddress ) );
|
|
|
|
if( eMyType == eIPType )
|
|
{
|
|
eReturn = prvNDCacheLookup( pxIPAddress, pxMACAddress, ppxEndPoint );
|
|
break;
|
|
}
|
|
}
|
|
|
|
FreeRTOS_printf( ( "eNDGetCacheEntry: LinkLocal %pip \"%s\"\n", pxIPAddress->ucBytes,
|
|
( eReturn == eARPCacheHit ) ? "hit" : "miss" ) );
|
|
}
|
|
else
|
|
{
|
|
pxEndPoint = FreeRTOS_FindGateWay( ( BaseType_t ) ipTYPE_IPv6 );
|
|
|
|
if( pxEndPoint != NULL )
|
|
{
|
|
( void ) memcpy( pxIPAddress->ucBytes, pxEndPoint->ipv6_settings.xGatewayAddress.ucBytes, ipSIZE_OF_IPv6_ADDRESS );
|
|
FreeRTOS_printf( ( "eNDGetCacheEntry: Using gw %pip\n", pxIPAddress->ucBytes ) );
|
|
FreeRTOS_printf( ( "eNDGetCacheEntry: From addr %pip\n", pxEndPoint->ipv6_settings.xIPAddress.ucBytes ) );
|
|
|
|
/* See if the gateway has an entry in the cache. */
|
|
eReturn = prvNDCacheLookup( pxIPAddress, pxMACAddress, ppxEndPoint );
|
|
|
|
if( *ppxEndPoint != NULL )
|
|
{
|
|
FreeRTOS_printf( ( "eNDGetCacheEntry: found end-point %pip\n", ( *ppxEndPoint )->ipv6_settings.xIPAddress.ucBytes ) );
|
|
}
|
|
|
|
*( ppxEndPoint ) = pxEndPoint;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return eReturn;
|
|
}
|
|
/*-----------------------------------------------------------*/
|
|
|
|
/**
|
|
* @brief Store a combination of IP-address, MAC-address and an end-point in a free location
|
|
* in the ND cache.
|
|
*
|
|
* @param[in] pxMACAddress: The MAC-address
|
|
* @param[in] pxIPAddress: The IP-address
|
|
* @param[in] pxEndPoint: The end-point through which the IP-address can be reached.
|
|
*
|
|
*/
|
|
void vNDRefreshCacheEntry( const MACAddress_t * pxMACAddress,
|
|
const IPv6_Address_t * pxIPAddress,
|
|
NetworkEndPoint_t * pxEndPoint )
|
|
{
|
|
BaseType_t x;
|
|
BaseType_t xFreeEntry = -1, xEntryFound = -1;
|
|
|
|
/* For each entry in the ND cache table. */
|
|
for( x = 0; x < ipconfigND_CACHE_ENTRIES; x++ )
|
|
{
|
|
if( xNDCache[ x ].ucValid == ( uint8_t ) pdFALSE )
|
|
{
|
|
if( xFreeEntry == -1 )
|
|
{
|
|
xFreeEntry = x;
|
|
}
|
|
}
|
|
else if( memcmp( xNDCache[ x ].xIPAddress.ucBytes, pxIPAddress->ucBytes, ipSIZE_OF_IPv6_ADDRESS ) == 0 )
|
|
{
|
|
xEntryFound = x;
|
|
break;
|
|
}
|
|
else
|
|
{
|
|
/* Entry is valid but the IP-address doesn't match. */
|
|
}
|
|
}
|
|
|
|
if( xEntryFound < 0 )
|
|
{
|
|
/* The IP-address was not found, use the first free location. */
|
|
xEntryFound = xFreeEntry;
|
|
}
|
|
|
|
if( xEntryFound >= 0 )
|
|
{
|
|
/* Copy the IP-address. */
|
|
( void ) memcpy( xNDCache[ xEntryFound ].xIPAddress.ucBytes, pxIPAddress->ucBytes, ipSIZE_OF_IPv6_ADDRESS );
|
|
/* Copy the MAC-address. */
|
|
( void ) memcpy( xNDCache[ xEntryFound ].xMACAddress.ucBytes, pxMACAddress->ucBytes, sizeof( MACAddress_t ) );
|
|
xNDCache[ xEntryFound ].pxEndPoint = pxEndPoint;
|
|
xNDCache[ xEntryFound ].ucAge = ( uint8_t ) ipconfigMAX_ARP_AGE;
|
|
xNDCache[ xEntryFound ].ucValid = ( uint8_t ) pdTRUE;
|
|
}
|
|
else
|
|
{
|
|
FreeRTOS_printf( ( "vNDRefreshCacheEntry: %pip not found\n", pxIPAddress->ucBytes ) );
|
|
}
|
|
}
|
|
/*-----------------------------------------------------------*/
|
|
|
|
/**
|
|
* @brief Reduce the age counter in each entry within the ND cache. An entry is no
|
|
* longer considered valid and is deleted if its age reaches zero.
|
|
* Just before getting to zero, 3 times a neighbour solicitation will be sent.
|
|
*/
|
|
void vNDAgeCache( void )
|
|
{
|
|
BaseType_t x;
|
|
|
|
/* Loop through each entry in the ND cache. */
|
|
for( x = 0; x < ipconfigND_CACHE_ENTRIES; x++ )
|
|
{
|
|
BaseType_t xDoSolicitate = pdFALSE;
|
|
|
|
/* If the entry is valid (its age is greater than zero). */
|
|
if( xNDCache[ x ].ucAge > 0U )
|
|
{
|
|
/* Decrement the age value of the entry in this ND cache table row.
|
|
* When the age reaches zero it is no longer considered valid. */
|
|
( xNDCache[ x ].ucAge )--;
|
|
|
|
if( xNDCache[ x ].ucAge == 0U )
|
|
{
|
|
/* The entry is no longer valid. Wipe it out. */
|
|
iptraceND_TABLE_ENTRY_EXPIRED( xNDCache[ x ].xIPAddress );
|
|
( void ) memset( &( xNDCache[ x ] ), 0, sizeof( xNDCache[ x ] ) );
|
|
}
|
|
else
|
|
{
|
|
/* If the entry is not yet valid, then it is waiting an ND
|
|
* advertisement, and the ND solicitation should be retransmitted. */
|
|
if( xNDCache[ x ].ucValid == ( uint8_t ) pdFALSE )
|
|
{
|
|
xDoSolicitate = pdTRUE;
|
|
}
|
|
else if( xNDCache[ x ].ucAge <= ( uint8_t ) ndMAX_CACHE_AGE_BEFORE_NEW_ND_SOLICITATION )
|
|
{
|
|
/* This entry will get removed soon. See if the MAC address is
|
|
* still valid to prevent this happening. */
|
|
iptraceND_TABLE_ENTRY_WILL_EXPIRE( xNDCache[ x ].xIPAddress );
|
|
xDoSolicitate = pdTRUE;
|
|
}
|
|
else
|
|
{
|
|
/* The age has just ticked down, with nothing to do. */
|
|
}
|
|
|
|
if( xDoSolicitate != pdFALSE )
|
|
{
|
|
size_t uxNeededSize;
|
|
NetworkBufferDescriptor_t * pxNetworkBuffer;
|
|
|
|
uxNeededSize = ipSIZE_OF_ETH_HEADER + ipSIZE_OF_IPv6_HEADER + sizeof( ICMPHeader_IPv6_t );
|
|
pxNetworkBuffer = pxGetNetworkBufferWithDescriptor( uxNeededSize, 0U );
|
|
|
|
if( pxNetworkBuffer != NULL )
|
|
{
|
|
pxNetworkBuffer->pxEndPoint = xNDCache[ x ].pxEndPoint;
|
|
/* _HT_ From here I am suspecting a network buffer leak */
|
|
vNDSendNeighbourSolicitation( pxNetworkBuffer, &( xNDCache[ x ].xIPAddress ) );
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
/*-----------------------------------------------------------*/
|
|
|
|
/**
|
|
* @brief Clear the Neighbour Discovery cache.
|
|
*/
|
|
void FreeRTOS_ClearND( void )
|
|
{
|
|
( void ) memset( xNDCache, 0, sizeof( xNDCache ) );
|
|
}
|
|
/*-----------------------------------------------------------*/
|
|
|
|
/**
|
|
* @brief Look-up an IPv6 address in the cache.
|
|
*
|
|
* @param[in] pxAddressToLookup: The IPv6 address to look-up.Ethernet packet.
|
|
* @param[out] pxMACAddress: The resulting MAC-address will be stored here.
|
|
* @param[out] ppxEndPoint: A pointer to a pointer to an end-point, where the end-point will be stored.
|
|
*
|
|
* @return An enum: either eARPCacheHit or eARPCacheMiss.
|
|
*/
|
|
static eARPLookupResult_t prvNDCacheLookup( const IPv6_Address_t * pxAddressToLookup,
|
|
MACAddress_t * const pxMACAddress,
|
|
NetworkEndPoint_t ** ppxEndPoint )
|
|
{
|
|
BaseType_t x;
|
|
eARPLookupResult_t eReturn = eARPCacheMiss;
|
|
|
|
/* For each entry in the ND cache table. */
|
|
for( x = 0; x < ipconfigND_CACHE_ENTRIES; x++ )
|
|
{
|
|
if( xNDCache[ x ].ucValid == ( uint8_t ) pdFALSE )
|
|
{
|
|
/* Skip invalid entries. */
|
|
}
|
|
else if( memcmp( xNDCache[ x ].xIPAddress.ucBytes, pxAddressToLookup->ucBytes, ipSIZE_OF_IPv6_ADDRESS ) == 0 )
|
|
{
|
|
( void ) memcpy( pxMACAddress->ucBytes, xNDCache[ x ].xMACAddress.ucBytes, sizeof( MACAddress_t ) );
|
|
eReturn = eARPCacheHit;
|
|
|
|
if( ppxEndPoint != NULL )
|
|
{
|
|
*ppxEndPoint = xNDCache[ x ].pxEndPoint;
|
|
}
|
|
|
|
FreeRTOS_debug_printf( ( "prvCacheLookup6[ %d ] %pip with %02x:%02x:%02x:%02x:%02x:%02x\n",
|
|
( int ) x,
|
|
pxAddressToLookup->ucBytes,
|
|
pxMACAddress->ucBytes[ 0 ],
|
|
pxMACAddress->ucBytes[ 1 ],
|
|
pxMACAddress->ucBytes[ 2 ],
|
|
pxMACAddress->ucBytes[ 3 ],
|
|
pxMACAddress->ucBytes[ 4 ],
|
|
pxMACAddress->ucBytes[ 5 ] ) );
|
|
break;
|
|
}
|
|
else
|
|
{
|
|
/* Entry is valid but the MAC-address doesn't match. */
|
|
}
|
|
}
|
|
|
|
if( eReturn == eARPCacheMiss )
|
|
{
|
|
FreeRTOS_printf( ( "prvNDCacheLookup %pip Miss\n", pxAddressToLookup->ucBytes ) );
|
|
|
|
if( ppxEndPoint != NULL )
|
|
{
|
|
*ppxEndPoint = NULL;
|
|
}
|
|
}
|
|
|
|
return eReturn;
|
|
}
|
|
/*-----------------------------------------------------------*/
|
|
|
|
#if ( ( ipconfigHAS_PRINTF != 0 ) || ( ipconfigHAS_DEBUG_PRINTF != 0 ) )
|
|
|
|
/**
|
|
* @brief Print the contents of the ND cache, for debugging only.
|
|
*/
|
|
void FreeRTOS_PrintNDCache( void )
|
|
{
|
|
BaseType_t x, xCount = 0;
|
|
|
|
/* Loop through each entry in the ND cache. */
|
|
for( x = 0; x < ipconfigARP_CACHE_ENTRIES; x++ )
|
|
{
|
|
if( xNDCache[ x ].ucValid != ( uint8_t ) 0U )
|
|
{
|
|
/* See if the MAC-address also matches, and we're all happy */
|
|
char pcBuffer[ 40 ];
|
|
|
|
FreeRTOS_printf( ( "ND %2d: age %3u - %pip MAC %02x-%02x-%02x-%02x-%02x-%02x endPoint %s\n",
|
|
( int ) x,
|
|
xNDCache[ x ].ucAge,
|
|
xNDCache[ x ].xIPAddress.ucBytes,
|
|
xNDCache[ x ].xMACAddress.ucBytes[ 0 ],
|
|
xNDCache[ x ].xMACAddress.ucBytes[ 1 ],
|
|
xNDCache[ x ].xMACAddress.ucBytes[ 2 ],
|
|
xNDCache[ x ].xMACAddress.ucBytes[ 3 ],
|
|
xNDCache[ x ].xMACAddress.ucBytes[ 4 ],
|
|
xNDCache[ x ].xMACAddress.ucBytes[ 5 ],
|
|
pcEndpointName( xNDCache[ x ].pxEndPoint, pcBuffer, sizeof( pcBuffer ) ) ) );
|
|
xCount++;
|
|
}
|
|
}
|
|
|
|
FreeRTOS_printf( ( "Arp has %ld entries\n", xCount ) );
|
|
}
|
|
|
|
#endif /* ( ipconfigHAS_PRINTF != 0 ) || ( ipconfigHAS_DEBUG_PRINTF != 0 ) */
|
|
/*-----------------------------------------------------------*/
|
|
|
|
/**
|
|
* @brief Return an ICMPv6 packet to the peer.
|
|
*
|
|
* @param[in] pxNetworkBuffer: The Ethernet packet.
|
|
* @param[in] uxICMPSize: The number of bytes to be sent.
|
|
*/
|
|
static void prvReturnICMP_IPv6( NetworkBufferDescriptor_t * const pxNetworkBuffer,
|
|
size_t uxICMPSize )
|
|
{
|
|
const NetworkEndPoint_t * pxEndPoint = pxNetworkBuffer->pxEndPoint;
|
|
|
|
/* 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] */
|
|
ICMPPacket_IPv6_t * pxICMPPacket = ( ( ICMPPacket_IPv6_t * ) pxNetworkBuffer->pucEthernetBuffer );
|
|
|
|
configASSERT( pxEndPoint != NULL );
|
|
configASSERT( pxEndPoint->bits.bIPv6 != pdFALSE_UNSIGNED );
|
|
|
|
( void ) memcpy( pxICMPPacket->xIPHeader.xDestinationAddress.ucBytes, pxICMPPacket->xIPHeader.xSourceAddress.ucBytes, ipSIZE_OF_IPv6_ADDRESS );
|
|
( void ) memcpy( pxICMPPacket->xIPHeader.xSourceAddress.ucBytes, pxEndPoint->ipv6_settings.xIPAddress.ucBytes, ipSIZE_OF_IPv6_ADDRESS );
|
|
pxICMPPacket->xIPHeader.usPayloadLength = FreeRTOS_htons( uxICMPSize );
|
|
|
|
/* Important: tell NIC driver how many bytes must be sent */
|
|
pxNetworkBuffer->xDataLength = ( size_t ) ( ipSIZE_OF_ETH_HEADER + ipSIZE_OF_IPv6_HEADER + uxICMPSize );
|
|
|
|
#if ( ipconfigDRIVER_INCLUDED_TX_IP_CHECKSUM == 0 )
|
|
{
|
|
/* calculate the UDP checksum for outgoing package */
|
|
( void ) usGenerateProtocolChecksum( pxNetworkBuffer->pucEthernetBuffer, pxNetworkBuffer->xDataLength, pdTRUE );
|
|
}
|
|
#else
|
|
{
|
|
/* Many EMAC peripherals will only calculate the ICMP checksum
|
|
* correctly if the field is nulled beforehand. */
|
|
pxICMPPacket->xICMPHeaderIPv6.usChecksum = 0;
|
|
}
|
|
#endif
|
|
|
|
/* This function will fill in the Ethernet addresses and send the packet */
|
|
vReturnEthernetFrame( pxNetworkBuffer, pdFALSE );
|
|
}
|
|
/*-----------------------------------------------------------*/
|
|
|
|
/**
|
|
* @brief Send out an ND request for the IPv6 address contained in pxNetworkBuffer, and
|
|
* add an entry into the ND table that indicates that an ND reply is outstanding
|
|
* so re-transmissions can be generated.
|
|
*
|
|
* @param[in] pxNetworkBuffer: The network buffer in which the message shall be stored.
|
|
* @param[in] pxIPAddress: The IPv6 address that is asked to send a Neighbour Advertisement.
|
|
*
|
|
* @note Send out an ND request for the IPv6 address contained in pxNetworkBuffer, and
|
|
* add an entry into the ND table that indicates that an ND reply is
|
|
* outstanding so re-transmissions can be generated.
|
|
*/
|
|
|
|
void vNDSendNeighbourSolicitation( NetworkBufferDescriptor_t * const pxNetworkBuffer,
|
|
const IPv6_Address_t * pxIPAddress )
|
|
{
|
|
ICMPPacket_IPv6_t * pxICMPPacket;
|
|
ICMPHeader_IPv6_t * pxICMPHeader_IPv6;
|
|
const NetworkEndPoint_t * pxEndPoint = pxNetworkBuffer->pxEndPoint;
|
|
size_t uxNeededSize;
|
|
IPv6_Address_t xTargetIPAddress;
|
|
MACAddress_t xMultiCastMacAddress;
|
|
NetworkBufferDescriptor_t * pxDescriptor = pxNetworkBuffer;
|
|
|
|
configASSERT( pxEndPoint != NULL );
|
|
configASSERT( pxEndPoint->bits.bIPv6 != pdFALSE_UNSIGNED );
|
|
|
|
uxNeededSize = ipSIZE_OF_ETH_HEADER + ipSIZE_OF_IPv6_HEADER + sizeof( ICMPHeader_IPv6_t );
|
|
|
|
if( pxDescriptor->xDataLength < uxNeededSize )
|
|
{
|
|
pxDescriptor = pxDuplicateNetworkBufferWithDescriptor( pxDescriptor, uxNeededSize );
|
|
}
|
|
|
|
if( pxDescriptor != NULL )
|
|
{
|
|
const uint32_t ulPayloadLength = 32U;
|
|
|
|
/* 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] */
|
|
pxICMPPacket = ( ( ICMPPacket_IPv6_t * ) pxDescriptor->pucEthernetBuffer );
|
|
pxICMPHeader_IPv6 = ( ( ICMPHeader_IPv6_t * ) &( pxICMPPacket->xICMPHeaderIPv6 ) );
|
|
|
|
pxDescriptor->xDataLength = uxNeededSize;
|
|
|
|
/* Set the multi-cast MAC-address. */
|
|
xMultiCastMacAddress.ucBytes[ 0 ] = 0x33U;
|
|
xMultiCastMacAddress.ucBytes[ 1 ] = 0x33U;
|
|
xMultiCastMacAddress.ucBytes[ 2 ] = 0xffU;
|
|
xMultiCastMacAddress.ucBytes[ 3 ] = pxIPAddress->ucBytes[ 13 ];
|
|
xMultiCastMacAddress.ucBytes[ 4 ] = pxIPAddress->ucBytes[ 14 ];
|
|
xMultiCastMacAddress.ucBytes[ 5 ] = pxIPAddress->ucBytes[ 15 ];
|
|
|
|
/* Set Ethernet header. Source and Destination will be swapped. */
|
|
( void ) memcpy( pxICMPPacket->xEthernetHeader.xSourceAddress.ucBytes, xMultiCastMacAddress.ucBytes, ipMAC_ADDRESS_LENGTH_BYTES );
|
|
( void ) memcpy( pxICMPPacket->xEthernetHeader.xDestinationAddress.ucBytes, pxEndPoint->xMACAddress.ucBytes, ipMAC_ADDRESS_LENGTH_BYTES );
|
|
pxICMPPacket->xEthernetHeader.usFrameType = ipIPv6_FRAME_TYPE;
|
|
|
|
/* Set IP-header. */
|
|
pxICMPPacket->xIPHeader.ucVersionTrafficClass = 0x60U;
|
|
pxICMPPacket->xIPHeader.ucTrafficClassFlow = 0U;
|
|
pxICMPPacket->xIPHeader.usFlowLabel = 0U;
|
|
pxICMPPacket->xIPHeader.usPayloadLength = FreeRTOS_htons( ulPayloadLength );
|
|
pxICMPPacket->xIPHeader.ucNextHeader = ipPROTOCOL_ICMP_IPv6;
|
|
pxICMPPacket->xIPHeader.ucHopLimit = 255U;
|
|
|
|
/* Source address "fe80::1" */
|
|
( void ) memset( pxICMPPacket->xIPHeader.xSourceAddress.ucBytes, 0, sizeof( pxICMPPacket->xIPHeader.xSourceAddress.ucBytes ) );
|
|
pxICMPPacket->xIPHeader.xSourceAddress.ucBytes[ 0 ] = 0xfeU;
|
|
pxICMPPacket->xIPHeader.xSourceAddress.ucBytes[ 1 ] = 0x80U;
|
|
pxICMPPacket->xIPHeader.xSourceAddress.ucBytes[ 15 ] = 0x01U;
|
|
|
|
/*ff02::1:ff5a:afe7 */
|
|
( void ) memset( xTargetIPAddress.ucBytes, 0, sizeof( xTargetIPAddress.ucBytes ) );
|
|
xTargetIPAddress.ucBytes[ 0 ] = 0xff;
|
|
xTargetIPAddress.ucBytes[ 1 ] = 0x02;
|
|
xTargetIPAddress.ucBytes[ 11 ] = 0x01;
|
|
xTargetIPAddress.ucBytes[ 12 ] = 0xff;
|
|
xTargetIPAddress.ucBytes[ 13 ] = pxIPAddress->ucBytes[ 13 ];
|
|
xTargetIPAddress.ucBytes[ 14 ] = pxIPAddress->ucBytes[ 14 ];
|
|
xTargetIPAddress.ucBytes[ 15 ] = pxIPAddress->ucBytes[ 15 ];
|
|
( void ) memcpy( pxICMPPacket->xIPHeader.xDestinationAddress.ucBytes, xTargetIPAddress.ucBytes, ipSIZE_OF_IPv6_ADDRESS );
|
|
|
|
/* Set ICMP header. */
|
|
( void ) memset( pxICMPHeader_IPv6, 0, sizeof( *pxICMPHeader_IPv6 ) );
|
|
pxICMPHeader_IPv6->ucTypeOfMessage = ipICMP_NEIGHBOR_SOLICITATION_IPv6;
|
|
( void ) memcpy( pxICMPHeader_IPv6->xIPv6Address.ucBytes, pxIPAddress->ucBytes, ipSIZE_OF_IPv6_ADDRESS );
|
|
pxICMPHeader_IPv6->ucOptionType = ndICMP_SOURCE_LINK_LAYER_ADDRESS;
|
|
pxICMPHeader_IPv6->ucOptionLength = 1U; /* times 8 bytes. */
|
|
( void ) memcpy( pxICMPHeader_IPv6->ucOptionBytes, pxEndPoint->xMACAddress.ucBytes, ipMAC_ADDRESS_LENGTH_BYTES );
|
|
|
|
/* Checksums. */
|
|
pxICMPHeader_IPv6->usChecksum = 0U;
|
|
/* calculate the ICMP checksum for the outgoing package. */
|
|
( void ) usGenerateProtocolChecksum( pxDescriptor->pucEthernetBuffer, pxDescriptor->xDataLength, pdTRUE );
|
|
|
|
/* This function will fill in the eth addresses and send the packet */
|
|
vReturnEthernetFrame( pxDescriptor, pdTRUE );
|
|
}
|
|
}
|
|
/*-----------------------------------------------------------*/
|
|
|
|
#if ( ipconfigSUPPORT_OUTGOING_PINGS == 1 )
|
|
|
|
/**
|
|
* @brief Send a PING request using an ICMPv6 format.
|
|
*
|
|
* @param[in] pxIPAddress: Send an IPv6 PING request.
|
|
* @param[in] uxNumberOfBytesToSend: The number of bytes to be sent.
|
|
* @param[in] uxBlockTimeTicks: The maximum number of clock-ticks to wait while
|
|
* putting the message on the queue for the IP-task.
|
|
*
|
|
* @return When failed: pdFAIL, otherwise the PING sequence number.
|
|
*/
|
|
BaseType_t FreeRTOS_SendPingRequestIPv6( const IPv6_Address_t * pxIPAddress,
|
|
size_t uxNumberOfBytesToSend,
|
|
TickType_t uxBlockTimeTicks )
|
|
{
|
|
NetworkBufferDescriptor_t * pxNetworkBuffer = NULL;
|
|
EthernetHeader_t * pxEthernetHeader;
|
|
ICMPPacket_IPv6_t * pxICMPPacket;
|
|
ICMPEcho_IPv6_t * pxICMPHeader;
|
|
BaseType_t xReturn = pdFAIL;
|
|
static uint16_t usSequenceNumber = 0;
|
|
uint8_t * pucChar;
|
|
IPStackEvent_t xStackTxEvent = { eStackTxEvent, NULL };
|
|
NetworkEndPoint_t * pxEndPoint = NULL;
|
|
size_t uxPacketLength = 0U;
|
|
|
|
pxEndPoint = FreeRTOS_FindEndPointOnIP_IPv6( pxIPAddress );
|
|
|
|
/* MISRA Ref 14.3.1 [Configuration dependent invariant] */
|
|
/* More details at: https://github.com/FreeRTOS/FreeRTOS-Plus-TCP/blob/main/MISRA.md#rule-143 */
|
|
/* coverity[misra_c_2012_rule_14_3_violation] */
|
|
/* coverity[notnull] */
|
|
BaseType_t xEnoughSpace;
|
|
|
|
{
|
|
BaseType_t xWanted = ( xIPv6_GetIPType( pxIPAddress ) == eIPv6_Global ) ? 1 : 0;
|
|
|
|
for( pxEndPoint = FreeRTOS_FirstEndPoint( NULL );
|
|
pxEndPoint != NULL;
|
|
pxEndPoint = FreeRTOS_NextEndPoint( NULL, pxEndPoint ) )
|
|
{
|
|
if( pxEndPoint->bits.bIPv6 != 0U )
|
|
{
|
|
BaseType_t xGot = ( xIPv6_GetIPType( &( pxEndPoint->ipv6_settings.xIPAddress ) ) == eIPv6_Global ) ? 1 : 0;
|
|
|
|
if( xWanted == xGot )
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if( uxNumberOfBytesToSend < ( ( ipconfigNETWORK_MTU - sizeof( IPHeader_IPv6_t ) ) - sizeof( ICMPEcho_IPv6_t ) ) )
|
|
{
|
|
xEnoughSpace = pdTRUE;
|
|
}
|
|
else
|
|
{
|
|
xEnoughSpace = pdFALSE;
|
|
}
|
|
|
|
if( pxEndPoint == NULL )
|
|
{
|
|
/* No endpoint found for the target IP-address. */
|
|
FreeRTOS_printf( ( "SendPingRequestIPv6: no end-point found for %pip\n",
|
|
pxIPAddress->ucBytes ) );
|
|
}
|
|
else if( ( uxGetNumberOfFreeNetworkBuffers() >= 3U ) && ( uxNumberOfBytesToSend >= 1U ) && ( xEnoughSpace != pdFALSE ) )
|
|
{
|
|
uxPacketLength = sizeof( EthernetHeader_t ) + sizeof( IPHeader_IPv6_t ) + sizeof( ICMPEcho_IPv6_t ) + uxNumberOfBytesToSend;
|
|
|
|
/* 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] */
|
|
pxNetworkBuffer = pxGetNetworkBufferWithDescriptor( BUFFER_FROM_WHERE_CALL( 181 ) uxPacketLength, uxBlockTimeTicks );
|
|
|
|
if( pxNetworkBuffer != NULL )
|
|
{
|
|
/* Probably not necessary to clear the buffer. */
|
|
( void ) memset( pxNetworkBuffer->pucEthernetBuffer, 0, pxNetworkBuffer->xDataLength );
|
|
|
|
pxNetworkBuffer->pxEndPoint = pxEndPoint;
|
|
pxNetworkBuffer->pxInterface = pxEndPoint->pxNetworkInterface;
|
|
|
|
/* 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] */
|
|
pxICMPPacket = ( ( ICMPPacket_IPv6_t * ) pxNetworkBuffer->pucEthernetBuffer );
|
|
|
|
pxICMPHeader = ( ( ICMPEcho_IPv6_t * ) &( pxICMPPacket->xICMPHeaderIPv6 ) );
|
|
usSequenceNumber++;
|
|
|
|
pxICMPPacket->xEthernetHeader.usFrameType = ipIPv6_FRAME_TYPE;
|
|
|
|
configASSERT( pxEndPoint != NULL );
|
|
configASSERT( pxEndPoint->bits.bIPv6 != pdFALSE_UNSIGNED );
|
|
|
|
pxICMPPacket->xIPHeader.usPayloadLength = FreeRTOS_htons( sizeof( ICMPEcho_IPv6_t ) + uxNumberOfBytesToSend );
|
|
( void ) memcpy( pxICMPPacket->xIPHeader.xDestinationAddress.ucBytes, pxIPAddress->ucBytes, ipSIZE_OF_IPv6_ADDRESS );
|
|
( void ) memcpy( pxICMPPacket->xIPHeader.xSourceAddress.ucBytes, pxEndPoint->ipv6_settings.xIPAddress.ucBytes, ipSIZE_OF_IPv6_ADDRESS );
|
|
FreeRTOS_printf( ( "ICMP send from %pip\n", pxICMPPacket->xIPHeader.xSourceAddress.ucBytes ) );
|
|
|
|
/* Fill in the basic header information. */
|
|
pxICMPHeader->ucTypeOfMessage = ipICMP_PING_REQUEST_IPv6;
|
|
pxICMPHeader->ucTypeOfService = 0;
|
|
pxICMPHeader->usIdentifier = FreeRTOS_htons( usSequenceNumber );
|
|
pxICMPHeader->usSequenceNumber = FreeRTOS_htons( usSequenceNumber );
|
|
|
|
/* Find the start of the data. */
|
|
pucChar = ( uint8_t * ) pxICMPHeader;
|
|
pucChar = &( pucChar[ sizeof( ICMPEcho_IPv6_t ) ] );
|
|
|
|
/* Just memset the data to a fixed value. */
|
|
( void ) memset( pucChar, ( int32_t ) ndECHO_DATA_FILL_BYTE, uxNumberOfBytesToSend );
|
|
|
|
/* The message is complete, IP and checksum's are handled by
|
|
* vProcessGeneratedUDPPacket */
|
|
pxNetworkBuffer->pucEthernetBuffer[ ipSOCKET_OPTIONS_OFFSET ] = FREERTOS_SO_UDPCKSUM_OUT;
|
|
( void ) memset( pxNetworkBuffer->xIPAddress.xIP_IPv6.ucBytes, 0, ipSIZE_OF_IPv6_ADDRESS );
|
|
( void ) memcpy( pxNetworkBuffer->xIPAddress.xIP_IPv6.ucBytes, pxIPAddress->ucBytes, ipSIZE_OF_IPv6_ADDRESS );
|
|
/* Let vProcessGeneratedUDPPacket() know that this is an ICMP packet. */
|
|
pxNetworkBuffer->usPort = ipPACKET_CONTAINS_ICMP_DATA;
|
|
/* 'uxPacketLength' is initialised due to the flow of the program. */
|
|
pxNetworkBuffer->xDataLength = uxPacketLength;
|
|
|
|
/* 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] */
|
|
pxEthernetHeader = ( ( EthernetHeader_t * ) pxNetworkBuffer->pucEthernetBuffer );
|
|
pxEthernetHeader->usFrameType = ipIPv6_FRAME_TYPE;
|
|
|
|
/* Send to the stack. */
|
|
xStackTxEvent.pvData = pxNetworkBuffer;
|
|
|
|
if( xSendEventStructToIPTask( &xStackTxEvent, uxBlockTimeTicks ) != pdPASS )
|
|
{
|
|
vReleaseNetworkBufferAndDescriptor( pxNetworkBuffer );
|
|
iptraceSTACK_TX_EVENT_LOST( ipSTACK_TX_EVENT );
|
|
}
|
|
else
|
|
{
|
|
xReturn = ( BaseType_t ) usSequenceNumber;
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
/* Either no proper end-pint found, or allocating the network buffer failed. */
|
|
}
|
|
|
|
return xReturn;
|
|
}
|
|
|
|
#endif /* ipconfigSUPPORT_OUTGOING_PINGS == 1 */
|
|
/*-----------------------------------------------------------*/
|
|
|
|
/**
|
|
* @brief Returns a printable string for the major ICMPv6 message types. Used for
|
|
* debugging only.
|
|
*
|
|
* @param[in] xType: The type of message.
|
|
*
|
|
* @return A null-terminated string that represents the type the kind of message.
|
|
*/
|
|
#if ( ipconfigHAS_PRINTF == 1 )
|
|
static const char * pcMessageType( BaseType_t xType )
|
|
{
|
|
const char * pcReturn;
|
|
|
|
switch( ( uint8_t ) xType )
|
|
{
|
|
case ipICMP_DEST_UNREACHABLE_IPv6:
|
|
pcReturn = "DEST_UNREACHABLE";
|
|
break;
|
|
|
|
case ipICMP_PACKET_TOO_BIG_IPv6:
|
|
pcReturn = "PACKET_TOO_BIG";
|
|
break;
|
|
|
|
case ipICMP_TIME_EXEEDED_IPv6:
|
|
pcReturn = "TIME_EXEEDED";
|
|
break;
|
|
|
|
case ipICMP_PARAMETER_PROBLEM_IPv6:
|
|
pcReturn = "PARAMETER_PROBLEM";
|
|
break;
|
|
|
|
case ipICMP_PING_REQUEST_IPv6:
|
|
pcReturn = "PING_REQUEST";
|
|
break;
|
|
|
|
case ipICMP_PING_REPLY_IPv6:
|
|
pcReturn = "PING_REPLY";
|
|
break;
|
|
|
|
case ipICMP_ROUTER_SOLICITATION_IPv6:
|
|
pcReturn = "ROUTER_SOL";
|
|
break;
|
|
|
|
case ipICMP_ROUTER_ADVERTISEMENT_IPv6:
|
|
pcReturn = "ROUTER_ADV";
|
|
break;
|
|
|
|
case ipICMP_NEIGHBOR_SOLICITATION_IPv6:
|
|
pcReturn = "NEIGHBOR_SOL";
|
|
break;
|
|
|
|
case ipICMP_NEIGHBOR_ADVERTISEMENT_IPv6:
|
|
pcReturn = "NEIGHBOR_ADV";
|
|
break;
|
|
|
|
default:
|
|
pcReturn = "UNKNOWN ICMP";
|
|
break;
|
|
}
|
|
|
|
return pcReturn;
|
|
}
|
|
#endif /* ( ipconfigHAS_PRINTF == 1 ) */
|
|
/*-----------------------------------------------------------*/
|
|
|
|
/**
|
|
* @brief When a neighbour advertisement has ben received, check if 'pxARPWaitingNetworkBuffer'
|
|
* was waiting for this new address look-up. If so, feed it to the IP-task as a new
|
|
* incoming packet.
|
|
*/
|
|
static void prvCheckWaitingBuffer( const IPv6_Address_t * pxIPv6Address )
|
|
{
|
|
const IPPacket_IPv6_t * pxIPPacket = ( ( IPPacket_IPv6_t * ) pxARPWaitingNetworkBuffer->pucEthernetBuffer );
|
|
const IPHeader_IPv6_t * pxIPHeader = &( pxIPPacket->xIPHeader );
|
|
|
|
if( memcmp( pxIPv6Address->ucBytes, pxIPHeader->xSourceAddress.ucBytes, ipSIZE_OF_IPv6_ADDRESS ) == 0 )
|
|
{
|
|
FreeRTOS_printf( ( "Waiting done\n" ) );
|
|
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( BUFFER_FROM_WHERE_CALL( 140 ) pxARPWaitingNetworkBuffer );
|
|
}
|
|
|
|
/* Clear the buffer. */
|
|
pxARPWaitingNetworkBuffer = NULL;
|
|
|
|
/* Found an ARP resolution, disable ARP resolution timer. */
|
|
vIPSetARPResolutionTimerEnableState( pdFALSE );
|
|
|
|
iptrace_DELAYED_ARP_REQUEST_REPLIED();
|
|
}
|
|
}
|
|
/*-----------------------------------------------------------*/
|
|
|
|
/**
|
|
* @brief Process an ICMPv6 packet and send replies when applicable.
|
|
*
|
|
* @param[in] pxNetworkBuffer: The Ethernet packet which contains an IPv6 message.
|
|
*
|
|
* @return A const value 'eReleaseBuffer' which means that the network must still be released.
|
|
*/
|
|
eFrameProcessingResult_t prvProcessICMPMessage_IPv6( NetworkBufferDescriptor_t * const 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] */
|
|
ICMPPacket_IPv6_t * pxICMPPacket = ( ( ICMPPacket_IPv6_t * ) pxNetworkBuffer->pucEthernetBuffer );
|
|
/* coverity[misra_c_2012_rule_11_3_violation] */
|
|
ICMPHeader_IPv6_t * pxICMPHeader_IPv6 = ( ( ICMPHeader_IPv6_t * ) &( pxICMPPacket->xICMPHeaderIPv6 ) );
|
|
NetworkEndPoint_t * pxEndPoint = pxNetworkBuffer->pxEndPoint;
|
|
size_t uxNeededSize;
|
|
|
|
#if ( ipconfigHAS_PRINTF == 1 )
|
|
{
|
|
if( pxICMPHeader_IPv6->ucTypeOfMessage != ipICMP_PING_REQUEST_IPv6 )
|
|
{
|
|
char pcAddress[ 40 ];
|
|
FreeRTOS_printf( ( "ICMPv6_recv %d (%s) from %pip to %pip end-point = %s\n",
|
|
pxICMPHeader_IPv6->ucTypeOfMessage,
|
|
pcMessageType( ( BaseType_t ) pxICMPHeader_IPv6->ucTypeOfMessage ),
|
|
pxICMPPacket->xIPHeader.xSourceAddress.ucBytes,
|
|
pxICMPPacket->xIPHeader.xDestinationAddress.ucBytes,
|
|
pcEndpointName( pxEndPoint, pcAddress, sizeof( pcAddress ) ) ) );
|
|
}
|
|
}
|
|
#endif /* ( ipconfigHAS_PRINTF == 1 ) */
|
|
|
|
if( ( pxEndPoint != NULL ) && ( pxEndPoint->bits.bIPv6 != pdFALSE_UNSIGNED ) )
|
|
{
|
|
switch( pxICMPHeader_IPv6->ucTypeOfMessage )
|
|
{
|
|
case ipICMP_DEST_UNREACHABLE_IPv6:
|
|
case ipICMP_PACKET_TOO_BIG_IPv6:
|
|
case ipICMP_TIME_EXEEDED_IPv6:
|
|
case ipICMP_PARAMETER_PROBLEM_IPv6:
|
|
/* These message types are not implemented. They are logged here above. */
|
|
break;
|
|
|
|
case ipICMP_PING_REQUEST_IPv6:
|
|
{
|
|
size_t uxICMPSize;
|
|
uint16_t usICMPSize;
|
|
|
|
/* Lint would complain about casting '()' immediately. */
|
|
usICMPSize = FreeRTOS_ntohs( pxICMPPacket->xIPHeader.usPayloadLength );
|
|
uxICMPSize = ( size_t ) usICMPSize;
|
|
uxNeededSize = ( size_t ) ( ipSIZE_OF_ETH_HEADER + ipSIZE_OF_IPv6_HEADER + uxICMPSize );
|
|
|
|
if( uxNeededSize > pxNetworkBuffer->xDataLength )
|
|
{
|
|
FreeRTOS_printf( ( "Too small\n" ) );
|
|
break;
|
|
}
|
|
|
|
pxICMPHeader_IPv6->ucTypeOfMessage = ipICMP_PING_REPLY_IPv6;
|
|
prvReturnICMP_IPv6( pxNetworkBuffer, uxICMPSize );
|
|
}
|
|
break;
|
|
|
|
#if ( ipconfigSUPPORT_OUTGOING_PINGS != 0 )
|
|
case ipICMP_PING_REPLY_IPv6:
|
|
{
|
|
ePingReplyStatus_t eStatus = eSuccess;
|
|
/* 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 ICMPEcho_IPv6_t * pxICMPEchoHeader = ( ( const ICMPEcho_IPv6_t * ) pxICMPHeader_IPv6 );
|
|
size_t uxDataLength, uxCount;
|
|
const uint8_t * pucByte;
|
|
|
|
/* Find the total length of the IP packet. */
|
|
uxDataLength = ipNUMERIC_CAST( size_t, FreeRTOS_ntohs( pxICMPPacket->xIPHeader.usPayloadLength ) );
|
|
uxDataLength = uxDataLength - sizeof( *pxICMPEchoHeader );
|
|
|
|
/* Find the first byte of the data within the ICMP packet. */
|
|
pucByte = ( const uint8_t * ) pxICMPEchoHeader;
|
|
pucByte = &( pucByte[ sizeof( *pxICMPEchoHeader ) ] );
|
|
|
|
/* Check each byte. */
|
|
for( uxCount = 0; uxCount < uxDataLength; uxCount++ )
|
|
{
|
|
if( *pucByte != ( uint8_t ) ipECHO_DATA_FILL_BYTE )
|
|
{
|
|
eStatus = eInvalidData;
|
|
break;
|
|
}
|
|
|
|
pucByte++;
|
|
}
|
|
|
|
/* Call back into the application to pass it the result. */
|
|
vApplicationPingReplyHook( eStatus, pxICMPEchoHeader->usIdentifier );
|
|
}
|
|
break;
|
|
#endif /* ( ipconfigSUPPORT_OUTGOING_PINGS != 0 ) */
|
|
case ipICMP_NEIGHBOR_SOLICITATION_IPv6:
|
|
{
|
|
size_t uxICMPSize;
|
|
BaseType_t xCompare;
|
|
NetworkEndPoint_t * pxEndPointFound = FreeRTOS_FindEndPointOnIP_IPv6( &( pxICMPHeader_IPv6->xIPv6Address ) );
|
|
char pcName[ 40 ];
|
|
FreeRTOS_printf( ( "Lookup %pip : endpoint %s\n",
|
|
pxICMPHeader_IPv6->xIPv6Address.ucBytes,
|
|
pcEndpointName( pxEndPointFound, pcName, sizeof( pcName ) ) ) );
|
|
|
|
if( pxEndPointFound != NULL )
|
|
{
|
|
pxEndPoint = pxEndPointFound;
|
|
}
|
|
|
|
pxNetworkBuffer->pxEndPoint = pxEndPoint;
|
|
pxNetworkBuffer->pxInterface = pxEndPoint->pxNetworkInterface;
|
|
|
|
uxICMPSize = sizeof( ICMPHeader_IPv6_t );
|
|
uxNeededSize = ( size_t ) ( ipSIZE_OF_ETH_HEADER + ipSIZE_OF_IPv6_HEADER + uxICMPSize );
|
|
|
|
if( uxNeededSize > pxNetworkBuffer->xDataLength )
|
|
{
|
|
FreeRTOS_printf( ( "Too small\n" ) );
|
|
break;
|
|
}
|
|
|
|
xCompare = memcmp( pxICMPHeader_IPv6->xIPv6Address.ucBytes, pxEndPoint->ipv6_settings.xIPAddress.ucBytes, ipSIZE_OF_IPv6_ADDRESS );
|
|
|
|
FreeRTOS_printf( ( "ND NS for %pip endpoint %pip %s\n",
|
|
pxICMPHeader_IPv6->xIPv6Address.ucBytes,
|
|
pxEndPoint->ipv6_settings.xIPAddress.ucBytes,
|
|
( xCompare == 0 ) ? "Reply" : "Ignore" ) );
|
|
|
|
if( xCompare == 0 )
|
|
{
|
|
pxICMPHeader_IPv6->ucTypeOfMessage = ipICMP_NEIGHBOR_ADVERTISEMENT_IPv6;
|
|
pxICMPHeader_IPv6->ucTypeOfService = 0U;
|
|
pxICMPHeader_IPv6->ulReserved = ndICMPv6_FLAG_SOLICITED | ndICMPv6_FLAG_UPDATE;
|
|
pxICMPHeader_IPv6->ulReserved = FreeRTOS_htonl( pxICMPHeader_IPv6->ulReserved );
|
|
|
|
/* Type of option. */
|
|
pxICMPHeader_IPv6->ucOptionType = ndICMP_TARGET_LINK_LAYER_ADDRESS;
|
|
/* Length of option in units of 8 bytes. */
|
|
pxICMPHeader_IPv6->ucOptionLength = 1U;
|
|
( void ) memcpy( pxICMPHeader_IPv6->ucOptionBytes, pxEndPoint->xMACAddress.ucBytes, sizeof( MACAddress_t ) );
|
|
pxICMPPacket->xIPHeader.ucHopLimit = 255U;
|
|
( void ) memcpy( pxICMPHeader_IPv6->xIPv6Address.ucBytes, pxEndPoint->ipv6_settings.xIPAddress.ucBytes, sizeof( pxICMPHeader_IPv6->xIPv6Address.ucBytes ) );
|
|
prvReturnICMP_IPv6( pxNetworkBuffer, uxICMPSize );
|
|
}
|
|
}
|
|
break;
|
|
|
|
case ipICMP_NEIGHBOR_ADVERTISEMENT_IPv6:
|
|
/* 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] */
|
|
vNDRefreshCacheEntry( ( ( const MACAddress_t * ) pxICMPHeader_IPv6->ucOptionBytes ),
|
|
&( pxICMPHeader_IPv6->xIPv6Address ),
|
|
pxEndPoint );
|
|
FreeRTOS_printf( ( "NEIGHBOR_ADV from %pip\n",
|
|
pxICMPHeader_IPv6->xIPv6Address.ucBytes ) );
|
|
|
|
#if ( ipconfigUSE_RA != 0 )
|
|
|
|
/* Receive a NA ( Neighbour Advertisement ) message to see if a chosen IP-address is already in use.
|
|
* This is important during SLAAC. */
|
|
vReceiveNA( pxNetworkBuffer );
|
|
#endif
|
|
|
|
if( ( pxARPWaitingNetworkBuffer != NULL ) &&
|
|
( uxIPHeaderSizePacket( pxARPWaitingNetworkBuffer ) == ipSIZE_OF_IPv6_HEADER ) )
|
|
{
|
|
prvCheckWaitingBuffer( &( pxICMPHeader_IPv6->xIPv6Address ) );
|
|
}
|
|
|
|
break;
|
|
|
|
case ipICMP_ROUTER_SOLICITATION_IPv6:
|
|
break;
|
|
|
|
#if ( ipconfigUSE_RA != 0 )
|
|
case ipICMP_ROUTER_ADVERTISEMENT_IPv6:
|
|
vReceiveRA( pxNetworkBuffer );
|
|
break;
|
|
#endif /* ( ipconfigUSE_RA != 0 ) */
|
|
|
|
default:
|
|
/* All possible values are included here above. */
|
|
break;
|
|
} /* switch( pxICMPHeader_IPv6->ucTypeOfMessage ) */
|
|
} /* if( pxEndPoint != NULL ) */
|
|
|
|
return eReleaseBuffer;
|
|
}
|
|
/*-----------------------------------------------------------*/
|
|
|
|
/**
|
|
* @brief Send out a Neighbour Advertisement message.
|
|
*
|
|
* @param[in] pxEndPoint: The end-point to use.
|
|
*/
|
|
/* MISRA Ref 8.9.1 [File scoped variables] */
|
|
/* More details at: https://github.com/FreeRTOS/FreeRTOS-Plus-TCP/blob/main/MISRA.md#rule-89 */
|
|
/* coverity[misra_c_2012_rule_8_9_violation] */
|
|
/* coverity[single_use] */
|
|
void FreeRTOS_OutputAdvertiseIPv6( NetworkEndPoint_t * pxEndPoint )
|
|
{
|
|
NetworkBufferDescriptor_t * pxNetworkBuffer;
|
|
ICMPPacket_IPv6_t * pxICMPPacket;
|
|
NetworkInterface_t * pxInterface;
|
|
ICMPHeader_IPv6_t * pxICMPHeader_IPv6;
|
|
size_t uxICMPSize;
|
|
size_t uxPacketSize;
|
|
|
|
uxPacketSize = ipSIZE_OF_ETH_HEADER + ipSIZE_OF_IPv6_HEADER + sizeof( ICMPHeader_IPv6_t );
|
|
|
|
/* This is called from the context of the IP event task, so a block time
|
|
* must not be used. */
|
|
pxNetworkBuffer = pxGetNetworkBufferWithDescriptor( uxPacketSize, ndDONT_BLOCK );
|
|
|
|
if( pxNetworkBuffer != NULL )
|
|
{
|
|
( void ) memset( pxNetworkBuffer->xIPAddress.xIP_IPv6.ucBytes, 0, ipSIZE_OF_IPv6_ADDRESS );
|
|
pxNetworkBuffer->pxEndPoint = pxEndPoint;
|
|
|
|
pxInterface = pxEndPoint->pxNetworkInterface;
|
|
|
|
configASSERT( pxInterface != 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] */
|
|
pxICMPPacket = ( ( ICMPPacket_IPv6_t * ) pxNetworkBuffer->pucEthernetBuffer );
|
|
pxICMPHeader_IPv6 = ( ( ICMPHeader_IPv6_t * ) &( pxICMPPacket->xICMPHeaderIPv6 ) );
|
|
|
|
( void ) memcpy( pxICMPPacket->xEthernetHeader.xDestinationAddress.ucBytes, pcLOCAL_ALL_NODES_MULTICAST_MAC, ipMAC_ADDRESS_LENGTH_BYTES );
|
|
( void ) memcpy( pxICMPPacket->xEthernetHeader.xSourceAddress.ucBytes, pxEndPoint->xMACAddress.ucBytes, ipMAC_ADDRESS_LENGTH_BYTES );
|
|
pxICMPPacket->xEthernetHeader.usFrameType = ipIPv6_FRAME_TYPE; /* 12 + 2 = 14 */
|
|
|
|
pxICMPPacket->xIPHeader.ucVersionTrafficClass = 0x60;
|
|
pxICMPPacket->xIPHeader.ucTrafficClassFlow = 0;
|
|
pxICMPPacket->xIPHeader.usFlowLabel = 0;
|
|
|
|
pxICMPPacket->xIPHeader.usPayloadLength = FreeRTOS_htons( sizeof( ICMPHeader_IPv6_t ) );
|
|
pxICMPPacket->xIPHeader.ucNextHeader = ipPROTOCOL_ICMP_IPv6;
|
|
pxICMPPacket->xIPHeader.ucHopLimit = 255;
|
|
( void ) memcpy( pxICMPPacket->xIPHeader.xSourceAddress.ucBytes, pxEndPoint->ipv6_settings.xIPAddress.ucBytes, ipSIZE_OF_IPv6_ADDRESS );
|
|
( void ) memcpy( pxICMPPacket->xIPHeader.xDestinationAddress.ucBytes, pcLOCAL_ALL_NODES_MULTICAST_IP, ipSIZE_OF_IPv6_ADDRESS );
|
|
|
|
uxICMPSize = sizeof( ICMPHeader_IPv6_t );
|
|
pxICMPHeader_IPv6->ucTypeOfMessage = ipICMP_NEIGHBOR_ADVERTISEMENT_IPv6;
|
|
pxICMPHeader_IPv6->ucTypeOfService = 0;
|
|
pxICMPHeader_IPv6->ulReserved = ndICMPv6_FLAG_SOLICITED | ndICMPv6_FLAG_UPDATE;
|
|
pxICMPHeader_IPv6->ulReserved = FreeRTOS_htonl( pxICMPHeader_IPv6->ulReserved );
|
|
|
|
/* Type of option. */
|
|
pxICMPHeader_IPv6->ucOptionType = ndICMP_TARGET_LINK_LAYER_ADDRESS;
|
|
/* Length of option in units of 8 bytes. */
|
|
pxICMPHeader_IPv6->ucOptionLength = 1;
|
|
( void ) memcpy( pxICMPHeader_IPv6->ucOptionBytes, pxEndPoint->xMACAddress.ucBytes, sizeof( MACAddress_t ) );
|
|
pxICMPPacket->xIPHeader.ucHopLimit = 255;
|
|
( void ) memcpy( pxICMPHeader_IPv6->xIPv6Address.ucBytes, pxEndPoint->ipv6_settings.xIPAddress.ucBytes, sizeof( pxICMPHeader_IPv6->xIPv6Address.ucBytes ) );
|
|
|
|
/* Important: tell NIC driver how many bytes must be sent */
|
|
pxNetworkBuffer->xDataLength = ( size_t ) ( ipSIZE_OF_ETH_HEADER + ipSIZE_OF_IPv6_HEADER + uxICMPSize );
|
|
|
|
pxICMPHeader_IPv6->usChecksum = 0;
|
|
/* calculate the UDP checksum for outgoing package */
|
|
( void ) usGenerateProtocolChecksum( pxNetworkBuffer->pucEthernetBuffer, pxNetworkBuffer->xDataLength, pdTRUE );
|
|
|
|
/* Set the parameter 'bReleaseAfterSend'. */
|
|
( void ) pxInterface->pfOutput( pxInterface, pxNetworkBuffer, pdTRUE );
|
|
}
|
|
}
|
|
/*-----------------------------------------------------------*/
|
|
|
|
/**
|
|
* @brief Create an IPv16 address, based on a prefix.
|
|
*
|
|
* @param[out] pxIPAddress: The location where the new IPv6 address will be stored.
|
|
* @param[in] pxPrefix: The prefix to be used.
|
|
* @param[in] uxPrefixLength: The length of the prefix.
|
|
* @param[in] xDoRandom: A non-zero value if the bits after the prefix should have a random value.
|
|
*
|
|
* @return pdPASS if the operation was successful. Or pdFAIL in case xApplicationGetRandomNumber()
|
|
* returned an error.
|
|
*/
|
|
BaseType_t FreeRTOS_CreateIPv6Address( IPv6_Address_t * pxIPAddress,
|
|
const IPv6_Address_t * pxPrefix,
|
|
size_t uxPrefixLength,
|
|
BaseType_t xDoRandom )
|
|
{
|
|
uint32_t pulRandom[ 4 ];
|
|
uint8_t * pucSource;
|
|
BaseType_t xIndex, xResult = pdPASS;
|
|
|
|
if( xDoRandom != pdFALSE )
|
|
{
|
|
/* Create an IP-address, based on a net prefix and a
|
|
* random host address.
|
|
* ARRAY_SIZE_X() returns the size of an array as a
|
|
* signed value ( BaseType_t ).
|
|
*/
|
|
for( xIndex = 0; xIndex < ARRAY_SIZE_X( pulRandom ); xIndex++ )
|
|
{
|
|
if( xApplicationGetRandomNumber( &( pulRandom[ xIndex ] ) ) == pdFAIL )
|
|
{
|
|
xResult = pdFAIL;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
( void ) memset( pulRandom, 0, sizeof( pulRandom ) );
|
|
}
|
|
|
|
if( xResult == pdPASS )
|
|
{
|
|
configASSERT( ( uxPrefixLength > 0U ) && ( uxPrefixLength < ( 8U * ipSIZE_OF_IPv6_ADDRESS ) ) );
|
|
|
|
if( uxPrefixLength >= 8U )
|
|
{
|
|
( void ) memcpy( pxIPAddress->ucBytes, pxPrefix->ucBytes, ( uxPrefixLength + 7U ) / 8U );
|
|
}
|
|
|
|
pucSource = ( uint8_t * ) pulRandom;
|
|
size_t uxIndex = uxPrefixLength / 8U;
|
|
|
|
if( ( uxPrefixLength % 8U ) != 0U )
|
|
{
|
|
/* uxHostLen is between 1 and 7 bits long. */
|
|
size_t uxHostLen = 8U - ( uxPrefixLength % 8U );
|
|
uint32_t uxHostMask = ( ( ( uint32_t ) 1U ) << uxHostLen ) - 1U;
|
|
uint8_t ucNetMask = ( uint8_t ) ~( uxHostMask );
|
|
|
|
pxIPAddress->ucBytes[ uxIndex ] &= ucNetMask;
|
|
pxIPAddress->ucBytes[ uxIndex ] |= ( pucSource[ 0 ] & ( ( uint8_t ) uxHostMask ) );
|
|
pucSource = &( pucSource[ 1 ] );
|
|
uxIndex++;
|
|
}
|
|
|
|
if( uxIndex < ipSIZE_OF_IPv6_ADDRESS )
|
|
{
|
|
( void ) memcpy( &( pxIPAddress->ucBytes[ uxIndex ] ), pucSource, ipSIZE_OF_IPv6_ADDRESS - uxIndex );
|
|
}
|
|
}
|
|
|
|
return xResult;
|
|
}
|
|
/*-----------------------------------------------------------*/
|
|
|
|
#endif /* ipconfigUSE_IPv6 */
|