xref: /FreeRTOS-Plus-TCP-v4.0.0/source/FreeRTOS_ND.c (revision 574b646147a48c508a8bfc82181fd89ea89c8c17)
1 /*
2  * FreeRTOS+TCP V2.3.1
3  * Copyright (C) 2020 Amazon.com, Inc. or its affiliates.  All Rights Reserved.
4  *
5  * Permission is hereby granted, free of charge, to any person obtaining a copy of
6  * this software and associated documentation files (the "Software"), to deal in
7  * the Software without restriction, including without limitation the rights to
8  * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
9  * the Software, and to permit persons to whom the Software is furnished to do so,
10  * subject to the following conditions:
11  *
12  * The above copyright notice and this permission notice shall be included in all
13  * copies or substantial portions of the Software.
14  *
15  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
17  * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
18  * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
19  * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
20  * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
21  *
22  * http://aws.amazon.com/freertos
23  * http://www.FreeRTOS.org
24  */
25 
26 /**
27  * @file FreeRTOS_ND.c
28  * @brief Implements a few functions that handle Neighbour Discovery and other ICMPv6 messages.
29  */
30 
31 /* Standard includes. */
32 #include <stdint.h>
33 #include <stdio.h>
34 
35 
36 /* FreeRTOS includes. */
37 #include "FreeRTOS.h"
38 #include "task.h"
39 
40 /* FreeRTOS+TCP includes. */
41 #include "FreeRTOS_IP.h"
42 #include "FreeRTOS_Sockets.h"
43 #include "FreeRTOS_IP_Private.h"
44 #include "FreeRTOS_ARP.h"
45 #include "FreeRTOS_UDP_IP.h"
46 #include "FreeRTOS_Routing.h"
47 #include "FreeRTOS_ND.h"
48 #include "FreeRTOS_IP_Timers.h"
49 
50 #if ( ipconfigUSE_LLMNR == 1 )
51     #include "FreeRTOS_DNS.h"
52 #endif /* ipconfigUSE_LLMNR */
53 #include "NetworkBufferManagement.h"
54 
55 /* The entire module FreeRTOS_ND.c is skipped when IPv6 is not used. */
56 #if ( ipconfigUSE_IPv6 != 0 )
57 
58 /** @brief Type of Neighbour Advertisement packets - SOLICIT. */
59     #define ndICMPv6_FLAG_SOLICITED                       0x40000000U
60 /** @brief Type of Neighbour Advertisement packets - UPDATE. */
61     #define ndICMPv6_FLAG_UPDATE                          0x20000000U
62 
63 /** @brief A block time of 0 simply means "don't block". */
64     #define ndDONT_BLOCK                                  ( ( TickType_t ) 0 )
65 
66 /** @brief The character used to fill ICMP echo requests, and therefore also the
67  *         character expected to fill ICMP echo replies.
68  */
69     #define ndECHO_DATA_FILL_BYTE                         'x'
70 
71 /** @brief When ucAge becomes 3 or less, it is time for a new
72  * neighbour solicitation.
73  */
74     #define ndMAX_CACHE_AGE_BEFORE_NEW_ND_SOLICITATION    ( 3U )
75 
76 /** @brief All nodes on the local network segment: IP address. */
77     /* MISRA Ref 8.9.1 [File scoped variables] */
78     /* More details at: https://github.com/FreeRTOS/FreeRTOS-Plus-TCP/blob/main/MISRA.md#rule-89 */
79     /* coverity[misra_c_2012_rule_8_9_violation] */
80     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 */
81 /** @brief All nodes on the local network segment: MAC address. */
82     static const uint8_t pcLOCAL_ALL_NODES_MULTICAST_MAC[ ipMAC_ADDRESS_LENGTH_BYTES ] = { 0x33, 0x33, 0x00, 0x00, 0x00, 0x01 };
83 
84 /** @brief See if the MAC-address can be resolved because it is a multi-cast address. */
85     static eARPLookupResult_t prvMACResolve( const IPv6_Address_t * pxAddressToLookup,
86                                              MACAddress_t * const pxMACAddress,
87                                              NetworkEndPoint_t ** ppxEndPoint );
88 
89 /** @brief Lookup an MAC address in the ND cache from the IP address. */
90     static eARPLookupResult_t prvNDCacheLookup( const IPv6_Address_t * pxAddressToLookup,
91                                                 MACAddress_t * const pxMACAddress,
92                                                 NetworkEndPoint_t ** ppxEndPoint );
93 
94     #if ( ipconfigHAS_PRINTF == 1 )
95         static const char * pcMessageType( BaseType_t xType );
96     #endif
97 
98 /** @brief Find the first end-point of type IPv6. */
99     static NetworkEndPoint_t * pxFindLocalEndpoint( void );
100 
101 /** @brief The ND cache. */
102     static NDCacheRow_t xNDCache[ ipconfigND_CACHE_ENTRIES ];
103 
104 /*-----------------------------------------------------------*/
105 
106 /*
107  *  ff02::1: All IPv6 devices
108  *  ff02::2: All IPv6 routers
109  *  ff02::5: All OSPFv3 routers
110  *  ff02::a: All EIGRP (IPv6) routers
111  */
112 
113 /**
114  * @brief Find the first end-point of type IPv6.
115  *
116  * @return The first IPv6 end-point found.
117  */
pxFindLocalEndpoint(void)118     static NetworkEndPoint_t * pxFindLocalEndpoint( void )
119     {
120         NetworkEndPoint_t * pxEndPoint;
121 
122         for( pxEndPoint = FreeRTOS_FirstEndPoint( NULL );
123              pxEndPoint != NULL;
124              pxEndPoint = FreeRTOS_NextEndPoint( NULL, pxEndPoint ) )
125         {
126             if( pxEndPoint->bits.bIPv6 == pdTRUE_UNSIGNED )
127             {
128                 IPv6_Type_t eType = xIPv6_GetIPType( &( pxEndPoint->ipv6_settings.xIPAddress ) );
129 
130                 if( eType == eIPv6_LinkLocal )
131                 {
132                     break;
133                 }
134             }
135         }
136 
137         return pxEndPoint;
138     }
139 
140 /**
141  * @brief See if the MAC-address can be resolved because it is a multi-cast address.
142  *
143  * @param[in] pxAddressToLookup The IP-address to look-up.
144  * @param[out] pxMACAddress The resulting MAC-address is stored here.
145  * @param[out] ppxEndPoint A pointer to an end-point pointer where the end-point will be stored.
146  *
147  * @return An enum, either eARPCacheHit or eARPCacheMiss.
148  */
prvMACResolve(const IPv6_Address_t * pxAddressToLookup,MACAddress_t * const pxMACAddress,NetworkEndPoint_t ** ppxEndPoint)149     static eARPLookupResult_t prvMACResolve( const IPv6_Address_t * pxAddressToLookup,
150                                              MACAddress_t * const pxMACAddress,
151                                              NetworkEndPoint_t ** ppxEndPoint )
152     {
153         eARPLookupResult_t eReturn;
154 
155         /* Mostly used multi-cast address is ff02::. */
156         if( xIsIPv6AllowedMulticast( pxAddressToLookup ) != pdFALSE )
157         {
158             vSetMultiCastIPv6MacAddress( pxAddressToLookup, pxMACAddress );
159 
160             if( ppxEndPoint != NULL )
161             {
162                 *ppxEndPoint = pxFindLocalEndpoint();
163             }
164 
165             eReturn = eARPCacheHit;
166         }
167         else
168         {
169             /* Not a multicast IP address. */
170             eReturn = eARPCacheMiss;
171         }
172 
173         return eReturn;
174     }
175 /*-----------------------------------------------------------*/
176 
177 /**
178  * @brief Find the MAC-address of an IPv6 address.  It will first determine if is a multicast
179  *        address, if not, it will check the ND cache.
180  *
181  * @param[in] pxIPAddress The IPv6 address to be looked up.
182  * @param[out] pxMACAddress The MAC-address found.
183  * @param[out] ppxEndPoint A pointer to a pointer to an end-point, where the end-point will be stored.
184  *
185  * @return An enum which says whether the address was found: eARPCacheHit or eARPCacheMiss.
186  */
eNDGetCacheEntry(IPv6_Address_t * pxIPAddress,MACAddress_t * const pxMACAddress,struct xNetworkEndPoint ** ppxEndPoint)187     eARPLookupResult_t eNDGetCacheEntry( IPv6_Address_t * pxIPAddress,
188                                          MACAddress_t * const pxMACAddress,
189                                          struct xNetworkEndPoint ** ppxEndPoint )
190     {
191         eARPLookupResult_t eReturn;
192         NetworkEndPoint_t * pxEndPoint;
193 
194         /* Multi-cast addresses can be resolved immediately. */
195         eReturn = prvMACResolve( pxIPAddress, pxMACAddress, ppxEndPoint );
196 
197         if( eReturn == eARPCacheMiss )
198         {
199             /* See if the IP-address has an entry in the cache. */
200             eReturn = prvNDCacheLookup( pxIPAddress, pxMACAddress, ppxEndPoint );
201         }
202 
203         if( eReturn == eARPCacheMiss )
204         {
205             FreeRTOS_printf( ( "eNDGetCacheEntry: lookup %pip miss\n", ( void * ) pxIPAddress->ucBytes ) );
206         }
207 
208         if( eReturn == eARPCacheMiss )
209         {
210             IPv6_Type_t eIPType = xIPv6_GetIPType( pxIPAddress );
211 
212             pxEndPoint = FreeRTOS_FindEndPointOnIP_IPv6( pxIPAddress );
213 
214             if( pxEndPoint != NULL )
215             {
216                 if( ppxEndPoint != NULL )
217                 {
218                     *( ppxEndPoint ) = pxEndPoint;
219                 }
220 
221                 FreeRTOS_printf( ( "eNDGetCacheEntry: FindEndPointOnIP failed for %pip (endpoint %pip)\n",
222                                    ( void * ) pxIPAddress->ucBytes,
223                                    ( void * ) pxEndPoint->ipv6_settings.xIPAddress.ucBytes ) );
224             }
225             else
226             {
227                 if( eIPType == eIPv6_LinkLocal )
228                 {
229                     for( pxEndPoint = FreeRTOS_FirstEndPoint( NULL );
230                          pxEndPoint != NULL;
231                          pxEndPoint = FreeRTOS_NextEndPoint( NULL, pxEndPoint ) )
232                     {
233                         IPv6_Type_t eMyType = xIPv6_GetIPType( &( pxEndPoint->ipv6_settings.xIPAddress ) );
234 
235                         if( eMyType == eIPType )
236                         {
237                             eReturn = prvNDCacheLookup( pxIPAddress, pxMACAddress, ppxEndPoint );
238                             break;
239                         }
240                     }
241 
242                     FreeRTOS_printf( ( "eNDGetCacheEntry: LinkLocal %pip \"%s\"\n", ( void * ) pxIPAddress->ucBytes,
243                                        ( eReturn == eARPCacheHit ) ? "hit" : "miss" ) );
244                 }
245                 else
246                 {
247                     pxEndPoint = FreeRTOS_FindGateWay( ( BaseType_t ) ipTYPE_IPv6 );
248 
249                     if( pxEndPoint != NULL )
250                     {
251                         ( void ) memcpy( pxIPAddress->ucBytes, pxEndPoint->ipv6_settings.xGatewayAddress.ucBytes, ipSIZE_OF_IPv6_ADDRESS );
252                         FreeRTOS_printf( ( "eNDGetCacheEntry: Using gw %pip\n", ( void * ) pxIPAddress->ucBytes ) );
253                         FreeRTOS_printf( ( "eNDGetCacheEntry: From addr %pip\n", ( void * ) pxEndPoint->ipv6_settings.xIPAddress.ucBytes ) );
254 
255                         /* See if the gateway has an entry in the cache. */
256                         eReturn = prvNDCacheLookup( pxIPAddress, pxMACAddress, ppxEndPoint );
257 
258                         if( *ppxEndPoint != NULL )
259                         {
260                             FreeRTOS_printf( ( "eNDGetCacheEntry: found end-point %pip\n", ( void * ) ( *ppxEndPoint )->ipv6_settings.xIPAddress.ucBytes ) );
261                         }
262 
263                         *( ppxEndPoint ) = pxEndPoint;
264                     }
265                 }
266             }
267         }
268 
269         return eReturn;
270     }
271 /*-----------------------------------------------------------*/
272 
273 /**
274  * @brief Store a combination of IP-address, MAC-address and an end-point in a free location
275  *        in the ND cache.
276  *
277  * @param[in] pxMACAddress The MAC-address
278  * @param[in] pxIPAddress The IP-address
279  * @param[in] pxEndPoint The end-point through which the IP-address can be reached.
280  *
281  */
vNDRefreshCacheEntry(const MACAddress_t * pxMACAddress,const IPv6_Address_t * pxIPAddress,NetworkEndPoint_t * pxEndPoint)282     void vNDRefreshCacheEntry( const MACAddress_t * pxMACAddress,
283                                const IPv6_Address_t * pxIPAddress,
284                                NetworkEndPoint_t * pxEndPoint )
285     {
286         BaseType_t x;
287         BaseType_t xFreeEntry = -1, xEntryFound = -1;
288 
289         /* For each entry in the ND cache table. */
290         for( x = 0; x < ipconfigND_CACHE_ENTRIES; x++ )
291         {
292             if( xNDCache[ x ].ucValid == ( uint8_t ) pdFALSE )
293             {
294                 if( xFreeEntry == -1 )
295                 {
296                     xFreeEntry = x;
297                 }
298             }
299             else if( memcmp( xNDCache[ x ].xIPAddress.ucBytes, pxIPAddress->ucBytes, ipSIZE_OF_IPv6_ADDRESS ) == 0 )
300             {
301                 xEntryFound = x;
302                 break;
303             }
304             else
305             {
306                 /* Entry is valid but the IP-address doesn't match. */
307             }
308         }
309 
310         if( xEntryFound < 0 )
311         {
312             /* The IP-address was not found, use the first free location. */
313             xEntryFound = xFreeEntry;
314         }
315 
316         if( xEntryFound >= 0 )
317         {
318             /* Copy the IP-address. */
319             ( void ) memcpy( xNDCache[ xEntryFound ].xIPAddress.ucBytes, pxIPAddress->ucBytes, ipSIZE_OF_IPv6_ADDRESS );
320             /* Copy the MAC-address. */
321             ( void ) memcpy( xNDCache[ xEntryFound ].xMACAddress.ucBytes, pxMACAddress->ucBytes, sizeof( MACAddress_t ) );
322             xNDCache[ xEntryFound ].pxEndPoint = pxEndPoint;
323             xNDCache[ xEntryFound ].ucAge = ( uint8_t ) ipconfigMAX_ARP_AGE;
324             xNDCache[ xEntryFound ].ucValid = ( uint8_t ) pdTRUE;
325         }
326         else
327         {
328             FreeRTOS_printf( ( "vNDRefreshCacheEntry: %pip not found\n", ( void * ) pxIPAddress->ucBytes ) );
329         }
330     }
331 /*-----------------------------------------------------------*/
332 
333 /**
334  * @brief Reduce the age counter in each entry within the ND cache.  An entry is no
335  * longer considered valid and is deleted if its age reaches zero.
336  * Just before getting to zero, 3 times a neighbour solicitation will be sent.
337  */
vNDAgeCache(void)338     void vNDAgeCache( void )
339     {
340         BaseType_t x;
341 
342         /* Loop through each entry in the ND cache. */
343         for( x = 0; x < ipconfigND_CACHE_ENTRIES; x++ )
344         {
345             BaseType_t xDoSolicitate = pdFALSE;
346 
347             /* If the entry is valid (its age is greater than zero). */
348             if( xNDCache[ x ].ucAge > 0U )
349             {
350                 /* Decrement the age value of the entry in this ND cache table row.
351                  * When the age reaches zero it is no longer considered valid. */
352                 ( xNDCache[ x ].ucAge )--;
353 
354                 if( xNDCache[ x ].ucAge == 0U )
355                 {
356                     /* The entry is no longer valid.  Wipe it out. */
357                     iptraceND_TABLE_ENTRY_EXPIRED( xNDCache[ x ].xIPAddress );
358                     ( void ) memset( &( xNDCache[ x ] ), 0, sizeof( xNDCache[ x ] ) );
359                 }
360                 else
361                 {
362                     /* If the entry is not yet valid, then it is waiting an ND
363                      * advertisement, and the ND solicitation should be retransmitted. */
364                     if( xNDCache[ x ].ucValid == ( uint8_t ) pdFALSE )
365                     {
366                         xDoSolicitate = pdTRUE;
367                     }
368                     else if( xNDCache[ x ].ucAge <= ( uint8_t ) ndMAX_CACHE_AGE_BEFORE_NEW_ND_SOLICITATION )
369                     {
370                         /* This entry will get removed soon.  See if the MAC address is
371                          * still valid to prevent this happening. */
372                         iptraceND_TABLE_ENTRY_WILL_EXPIRE( xNDCache[ x ].xIPAddress );
373                         xDoSolicitate = pdTRUE;
374                     }
375                     else
376                     {
377                         /* The age has just ticked down, with nothing to do. */
378                     }
379 
380                     if( xDoSolicitate != pdFALSE )
381                     {
382                         size_t uxNeededSize;
383                         NetworkBufferDescriptor_t * pxNetworkBuffer;
384 
385                         uxNeededSize = ipSIZE_OF_ETH_HEADER + ipSIZE_OF_IPv6_HEADER + sizeof( ICMPHeader_IPv6_t );
386                         pxNetworkBuffer = pxGetNetworkBufferWithDescriptor( uxNeededSize, 0U );
387 
388                         if( pxNetworkBuffer != NULL )
389                         {
390                             pxNetworkBuffer->pxEndPoint = xNDCache[ x ].pxEndPoint;
391                             /* _HT_ From here I am suspecting a network buffer leak */
392                             vNDSendNeighbourSolicitation( pxNetworkBuffer, &( xNDCache[ x ].xIPAddress ) );
393                         }
394                     }
395                 }
396             }
397         }
398     }
399 /*-----------------------------------------------------------*/
400 
401 /**
402  * @brief Clear the Neighbour Discovery cache.
403  */
FreeRTOS_ClearND(void)404     void FreeRTOS_ClearND( void )
405     {
406         ( void ) memset( xNDCache, 0, sizeof( xNDCache ) );
407     }
408 /*-----------------------------------------------------------*/
409 
410 /**
411  * @brief Look-up an IPv6 address in the cache.
412  *
413  * @param[in] pxAddressToLookup The IPv6 address to look-up.Ethernet packet.
414  * @param[out] pxMACAddress The resulting MAC-address will be stored here.
415  * @param[out] ppxEndPoint A pointer to a pointer to an end-point, where the end-point will be stored.
416  *
417  * @return An enum: either eARPCacheHit or eARPCacheMiss.
418  */
prvNDCacheLookup(const IPv6_Address_t * pxAddressToLookup,MACAddress_t * const pxMACAddress,NetworkEndPoint_t ** ppxEndPoint)419     static eARPLookupResult_t prvNDCacheLookup( const IPv6_Address_t * pxAddressToLookup,
420                                                 MACAddress_t * const pxMACAddress,
421                                                 NetworkEndPoint_t ** ppxEndPoint )
422     {
423         BaseType_t x;
424         eARPLookupResult_t eReturn = eARPCacheMiss;
425 
426         /* For each entry in the ND cache table. */
427         for( x = 0; x < ipconfigND_CACHE_ENTRIES; x++ )
428         {
429             if( xNDCache[ x ].ucValid == ( uint8_t ) pdFALSE )
430             {
431                 /* Skip invalid entries. */
432             }
433             else if( memcmp( xNDCache[ x ].xIPAddress.ucBytes, pxAddressToLookup->ucBytes, ipSIZE_OF_IPv6_ADDRESS ) == 0 )
434             {
435                 ( void ) memcpy( pxMACAddress->ucBytes, xNDCache[ x ].xMACAddress.ucBytes, sizeof( MACAddress_t ) );
436                 eReturn = eARPCacheHit;
437 
438                 if( ppxEndPoint != NULL )
439                 {
440                     *ppxEndPoint = xNDCache[ x ].pxEndPoint;
441                 }
442 
443                 FreeRTOS_debug_printf( ( "prvCacheLookup6[ %d ] %pip with %02x:%02x:%02x:%02x:%02x:%02x\n",
444                                          ( int ) x,
445                                          ( void * ) pxAddressToLookup->ucBytes,
446                                          pxMACAddress->ucBytes[ 0 ],
447                                          pxMACAddress->ucBytes[ 1 ],
448                                          pxMACAddress->ucBytes[ 2 ],
449                                          pxMACAddress->ucBytes[ 3 ],
450                                          pxMACAddress->ucBytes[ 4 ],
451                                          pxMACAddress->ucBytes[ 5 ] ) );
452                 break;
453             }
454             else
455             {
456                 /* Entry is valid but the MAC-address doesn't match. */
457             }
458         }
459 
460         if( eReturn == eARPCacheMiss )
461         {
462             FreeRTOS_printf( ( "prvNDCacheLookup %pip Miss\n", ( void * ) pxAddressToLookup->ucBytes ) );
463 
464             if( ppxEndPoint != NULL )
465             {
466                 *ppxEndPoint = NULL;
467             }
468         }
469 
470         return eReturn;
471     }
472 /*-----------------------------------------------------------*/
473 
474     #if ( ( ipconfigHAS_PRINTF != 0 ) || ( ipconfigHAS_DEBUG_PRINTF != 0 ) )
475 
476 /**
477  * @brief Print the contents of the ND cache, for debugging only.
478  */
FreeRTOS_PrintNDCache(void)479         void FreeRTOS_PrintNDCache( void )
480         {
481             BaseType_t x, xCount = 0;
482             char pcBuffer[ 40 ];
483 
484             /* Loop through each entry in the ND cache. */
485             for( x = 0; x < ipconfigARP_CACHE_ENTRIES; x++ )
486             {
487                 if( xNDCache[ x ].ucValid != ( uint8_t ) 0U )
488                 {
489                     /* See if the MAC-address also matches, and we're all happy */
490 
491                     FreeRTOS_printf( ( "ND %2d: age %3u - %pip MAC %02x-%02x-%02x-%02x-%02x-%02x endPoint %s\n",
492                                        ( int ) x,
493                                        xNDCache[ x ].ucAge,
494                                        ( void * ) xNDCache[ x ].xIPAddress.ucBytes,
495                                        xNDCache[ x ].xMACAddress.ucBytes[ 0 ],
496                                        xNDCache[ x ].xMACAddress.ucBytes[ 1 ],
497                                        xNDCache[ x ].xMACAddress.ucBytes[ 2 ],
498                                        xNDCache[ x ].xMACAddress.ucBytes[ 3 ],
499                                        xNDCache[ x ].xMACAddress.ucBytes[ 4 ],
500                                        xNDCache[ x ].xMACAddress.ucBytes[ 5 ],
501                                        pcEndpointName( xNDCache[ x ].pxEndPoint, pcBuffer, sizeof( pcBuffer ) ) ) );
502                     xCount++;
503                 }
504             }
505 
506             FreeRTOS_printf( ( "Arp has %ld entries\n", xCount ) );
507         }
508 
509     #endif /* ( ipconfigHAS_PRINTF != 0 ) || ( ipconfigHAS_DEBUG_PRINTF != 0 ) */
510 /*-----------------------------------------------------------*/
511 
512 /**
513  * @brief Return an ICMPv6 packet to the peer.
514  *
515  * @param[in] pxNetworkBuffer The Ethernet packet.
516  * @param[in] uxICMPSize The number of bytes to be sent.
517  */
prvReturnICMP_IPv6(NetworkBufferDescriptor_t * const pxNetworkBuffer,size_t uxICMPSize)518     static void prvReturnICMP_IPv6( NetworkBufferDescriptor_t * const pxNetworkBuffer,
519                                     size_t uxICMPSize )
520     {
521         const NetworkEndPoint_t * pxEndPoint = pxNetworkBuffer->pxEndPoint;
522 
523         /* MISRA Ref 11.3.1 [Misaligned access] */
524         /* More details at: https://github.com/FreeRTOS/FreeRTOS-Plus-TCP/blob/main/MISRA.md#rule-113 */
525         /* coverity[misra_c_2012_rule_11_3_violation] */
526         ICMPPacket_IPv6_t * pxICMPPacket = ( ( ICMPPacket_IPv6_t * ) pxNetworkBuffer->pucEthernetBuffer );
527 
528         ( void ) memcpy( pxICMPPacket->xIPHeader.xDestinationAddress.ucBytes, pxICMPPacket->xIPHeader.xSourceAddress.ucBytes, ipSIZE_OF_IPv6_ADDRESS );
529         ( void ) memcpy( pxICMPPacket->xIPHeader.xSourceAddress.ucBytes, pxEndPoint->ipv6_settings.xIPAddress.ucBytes, ipSIZE_OF_IPv6_ADDRESS );
530         pxICMPPacket->xIPHeader.usPayloadLength = FreeRTOS_htons( uxICMPSize );
531 
532         /* Important: tell NIC driver how many bytes must be sent */
533         pxNetworkBuffer->xDataLength = ( size_t ) ( ipSIZE_OF_ETH_HEADER + ipSIZE_OF_IPv6_HEADER + uxICMPSize );
534 
535         #if ( ipconfigDRIVER_INCLUDED_TX_IP_CHECKSUM == 0 )
536             {
537                 /* calculate the ICMPv6 checksum for outgoing package */
538                 ( void ) usGenerateProtocolChecksum( pxNetworkBuffer->pucEthernetBuffer, pxNetworkBuffer->xDataLength, pdTRUE );
539             }
540         #else
541             {
542                 /* Many EMAC peripherals will only calculate the ICMP checksum
543                  * correctly if the field is nulled beforehand. */
544                 pxICMPPacket->xICMPHeaderIPv6.usChecksum = 0;
545             }
546         #endif
547 
548         /* This function will fill in the Ethernet addresses and send the packet */
549         vReturnEthernetFrame( pxNetworkBuffer, pdFALSE );
550     }
551 /*-----------------------------------------------------------*/
552 
553 /**
554  * @brief Send out an ND request for the IPv6 address contained in pxNetworkBuffer, and
555  *        add an entry into the ND table that indicates that an ND reply is outstanding
556  *        so re-transmissions can be generated.
557  *
558  * @param[in] pxNetworkBuffer The network buffer in which the message shall be stored.
559  * @param[in] pxIPAddress The IPv6 address that is asked to send a Neighbour Advertisement.
560  *
561  * @note Send out an ND request for the IPv6 address contained in pxNetworkBuffer, and
562  * add an entry into the ND table that indicates that an ND reply is
563  * outstanding so re-transmissions can be generated.
564  */
565 
vNDSendNeighbourSolicitation(NetworkBufferDescriptor_t * pxNetworkBuffer,const IPv6_Address_t * pxIPAddress)566     void vNDSendNeighbourSolicitation( NetworkBufferDescriptor_t * pxNetworkBuffer,
567                                        const IPv6_Address_t * pxIPAddress )
568     {
569         ICMPPacket_IPv6_t * pxICMPPacket;
570         ICMPHeader_IPv6_t * pxICMPHeader_IPv6;
571         const NetworkEndPoint_t * pxEndPoint = pxNetworkBuffer->pxEndPoint;
572         size_t uxNeededSize;
573         IPv6_Address_t xTargetIPAddress;
574         MACAddress_t xMultiCastMacAddress;
575         NetworkBufferDescriptor_t * pxDescriptor = pxNetworkBuffer;
576         NetworkBufferDescriptor_t * pxNewDescriptor = NULL;
577 
578         if( ( pxEndPoint != NULL ) && ( pxEndPoint->bits.bIPv6 != pdFALSE_UNSIGNED ) )
579         {
580             uxNeededSize = ipSIZE_OF_ETH_HEADER + ipSIZE_OF_IPv6_HEADER + sizeof( ICMPHeader_IPv6_t );
581 
582             if( pxDescriptor->xDataLength < uxNeededSize )
583             {
584                 pxNewDescriptor = pxDuplicateNetworkBufferWithDescriptor( pxDescriptor, uxNeededSize );
585                 vReleaseNetworkBufferAndDescriptor( pxDescriptor );
586                 pxDescriptor = pxNewDescriptor;
587             }
588 
589             if( pxDescriptor != NULL )
590             {
591                 const uint32_t ulPayloadLength = 32U;
592 
593                 /* MISRA Ref 11.3.1 [Misaligned access] */
594                 /* More details at: https://github.com/FreeRTOS/FreeRTOS-Plus-TCP/blob/main/MISRA.md#rule-113 */
595                 /* coverity[misra_c_2012_rule_11_3_violation] */
596                 pxICMPPacket = ( ( ICMPPacket_IPv6_t * ) pxDescriptor->pucEthernetBuffer );
597                 pxICMPHeader_IPv6 = ( ( ICMPHeader_IPv6_t * ) &( pxICMPPacket->xICMPHeaderIPv6 ) );
598 
599                 pxDescriptor->xDataLength = uxNeededSize;
600 
601                 /* Set the multi-cast MAC-address. */
602                 xMultiCastMacAddress.ucBytes[ 0 ] = 0x33U;
603                 xMultiCastMacAddress.ucBytes[ 1 ] = 0x33U;
604                 xMultiCastMacAddress.ucBytes[ 2 ] = 0xffU;
605                 xMultiCastMacAddress.ucBytes[ 3 ] = pxIPAddress->ucBytes[ 13 ];
606                 xMultiCastMacAddress.ucBytes[ 4 ] = pxIPAddress->ucBytes[ 14 ];
607                 xMultiCastMacAddress.ucBytes[ 5 ] = pxIPAddress->ucBytes[ 15 ];
608 
609                 /* Set Ethernet header. Source and Destination will be swapped. */
610                 ( void ) memcpy( pxICMPPacket->xEthernetHeader.xSourceAddress.ucBytes, xMultiCastMacAddress.ucBytes, ipMAC_ADDRESS_LENGTH_BYTES );
611                 ( void ) memcpy( pxICMPPacket->xEthernetHeader.xDestinationAddress.ucBytes, pxEndPoint->xMACAddress.ucBytes, ipMAC_ADDRESS_LENGTH_BYTES );
612                 pxICMPPacket->xEthernetHeader.usFrameType = ipIPv6_FRAME_TYPE;
613 
614                 /* Set IP-header. */
615                 pxICMPPacket->xIPHeader.ucVersionTrafficClass = 0x60U;
616                 pxICMPPacket->xIPHeader.ucTrafficClassFlow = 0U;
617                 pxICMPPacket->xIPHeader.usFlowLabel = 0U;
618                 pxICMPPacket->xIPHeader.usPayloadLength = FreeRTOS_htons( ulPayloadLength );
619                 pxICMPPacket->xIPHeader.ucNextHeader = ipPROTOCOL_ICMP_IPv6;
620                 pxICMPPacket->xIPHeader.ucHopLimit = 255U;
621 
622                 /* Source address */
623                 ( void ) memcpy( pxICMPPacket->xIPHeader.xSourceAddress.ucBytes, pxEndPoint->ipv6_settings.xIPAddress.ucBytes, ipSIZE_OF_IPv6_ADDRESS );
624 
625                 /*ff02::1:ff5a:afe7 */
626                 ( void ) memset( xTargetIPAddress.ucBytes, 0, sizeof( xTargetIPAddress.ucBytes ) );
627                 xTargetIPAddress.ucBytes[ 0 ] = 0xff;
628                 xTargetIPAddress.ucBytes[ 1 ] = 0x02;
629                 xTargetIPAddress.ucBytes[ 11 ] = 0x01;
630                 xTargetIPAddress.ucBytes[ 12 ] = 0xff;
631                 xTargetIPAddress.ucBytes[ 13 ] = pxIPAddress->ucBytes[ 13 ];
632                 xTargetIPAddress.ucBytes[ 14 ] = pxIPAddress->ucBytes[ 14 ];
633                 xTargetIPAddress.ucBytes[ 15 ] = pxIPAddress->ucBytes[ 15 ];
634                 ( void ) memcpy( pxICMPPacket->xIPHeader.xDestinationAddress.ucBytes, xTargetIPAddress.ucBytes, ipSIZE_OF_IPv6_ADDRESS );
635 
636                 /* Set ICMP header. */
637                 ( void ) memset( pxICMPHeader_IPv6, 0, sizeof( *pxICMPHeader_IPv6 ) );
638                 pxICMPHeader_IPv6->ucTypeOfMessage = ipICMP_NEIGHBOR_SOLICITATION_IPv6;
639                 ( void ) memcpy( pxICMPHeader_IPv6->xIPv6Address.ucBytes, pxIPAddress->ucBytes, ipSIZE_OF_IPv6_ADDRESS );
640                 pxICMPHeader_IPv6->ucOptionType = ndICMP_SOURCE_LINK_LAYER_ADDRESS;
641                 pxICMPHeader_IPv6->ucOptionLength = 1U; /* times 8 bytes. */
642                 ( void ) memcpy( pxICMPHeader_IPv6->ucOptionBytes, pxEndPoint->xMACAddress.ucBytes, ipMAC_ADDRESS_LENGTH_BYTES );
643 
644                 /* Checksums. */
645                 #if ( ipconfigDRIVER_INCLUDED_TX_IP_CHECKSUM == 0 )
646                     {
647                         /* calculate the ICMPv6 checksum for outgoing package */
648                         ( void ) usGenerateProtocolChecksum( pxDescriptor->pucEthernetBuffer, pxDescriptor->xDataLength, pdTRUE );
649                     }
650                 #else
651                     {
652                         /* Many EMAC peripherals will only calculate the ICMP checksum
653                          * correctly if the field is nulled beforehand. */
654                         pxICMPHeader_IPv6->usChecksum = 0U;
655                     }
656                 #endif
657 
658                 /* This function will fill in the eth addresses and send the packet */
659                 vReturnEthernetFrame( pxDescriptor, pdTRUE );
660             }
661         }
662     }
663 /*-----------------------------------------------------------*/
664 
665     #if ( ipconfigSUPPORT_OUTGOING_PINGS == 1 )
666 
667 /**
668  * @brief Send a PING request using an ICMPv6 format.
669  *
670  * @param[in] pxIPAddress Send an IPv6 PING request.
671  * @param[in] uxNumberOfBytesToSend The number of bytes to be sent.
672  * @param[in] uxBlockTimeTicks The maximum number of clock-ticks to wait while
673  *            putting the message on the queue for the IP-task.
674  *
675  * @return When failed: pdFAIL, otherwise the PING sequence number.
676  */
FreeRTOS_SendPingRequestIPv6(const IPv6_Address_t * pxIPAddress,size_t uxNumberOfBytesToSend,TickType_t uxBlockTimeTicks)677         BaseType_t FreeRTOS_SendPingRequestIPv6( const IPv6_Address_t * pxIPAddress,
678                                                  size_t uxNumberOfBytesToSend,
679                                                  TickType_t uxBlockTimeTicks )
680         {
681             NetworkBufferDescriptor_t * pxNetworkBuffer = NULL;
682             EthernetHeader_t * pxEthernetHeader;
683             ICMPPacket_IPv6_t * pxICMPPacket;
684             ICMPEcho_IPv6_t * pxICMPHeader;
685             BaseType_t xReturn = pdFAIL;
686             static uint16_t usSequenceNumber = 0;
687             uint8_t * pucChar;
688             IPStackEvent_t xStackTxEvent = { eStackTxEvent, NULL };
689             NetworkEndPoint_t * pxEndPoint = NULL;
690             size_t uxPacketLength = 0U;
691             BaseType_t xEnoughSpace;
692 
693             pxEndPoint = FreeRTOS_FindEndPointOnIP_IPv6( pxIPAddress );
694 
695             /* MISRA Ref 14.3.1 [Configuration dependent invariant] */
696             /* More details at: https://github.com/FreeRTOS/FreeRTOS-Plus-TCP/blob/main/MISRA.md#rule-143 */
697             /* coverity[misra_c_2012_rule_14_3_violation] */
698             /* coverity[notnull] */
699             if( pxEndPoint == NULL )
700             {
701                 BaseType_t xWanted = ( xIPv6_GetIPType( pxIPAddress ) == eIPv6_Global ) ? 1 : 0;
702 
703                 for( pxEndPoint = FreeRTOS_FirstEndPoint( NULL );
704                      pxEndPoint != NULL;
705                      pxEndPoint = FreeRTOS_NextEndPoint( NULL, pxEndPoint ) )
706                 {
707                     if( pxEndPoint->bits.bIPv6 != 0U )
708                     {
709                         BaseType_t xGot = ( xIPv6_GetIPType( &( pxEndPoint->ipv6_settings.xIPAddress ) ) == eIPv6_Global ) ? 1 : 0;
710 
711                         if( xWanted == xGot )
712                         {
713                             break;
714                         }
715                     }
716                 }
717             }
718 
719             if( uxNumberOfBytesToSend < ( ( ipconfigNETWORK_MTU - sizeof( IPHeader_IPv6_t ) ) - sizeof( ICMPEcho_IPv6_t ) ) )
720             {
721                 xEnoughSpace = pdTRUE;
722             }
723             else
724             {
725                 xEnoughSpace = pdFALSE;
726             }
727 
728             if( pxEndPoint == NULL )
729             {
730                 /* No endpoint found for the target IP-address. */
731                 FreeRTOS_printf( ( "SendPingRequestIPv6: no end-point found for %pip\n",
732                                    ( void * ) pxIPAddress->ucBytes ) );
733             }
734             else if( ( uxGetNumberOfFreeNetworkBuffers() >= 3U ) && ( uxNumberOfBytesToSend >= 1U ) && ( xEnoughSpace != pdFALSE ) )
735             {
736                 uxPacketLength = sizeof( EthernetHeader_t ) + sizeof( IPHeader_IPv6_t ) + sizeof( ICMPEcho_IPv6_t ) + uxNumberOfBytesToSend;
737 
738                 /* MISRA Ref 11.3.1 [Misaligned access] */
739                 /* More details at: https://github.com/FreeRTOS/FreeRTOS-Plus-TCP/blob/main/MISRA.md#rule-113 */
740                 /* coverity[misra_c_2012_rule_11_3_violation] */
741                 pxNetworkBuffer = pxGetNetworkBufferWithDescriptor( BUFFER_FROM_WHERE_CALL( 181 ) uxPacketLength, uxBlockTimeTicks );
742 
743                 if( pxNetworkBuffer != NULL )
744                 {
745                     /* Probably not necessary to clear the buffer. */
746                     ( void ) memset( pxNetworkBuffer->pucEthernetBuffer, 0, pxNetworkBuffer->xDataLength );
747 
748                     pxNetworkBuffer->pxEndPoint = pxEndPoint;
749                     pxNetworkBuffer->pxInterface = pxEndPoint->pxNetworkInterface;
750 
751                     /* MISRA Ref 11.3.1 [Misaligned access] */
752                     /* More details at: https://github.com/FreeRTOS/FreeRTOS-Plus-TCP/blob/main/MISRA.md#rule-113 */
753                     /* coverity[misra_c_2012_rule_11_3_violation] */
754                     pxICMPPacket = ( ( ICMPPacket_IPv6_t * ) pxNetworkBuffer->pucEthernetBuffer );
755 
756                     pxICMPHeader = ( ( ICMPEcho_IPv6_t * ) &( pxICMPPacket->xICMPHeaderIPv6 ) );
757                     usSequenceNumber++;
758 
759                     pxICMPPacket->xEthernetHeader.usFrameType = ipIPv6_FRAME_TYPE;
760 
761                     pxICMPPacket->xIPHeader.usPayloadLength = FreeRTOS_htons( sizeof( ICMPEcho_IPv6_t ) + uxNumberOfBytesToSend );
762                     ( void ) memcpy( pxICMPPacket->xIPHeader.xDestinationAddress.ucBytes, pxIPAddress->ucBytes, ipSIZE_OF_IPv6_ADDRESS );
763                     ( void ) memcpy( pxICMPPacket->xIPHeader.xSourceAddress.ucBytes, pxEndPoint->ipv6_settings.xIPAddress.ucBytes, ipSIZE_OF_IPv6_ADDRESS );
764                     FreeRTOS_printf( ( "ICMP send from %pip\n", ( void * ) pxICMPPacket->xIPHeader.xSourceAddress.ucBytes ) );
765 
766                     /* Fill in the basic header information. */
767                     pxICMPHeader->ucTypeOfMessage = ipICMP_PING_REQUEST_IPv6;
768                     pxICMPHeader->ucTypeOfService = 0;
769                     pxICMPHeader->usIdentifier = FreeRTOS_htons( usSequenceNumber );
770                     pxICMPHeader->usSequenceNumber = FreeRTOS_htons( usSequenceNumber );
771 
772                     /* Find the start of the data. */
773                     pucChar = ( uint8_t * ) pxICMPHeader;
774                     pucChar = &( pucChar[ sizeof( ICMPEcho_IPv6_t ) ] );
775 
776                     /* Just memset the data to a fixed value. */
777                     ( void ) memset( pucChar, ( int32_t ) ndECHO_DATA_FILL_BYTE, uxNumberOfBytesToSend );
778 
779                     /* The message is complete, IP and checksum's are handled by
780                      * vProcessGeneratedUDPPacket */
781                     pxNetworkBuffer->pucEthernetBuffer[ ipSOCKET_OPTIONS_OFFSET ] = FREERTOS_SO_UDPCKSUM_OUT;
782                     ( void ) memset( pxNetworkBuffer->xIPAddress.xIP_IPv6.ucBytes, 0, ipSIZE_OF_IPv6_ADDRESS );
783                     ( void ) memcpy( pxNetworkBuffer->xIPAddress.xIP_IPv6.ucBytes, pxIPAddress->ucBytes, ipSIZE_OF_IPv6_ADDRESS );
784                     /* Let vProcessGeneratedUDPPacket() know that this is an ICMP packet. */
785                     pxNetworkBuffer->usPort = ipPACKET_CONTAINS_ICMP_DATA;
786                     /* 'uxPacketLength' is initialised due to the flow of the program. */
787                     pxNetworkBuffer->xDataLength = uxPacketLength;
788 
789                     /* MISRA Ref 11.3.1 [Misaligned access] */
790                     /* More details at: https://github.com/FreeRTOS/FreeRTOS-Plus-TCP/blob/main/MISRA.md#rule-113 */
791                     /* coverity[misra_c_2012_rule_11_3_violation] */
792                     pxEthernetHeader = ( ( EthernetHeader_t * ) pxNetworkBuffer->pucEthernetBuffer );
793                     pxEthernetHeader->usFrameType = ipIPv6_FRAME_TYPE;
794 
795                     /* Send to the stack. */
796                     xStackTxEvent.pvData = pxNetworkBuffer;
797 
798                     if( xSendEventStructToIPTask( &xStackTxEvent, uxBlockTimeTicks ) != pdPASS )
799                     {
800                         vReleaseNetworkBufferAndDescriptor( pxNetworkBuffer );
801                         iptraceSTACK_TX_EVENT_LOST( ipSTACK_TX_EVENT );
802                     }
803                     else
804                     {
805                         xReturn = ( BaseType_t ) usSequenceNumber;
806                     }
807                 }
808             }
809             else
810             {
811                 /* Either no proper end-pint found, or allocating the network buffer failed. */
812             }
813 
814             return xReturn;
815         }
816 
817     #endif /* ipconfigSUPPORT_OUTGOING_PINGS == 1 */
818 /*-----------------------------------------------------------*/
819 
820 
821     #if ( ipconfigHAS_PRINTF == 1 )
822 
823 /**
824  * @brief Returns a printable string for the major ICMPv6 message types.  Used for
825  *        debugging only.
826  *
827  * @param[in] xType The type of message.
828  *
829  * @return A null-terminated string that represents the type the kind of message.
830  */
pcMessageType(BaseType_t xType)831         static const char * pcMessageType( BaseType_t xType )
832         {
833             const char * pcReturn;
834 
835             switch( ( uint8_t ) xType )
836             {
837                 case ipICMP_DEST_UNREACHABLE_IPv6:
838                     pcReturn = "DEST_UNREACHABLE";
839                     break;
840 
841                 case ipICMP_PACKET_TOO_BIG_IPv6:
842                     pcReturn = "PACKET_TOO_BIG";
843                     break;
844 
845                 case ipICMP_TIME_EXEEDED_IPv6:
846                     pcReturn = "TIME_EXEEDED";
847                     break;
848 
849                 case ipICMP_PARAMETER_PROBLEM_IPv6:
850                     pcReturn = "PARAMETER_PROBLEM";
851                     break;
852 
853                 case ipICMP_PING_REQUEST_IPv6:
854                     pcReturn = "PING_REQUEST";
855                     break;
856 
857                 case ipICMP_PING_REPLY_IPv6:
858                     pcReturn = "PING_REPLY";
859                     break;
860 
861                 case ipICMP_ROUTER_SOLICITATION_IPv6:
862                     pcReturn = "ROUTER_SOL";
863                     break;
864 
865                 case ipICMP_ROUTER_ADVERTISEMENT_IPv6:
866                     pcReturn = "ROUTER_ADV";
867                     break;
868 
869                 case ipICMP_NEIGHBOR_SOLICITATION_IPv6:
870                     pcReturn = "NEIGHBOR_SOL";
871                     break;
872 
873                 case ipICMP_NEIGHBOR_ADVERTISEMENT_IPv6:
874                     pcReturn = "NEIGHBOR_ADV";
875                     break;
876 
877                 default:
878                     pcReturn = "UNKNOWN ICMP";
879                     break;
880             }
881 
882             return pcReturn;
883         }
884     #endif /* ( ipconfigHAS_PRINTF == 1 ) */
885 /*-----------------------------------------------------------*/
886 
887 /**
888  * @brief When a neighbour advertisement has been received, check if 'pxARPWaitingNetworkBuffer'
889  *        was waiting for this new address look-up. If so, feed it to the IP-task as a new
890  *        incoming packet.
891  */
prvCheckWaitingBuffer(const IPv6_Address_t * pxIPv6Address)892     static void prvCheckWaitingBuffer( const IPv6_Address_t * pxIPv6Address )
893     {
894         /* MISRA Ref 11.3.1 [Misaligned access] */
895         /* More details at: https://github.com/FreeRTOS/FreeRTOS-Plus-TCP/blob/main/MISRA.md#rule-113 */
896         /* coverity[misra_c_2012_rule_11_3_violation] */
897         const IPPacket_IPv6_t * pxIPPacket = ( ( IPPacket_IPv6_t * ) pxARPWaitingNetworkBuffer->pucEthernetBuffer );
898         const IPHeader_IPv6_t * pxIPHeader = &( pxIPPacket->xIPHeader );
899 
900         if( memcmp( pxIPv6Address->ucBytes, pxIPHeader->xSourceAddress.ucBytes, ipSIZE_OF_IPv6_ADDRESS ) == 0 )
901         {
902             FreeRTOS_printf( ( "Waiting done\n" ) );
903             IPStackEvent_t xEventMessage;
904             const TickType_t xDontBlock = ( TickType_t ) 0;
905 
906             xEventMessage.eEventType = eNetworkRxEvent;
907             xEventMessage.pvData = ( void * ) pxARPWaitingNetworkBuffer;
908 
909             if( xSendEventStructToIPTask( &xEventMessage, xDontBlock ) != pdPASS )
910             {
911                 /* Failed to send the message, so release the network buffer. */
912                 vReleaseNetworkBufferAndDescriptor( BUFFER_FROM_WHERE_CALL( 140 ) pxARPWaitingNetworkBuffer );
913             }
914 
915             /* Clear the buffer. */
916             pxARPWaitingNetworkBuffer = NULL;
917 
918             /* Found an ARP resolution, disable ARP resolution timer. */
919             vIPSetARPResolutionTimerEnableState( pdFALSE );
920 
921             iptrace_DELAYED_ARP_REQUEST_REPLIED();
922         }
923     }
924 /*-----------------------------------------------------------*/
925 
926 /**
927  * @brief Process an ICMPv6 packet and send replies when applicable.
928  *
929  * @param[in] pxNetworkBuffer The Ethernet packet which contains an IPv6 message.
930  *
931  * @return A const value 'eReleaseBuffer' which means that the network must still be released.
932  */
prvProcessICMPMessage_IPv6(NetworkBufferDescriptor_t * const pxNetworkBuffer)933     eFrameProcessingResult_t prvProcessICMPMessage_IPv6( NetworkBufferDescriptor_t * const pxNetworkBuffer )
934     {
935         /* MISRA Ref 11.3.1 [Misaligned access] */
936         /* More details at: https://github.com/FreeRTOS/FreeRTOS-Plus-TCP/blob/main/MISRA.md#rule-113 */
937         /* coverity[misra_c_2012_rule_11_3_violation] */
938         ICMPPacket_IPv6_t * pxICMPPacket = ( ( ICMPPacket_IPv6_t * ) pxNetworkBuffer->pucEthernetBuffer );
939         /* coverity[misra_c_2012_rule_11_3_violation] */
940         ICMPHeader_IPv6_t * pxICMPHeader_IPv6 = ( ( ICMPHeader_IPv6_t * ) &( pxICMPPacket->xICMPHeaderIPv6 ) );
941         NetworkEndPoint_t * pxEndPoint = pxNetworkBuffer->pxEndPoint;
942         size_t uxNeededSize;
943 
944         #if ( ipconfigHAS_PRINTF == 1 )
945             {
946                 if( pxICMPHeader_IPv6->ucTypeOfMessage != ipICMP_PING_REQUEST_IPv6 )
947                 {
948                     char pcAddress[ 40 ];
949                     FreeRTOS_printf( ( "ICMPv6_recv %d (%s) from %pip to %pip end-point = %s\n",
950                                        pxICMPHeader_IPv6->ucTypeOfMessage,
951                                        pcMessageType( ( BaseType_t ) pxICMPHeader_IPv6->ucTypeOfMessage ),
952                                        ( void * ) pxICMPPacket->xIPHeader.xSourceAddress.ucBytes,
953                                        ( void * ) pxICMPPacket->xIPHeader.xDestinationAddress.ucBytes,
954                                        pcEndpointName( pxEndPoint, pcAddress, sizeof( pcAddress ) ) ) );
955                 }
956             }
957         #endif /* ( ipconfigHAS_PRINTF == 1 ) */
958 
959         if( ( pxEndPoint != NULL ) && ( pxEndPoint->bits.bIPv6 != pdFALSE_UNSIGNED ) )
960         {
961             switch( pxICMPHeader_IPv6->ucTypeOfMessage )
962             {
963                 case ipICMP_DEST_UNREACHABLE_IPv6:
964                 case ipICMP_PACKET_TOO_BIG_IPv6:
965                 case ipICMP_TIME_EXEEDED_IPv6:
966                 case ipICMP_PARAMETER_PROBLEM_IPv6:
967                     /* These message types are not implemented. They are logged here above. */
968                     break;
969 
970                 case ipICMP_PING_REQUEST_IPv6:
971                    {
972                        size_t uxICMPSize;
973                        uint16_t usICMPSize;
974 
975                        /* Lint would complain about casting '()' immediately. */
976                        usICMPSize = FreeRTOS_ntohs( pxICMPPacket->xIPHeader.usPayloadLength );
977                        uxICMPSize = ( size_t ) usICMPSize;
978                        uxNeededSize = ( size_t ) ( ipSIZE_OF_ETH_HEADER + ipSIZE_OF_IPv6_HEADER + uxICMPSize );
979 
980                        if( uxNeededSize > pxNetworkBuffer->xDataLength )
981                        {
982                            FreeRTOS_printf( ( "Too small\n" ) );
983                            break;
984                        }
985 
986                        pxICMPHeader_IPv6->ucTypeOfMessage = ipICMP_PING_REPLY_IPv6;
987                        prvReturnICMP_IPv6( pxNetworkBuffer, uxICMPSize );
988                    }
989                    break;
990 
991                     #if ( ipconfigSUPPORT_OUTGOING_PINGS != 0 )
992                         case ipICMP_PING_REPLY_IPv6:
993                            {
994                                ePingReplyStatus_t eStatus = eSuccess;
995                                /* MISRA Ref 11.3.1 [Misaligned access] */
996                                /* More details at: https://github.com/FreeRTOS/FreeRTOS-Plus-TCP/blob/main/MISRA.md#rule-113 */
997                                /* coverity[misra_c_2012_rule_11_3_violation] */
998                                const ICMPEcho_IPv6_t * pxICMPEchoHeader = ( ( const ICMPEcho_IPv6_t * ) pxICMPHeader_IPv6 );
999                                size_t uxDataLength, uxCount;
1000                                const uint8_t * pucByte;
1001 
1002                                /* Find the total length of the IP packet. */
1003                                uxDataLength = ipNUMERIC_CAST( size_t, FreeRTOS_ntohs( pxICMPPacket->xIPHeader.usPayloadLength ) );
1004                                uxDataLength = uxDataLength - sizeof( *pxICMPEchoHeader );
1005 
1006                                /* Find the first byte of the data within the ICMP packet. */
1007                                pucByte = ( const uint8_t * ) pxICMPEchoHeader;
1008                                pucByte = &( pucByte[ sizeof( *pxICMPEchoHeader ) ] );
1009 
1010                                /* Check each byte. */
1011                                for( uxCount = 0; uxCount < uxDataLength; uxCount++ )
1012                                {
1013                                    if( *pucByte != ( uint8_t ) ipECHO_DATA_FILL_BYTE )
1014                                    {
1015                                        eStatus = eInvalidData;
1016                                        break;
1017                                    }
1018 
1019                                    pucByte++;
1020                                }
1021 
1022                                /* Call back into the application to pass it the result. */
1023                                vApplicationPingReplyHook( eStatus, pxICMPEchoHeader->usIdentifier );
1024                            }
1025                            break;
1026                     #endif /* ( ipconfigSUPPORT_OUTGOING_PINGS != 0 ) */
1027                 case ipICMP_NEIGHBOR_SOLICITATION_IPv6:
1028                    {
1029                        size_t uxICMPSize;
1030                        BaseType_t xCompare;
1031                        NetworkEndPoint_t * pxEndPointFound = FreeRTOS_FindEndPointOnIP_IPv6( &( pxICMPHeader_IPv6->xIPv6Address ) );
1032                        char pcName[ 40 ];
1033                        ( void ) memset( &( pcName ), 0, sizeof( pcName ) );
1034                        FreeRTOS_printf( ( "Lookup %pip : endpoint %s\n",
1035                                           ( void * ) pxICMPHeader_IPv6->xIPv6Address.ucBytes,
1036                                           pcEndpointName( pxEndPointFound, pcName, sizeof( pcName ) ) ) );
1037 
1038                        if( pxEndPointFound != NULL )
1039                        {
1040                            pxEndPoint = pxEndPointFound;
1041                        }
1042 
1043                        pxNetworkBuffer->pxEndPoint = pxEndPoint;
1044                        pxNetworkBuffer->pxInterface = pxEndPoint->pxNetworkInterface;
1045 
1046                        uxICMPSize = sizeof( ICMPHeader_IPv6_t );
1047                        uxNeededSize = ( size_t ) ( ipSIZE_OF_ETH_HEADER + ipSIZE_OF_IPv6_HEADER + uxICMPSize );
1048 
1049                        if( uxNeededSize > pxNetworkBuffer->xDataLength )
1050                        {
1051                            FreeRTOS_printf( ( "Too small\n" ) );
1052                            break;
1053                        }
1054 
1055                        xCompare = memcmp( pxICMPHeader_IPv6->xIPv6Address.ucBytes, pxEndPoint->ipv6_settings.xIPAddress.ucBytes, ipSIZE_OF_IPv6_ADDRESS );
1056 
1057                        FreeRTOS_printf( ( "ND NS for %pip endpoint %pip %s\n",
1058                                           ( void * ) pxICMPHeader_IPv6->xIPv6Address.ucBytes,
1059                                           ( void * ) pxEndPoint->ipv6_settings.xIPAddress.ucBytes,
1060                                           ( xCompare == 0 ) ? "Reply" : "Ignore" ) );
1061 
1062                        if( xCompare == 0 )
1063                        {
1064                            pxICMPHeader_IPv6->ucTypeOfMessage = ipICMP_NEIGHBOR_ADVERTISEMENT_IPv6;
1065                            pxICMPHeader_IPv6->ucTypeOfService = 0U;
1066                            pxICMPHeader_IPv6->ulReserved = ndICMPv6_FLAG_SOLICITED | ndICMPv6_FLAG_UPDATE;
1067                            pxICMPHeader_IPv6->ulReserved = FreeRTOS_htonl( pxICMPHeader_IPv6->ulReserved );
1068 
1069                            /* Type of option. */
1070                            pxICMPHeader_IPv6->ucOptionType = ndICMP_TARGET_LINK_LAYER_ADDRESS;
1071                            /* Length of option in units of 8 bytes. */
1072                            pxICMPHeader_IPv6->ucOptionLength = 1U;
1073                            ( void ) memcpy( pxICMPHeader_IPv6->ucOptionBytes, pxEndPoint->xMACAddress.ucBytes, sizeof( MACAddress_t ) );
1074                            pxICMPPacket->xIPHeader.ucHopLimit = 255U;
1075                            ( void ) memcpy( pxICMPHeader_IPv6->xIPv6Address.ucBytes, pxEndPoint->ipv6_settings.xIPAddress.ucBytes, sizeof( pxICMPHeader_IPv6->xIPv6Address.ucBytes ) );
1076                            prvReturnICMP_IPv6( pxNetworkBuffer, uxICMPSize );
1077                        }
1078                    }
1079                    break;
1080 
1081                 case ipICMP_NEIGHBOR_ADVERTISEMENT_IPv6:
1082                     /* MISRA Ref 11.3.1 [Misaligned access] */
1083                     /* More details at: https://github.com/FreeRTOS/FreeRTOS-Plus-TCP/blob/main/MISRA.md#rule-113 */
1084                     /* coverity[misra_c_2012_rule_11_3_violation] */
1085                     vNDRefreshCacheEntry( ( ( const MACAddress_t * ) pxICMPHeader_IPv6->ucOptionBytes ),
1086                                           &( pxICMPHeader_IPv6->xIPv6Address ),
1087                                           pxEndPoint );
1088                     FreeRTOS_printf( ( "NEIGHBOR_ADV from %pip\n",
1089                                        ( void * ) pxICMPHeader_IPv6->xIPv6Address.ucBytes ) );
1090 
1091                     #if ( ipconfigUSE_RA != 0 )
1092 
1093                         /* Receive a NA ( Neighbour Advertisement ) message to see if a chosen IP-address is already in use.
1094                          * This is important during SLAAC. */
1095                         vReceiveNA( pxNetworkBuffer );
1096                     #endif
1097 
1098                     if( ( pxARPWaitingNetworkBuffer != NULL ) &&
1099                         ( uxIPHeaderSizePacket( pxARPWaitingNetworkBuffer ) == ipSIZE_OF_IPv6_HEADER ) )
1100                     {
1101                         prvCheckWaitingBuffer( &( pxICMPHeader_IPv6->xIPv6Address ) );
1102                     }
1103 
1104                     break;
1105 
1106                 case ipICMP_ROUTER_SOLICITATION_IPv6:
1107                     break;
1108 
1109                     #if ( ipconfigUSE_RA != 0 )
1110                         case ipICMP_ROUTER_ADVERTISEMENT_IPv6:
1111                             vReceiveRA( pxNetworkBuffer );
1112                             break;
1113                     #endif /* ( ipconfigUSE_RA != 0 ) */
1114 
1115                 default:
1116                     /* All possible values are included here above. */
1117                     break;
1118             } /* switch( pxICMPHeader_IPv6->ucTypeOfMessage ) */
1119         }     /* if( pxEndPoint != NULL ) */
1120 
1121         return eReleaseBuffer;
1122     }
1123 /*-----------------------------------------------------------*/
1124 
1125 /**
1126  * @brief Send out a Neighbour Advertisement message.
1127  *
1128  * @param[in] pxEndPoint The end-point to use.
1129  */
1130     /* MISRA Ref 8.9.1 [File scoped variables] */
1131     /* More details at: https://github.com/FreeRTOS/FreeRTOS-Plus-TCP/blob/main/MISRA.md#rule-89 */
1132     /* coverity[misra_c_2012_rule_8_9_violation] */
1133     /* coverity[single_use] */
FreeRTOS_OutputAdvertiseIPv6(NetworkEndPoint_t * pxEndPoint)1134     void FreeRTOS_OutputAdvertiseIPv6( NetworkEndPoint_t * pxEndPoint )
1135     {
1136         NetworkBufferDescriptor_t * pxNetworkBuffer;
1137         ICMPPacket_IPv6_t * pxICMPPacket;
1138         NetworkInterface_t * pxInterface;
1139         ICMPHeader_IPv6_t * pxICMPHeader_IPv6;
1140         size_t uxICMPSize;
1141         size_t uxPacketSize;
1142 
1143         uxPacketSize = ipSIZE_OF_ETH_HEADER + ipSIZE_OF_IPv6_HEADER + sizeof( ICMPHeader_IPv6_t );
1144 
1145         /* This is called from the context of the IP event task, so a block time
1146          * must not be used. */
1147         pxNetworkBuffer = pxGetNetworkBufferWithDescriptor( uxPacketSize, ndDONT_BLOCK );
1148 
1149         if( pxNetworkBuffer != NULL )
1150         {
1151             ( void ) memset( pxNetworkBuffer->xIPAddress.xIP_IPv6.ucBytes, 0, ipSIZE_OF_IPv6_ADDRESS );
1152             pxNetworkBuffer->pxEndPoint = pxEndPoint;
1153 
1154             pxInterface = pxEndPoint->pxNetworkInterface;
1155 
1156             configASSERT( pxInterface != NULL );
1157 
1158             /* MISRA Ref 11.3.1 [Misaligned access] */
1159             /* More details at: https://github.com/FreeRTOS/FreeRTOS-Plus-TCP/blob/main/MISRA.md#rule-113 */
1160             /* coverity[misra_c_2012_rule_11_3_violation] */
1161             pxICMPPacket = ( ( ICMPPacket_IPv6_t * ) pxNetworkBuffer->pucEthernetBuffer );
1162             pxICMPHeader_IPv6 = ( ( ICMPHeader_IPv6_t * ) &( pxICMPPacket->xICMPHeaderIPv6 ) );
1163 
1164             ( void ) memcpy( pxICMPPacket->xEthernetHeader.xDestinationAddress.ucBytes, pcLOCAL_ALL_NODES_MULTICAST_MAC, ipMAC_ADDRESS_LENGTH_BYTES );
1165             ( void ) memcpy( pxICMPPacket->xEthernetHeader.xSourceAddress.ucBytes, pxEndPoint->xMACAddress.ucBytes, ipMAC_ADDRESS_LENGTH_BYTES );
1166             pxICMPPacket->xEthernetHeader.usFrameType = ipIPv6_FRAME_TYPE; /* 12 + 2 = 14 */
1167 
1168             pxICMPPacket->xIPHeader.ucVersionTrafficClass = 0x60;
1169             pxICMPPacket->xIPHeader.ucTrafficClassFlow = 0;
1170             pxICMPPacket->xIPHeader.usFlowLabel = 0;
1171 
1172             pxICMPPacket->xIPHeader.usPayloadLength = FreeRTOS_htons( sizeof( ICMPHeader_IPv6_t ) );
1173             pxICMPPacket->xIPHeader.ucNextHeader = ipPROTOCOL_ICMP_IPv6;
1174             pxICMPPacket->xIPHeader.ucHopLimit = 255;
1175             ( void ) memcpy( pxICMPPacket->xIPHeader.xSourceAddress.ucBytes, pxEndPoint->ipv6_settings.xIPAddress.ucBytes, ipSIZE_OF_IPv6_ADDRESS );
1176             ( void ) memcpy( pxICMPPacket->xIPHeader.xDestinationAddress.ucBytes, pcLOCAL_ALL_NODES_MULTICAST_IP, ipSIZE_OF_IPv6_ADDRESS );
1177 
1178             uxICMPSize = sizeof( ICMPHeader_IPv6_t );
1179             pxICMPHeader_IPv6->ucTypeOfMessage = ipICMP_NEIGHBOR_ADVERTISEMENT_IPv6;
1180             pxICMPHeader_IPv6->ucTypeOfService = 0;
1181             pxICMPHeader_IPv6->ulReserved = ndICMPv6_FLAG_SOLICITED | ndICMPv6_FLAG_UPDATE;
1182             pxICMPHeader_IPv6->ulReserved = FreeRTOS_htonl( pxICMPHeader_IPv6->ulReserved );
1183 
1184             /* Type of option. */
1185             pxICMPHeader_IPv6->ucOptionType = ndICMP_TARGET_LINK_LAYER_ADDRESS;
1186             /* Length of option in units of 8 bytes. */
1187             pxICMPHeader_IPv6->ucOptionLength = 1;
1188             ( void ) memcpy( pxICMPHeader_IPv6->ucOptionBytes, pxEndPoint->xMACAddress.ucBytes, sizeof( MACAddress_t ) );
1189             pxICMPPacket->xIPHeader.ucHopLimit = 255;
1190             ( void ) memcpy( pxICMPHeader_IPv6->xIPv6Address.ucBytes, pxEndPoint->ipv6_settings.xIPAddress.ucBytes, sizeof( pxICMPHeader_IPv6->xIPv6Address.ucBytes ) );
1191 
1192             /* Important: tell NIC driver how many bytes must be sent */
1193             pxNetworkBuffer->xDataLength = ( size_t ) ( ipSIZE_OF_ETH_HEADER + ipSIZE_OF_IPv6_HEADER + uxICMPSize );
1194 
1195             #if ( ipconfigDRIVER_INCLUDED_TX_IP_CHECKSUM == 0 )
1196                 {
1197                     /* calculate the ICMPv6 checksum for outgoing package */
1198                     ( void ) usGenerateProtocolChecksum( pxNetworkBuffer->pucEthernetBuffer, pxNetworkBuffer->xDataLength, pdTRUE );
1199                 }
1200             #else
1201                 {
1202                     /* Many EMAC peripherals will only calculate the ICMP checksum
1203                      * correctly if the field is nulled beforehand. */
1204                     pxICMPHeader_IPv6->usChecksum = 0;
1205                 }
1206             #endif
1207 
1208             /* Set the parameter 'bReleaseAfterSend'. */
1209             ( void ) pxInterface->pfOutput( pxInterface, pxNetworkBuffer, pdTRUE );
1210         }
1211     }
1212 /*-----------------------------------------------------------*/
1213 
1214 /**
1215  * @brief Create an IPv16 address, based on a prefix.
1216  *
1217  * @param[out] pxIPAddress The location where the new IPv6 address will be stored.
1218  * @param[in] pxPrefix The prefix to be used.
1219  * @param[in] uxPrefixLength The length of the prefix.
1220  * @param[in] xDoRandom A non-zero value if the bits after the prefix should have a random value.
1221  *
1222  * @return pdPASS if the operation was successful. Or pdFAIL in case xApplicationGetRandomNumber()
1223  *         returned an error.
1224  */
FreeRTOS_CreateIPv6Address(IPv6_Address_t * pxIPAddress,const IPv6_Address_t * pxPrefix,size_t uxPrefixLength,BaseType_t xDoRandom)1225     BaseType_t FreeRTOS_CreateIPv6Address( IPv6_Address_t * pxIPAddress,
1226                                            const IPv6_Address_t * pxPrefix,
1227                                            size_t uxPrefixLength,
1228                                            BaseType_t xDoRandom )
1229     {
1230         uint32_t pulRandom[ 4 ];
1231         uint8_t * pucSource;
1232         BaseType_t xIndex, xResult = pdPASS;
1233 
1234         if( xDoRandom != pdFALSE )
1235         {
1236             /* Create an IP-address, based on a net prefix and a
1237              * random host address.
1238              * ARRAY_SIZE_X() returns the size of an array as a
1239              * signed value ( BaseType_t ).
1240              */
1241             for( xIndex = 0; xIndex < ARRAY_SIZE_X( pulRandom ); xIndex++ )
1242             {
1243                 if( xApplicationGetRandomNumber( &( pulRandom[ xIndex ] ) ) == pdFAIL )
1244                 {
1245                     xResult = pdFAIL;
1246                     break;
1247                 }
1248             }
1249         }
1250         else
1251         {
1252             ( void ) memset( pulRandom, 0, sizeof( pulRandom ) );
1253         }
1254 
1255         if( xResult == pdPASS )
1256         {
1257             configASSERT( ( uxPrefixLength > 0U ) && ( uxPrefixLength < ( 8U * ipSIZE_OF_IPv6_ADDRESS ) ) );
1258 
1259             if( uxPrefixLength >= 8U )
1260             {
1261                 ( void ) memcpy( pxIPAddress->ucBytes, pxPrefix->ucBytes, ( uxPrefixLength + 7U ) / 8U );
1262             }
1263 
1264             pucSource = ( uint8_t * ) pulRandom;
1265             size_t uxIndex = uxPrefixLength / 8U;
1266 
1267             if( ( uxPrefixLength % 8U ) != 0U )
1268             {
1269                 /* uxHostLen is between 1 and 7 bits long. */
1270                 size_t uxHostLen = 8U - ( uxPrefixLength % 8U );
1271                 uint32_t uxHostMask = ( ( ( uint32_t ) 1U ) << uxHostLen ) - 1U;
1272                 uint8_t ucNetMask = ( uint8_t ) ~( uxHostMask );
1273 
1274                 pxIPAddress->ucBytes[ uxIndex ] &= ucNetMask;
1275                 pxIPAddress->ucBytes[ uxIndex ] |= ( pucSource[ 0 ] & ( ( uint8_t ) uxHostMask ) );
1276                 pucSource = &( pucSource[ 1 ] );
1277                 uxIndex++;
1278             }
1279 
1280             if( uxIndex < ipSIZE_OF_IPv6_ADDRESS )
1281             {
1282                 ( void ) memcpy( &( pxIPAddress->ucBytes[ uxIndex ] ), pucSource, ipSIZE_OF_IPv6_ADDRESS - uxIndex );
1283             }
1284         }
1285 
1286         return xResult;
1287     }
1288 /*-----------------------------------------------------------*/
1289 #endif /* ipconfigUSE_IPv6 */
1290