/* * 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 #include /* 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 - SOLICIT. */ #define ndICMPv6_FLAG_SOLICITED 0x40000000U /** @brief Type of Neighbour Advertisement packets - UPDATE. */ #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 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 */ /** @brief All nodes on the local network segment: MAC address. */ 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 ) { IPv6_Type_t eType = xIPv6_GetIPType( &( pxEndPoint->ipv6_settings.xIPAddress ) ); if( eType == eIPv6_LinkLocal ) { 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( xIsIPv6AllowedMulticast( 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", ( void * ) 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", ( void * ) pxIPAddress->ucBytes, ( void * ) 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", ( void * ) 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", ( void * ) pxIPAddress->ucBytes ) ); FreeRTOS_printf( ( "eNDGetCacheEntry: From addr %pip\n", ( void * ) 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", ( void * ) ( *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; BaseType_t xOldestValue = ipconfigMAX_ARP_AGE + 1; BaseType_t xOldestEntry = 0; /* 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. */ /* Keep track of the oldest entry in case we need to overwrite it. The problem we are trying to avoid is * that there may be a queued packet in pxARPWaitingNetworkBuffer and we may have just received the * neighbor advertisement needed for that packet. If we don't store this network advertisement in cache, * the parting of the frame from pxARPWaitingNetworkBuffer will cause the sending of neighbor solicitation * and stores the frame in pxARPWaitingNetworkBuffer. This becomes a vicious circle with thousands of * neighbor solicitation/advertisement packets going back and forth because the ND cache is full. * Overwriting the oldest cache entry is not a fool-proof solution, but it's something. */ if( xNDCache[ x ].ucAge < xOldestValue ) { xOldestValue = xNDCache[ x ].ucAge; xOldestEntry = x; } } } if( xEntryFound < 0 ) { /* The IP-address was not found, use the first free location. */ if( xFreeEntry >= 0 ) { xEntryFound = xFreeEntry; } else { /* No free location. Overwrite the oldest. */ xEntryFound = xOldestEntry; FreeRTOS_printf( ( "vNDRefreshCacheEntry: Cache FULL! Overwriting oldest entry %i with %02X-%02X-%02X-%02X-%02X-%02X\n", ( int ) xEntryFound, pxMACAddress->ucBytes[ 0 ], pxMACAddress->ucBytes[ 1 ], pxMACAddress->ucBytes[ 2 ], pxMACAddress->ucBytes[ 3 ], pxMACAddress->ucBytes[ 4 ], pxMACAddress->ucBytes[ 5 ] ) ); } } /* At this point, xEntryFound is always a valid index. */ /* 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; } /*-----------------------------------------------------------*/ /** * @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, ( void * ) 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", ( void * ) 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; char pcBuffer[ 40 ]; /* Loop through each entry in the ND cache. */ for( x = 0; x < ipconfigND_CACHE_ENTRIES; x++ ) { if( xNDCache[ x ].ucValid != ( uint8_t ) 0U ) { /* See if the MAC-address also matches, and we're all happy */ FreeRTOS_printf( ( "ND %2d: age %3u - %pip MAC %02x-%02x-%02x-%02x-%02x-%02x endPoint %s\n", ( int ) x, xNDCache[ x ].ucAge, ( void * ) 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 ); ( 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 ICMPv6 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 * 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; NetworkBufferDescriptor_t * pxNewDescriptor = NULL; BaseType_t xReleased = pdFALSE; if( ( pxEndPoint != NULL ) && ( pxEndPoint->bits.bIPv6 != pdFALSE_UNSIGNED ) ) { uxNeededSize = ipSIZE_OF_ETH_HEADER + ipSIZE_OF_IPv6_HEADER + sizeof( ICMPHeader_IPv6_t ); if( pxDescriptor->xDataLength < uxNeededSize ) { pxNewDescriptor = pxDuplicateNetworkBufferWithDescriptor( pxDescriptor, uxNeededSize ); vReleaseNetworkBufferAndDescriptor( pxDescriptor ); pxDescriptor = pxNewDescriptor; } 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 */ ( void ) memcpy( pxICMPPacket->xIPHeader.xSourceAddress.ucBytes, pxEndPoint->ipv6_settings.xIPAddress.ucBytes, ipSIZE_OF_IPv6_ADDRESS ); /*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. */ #if ( ipconfigDRIVER_INCLUDED_TX_IP_CHECKSUM == 0 ) { /* calculate the ICMPv6 checksum for outgoing package */ ( void ) usGenerateProtocolChecksum( pxDescriptor->pucEthernetBuffer, pxDescriptor->xDataLength, pdTRUE ); } #else { /* Many EMAC peripherals will only calculate the ICMP checksum * correctly if the field is nulled beforehand. */ pxICMPHeader_IPv6->usChecksum = 0U; } #endif /* This function will fill in the eth addresses and send the packet */ vReturnEthernetFrame( pxDescriptor, pdTRUE ); xReleased = pdTRUE; } } if( ( pxDescriptor != NULL ) && ( xReleased == pdFALSE ) ) { vReleaseNetworkBufferAndDescriptor( pxDescriptor ); } } /*-----------------------------------------------------------*/ #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; BaseType_t xEnoughSpace; 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] */ if( pxEndPoint == NULL ) { 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", ( void * ) 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; 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", ( void * ) 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 */ /*-----------------------------------------------------------*/ #if ( ipconfigHAS_PRINTF == 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. */ 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_EXCEEDED_IPv6: pcReturn = "TIME_EXCEEDED"; 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 been 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 ) { /* 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_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 ), ( void * ) pxICMPPacket->xIPHeader.xSourceAddress.ucBytes, ( void * ) 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_EXCEEDED_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 ]; ( void ) memset( &( pcName ), 0, sizeof( pcName ) ); FreeRTOS_printf( ( "Lookup %pip : endpoint %s\n", ( void * ) 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", ( void * ) pxICMPHeader_IPv6->xIPv6Address.ucBytes, ( void * ) 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", ( void * ) 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 ); #if ( ipconfigDRIVER_INCLUDED_TX_IP_CHECKSUM == 0 ) { /* calculate the ICMPv6 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. */ pxICMPHeader_IPv6->usChecksum = 0; } #endif /* 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 ) { /* A loopback IP-address has a prefix of 128. */ 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 */