1
0
mirror of https://github.com/FreeRTOS/FreeRTOS-Plus-TCP synced 2025-10-20 22:10:04 +08:00
Files
FreeRTOS-Plus-TCP/source/FreeRTOS_ND.c
Hein Tibosch ff11a1484a Dev integration hein.v8 (#738)
* 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>
2023-02-23 14:50:27 +05:30

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 */