xref: /FreeRTOS-Plus-TCP-v4.0.0/source/FreeRTOS_RA.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_RA.c
28  * @brief A client implementation of Router advertisement protocol.
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_IP_Timers.h"
45 #include "FreeRTOS_ARP.h"
46 #include "FreeRTOS_UDP_IP.h"
47 #include "FreeRTOS_Routing.h"
48 #include "FreeRTOS_ND.h"
49 #if ( ipconfigUSE_LLMNR == 1 )
50     #include "FreeRTOS_DNS.h"
51 #endif /* ipconfigUSE_LLMNR */
52 #include "NetworkBufferManagement.h"
53 
54 /* This define may exclude the entire source file. */
55 #if ( ipconfigUSE_IPv6 != 0 ) && ( ipconfigUSE_RA != 0 )
56 
57 /*-----------------------------------------------------------*/
58 
59 /** A block time of 0 simply means "don't block". */
60     #define raDONT_BLOCK                       ( ( TickType_t ) 0 )
61 
62 /** The default value for the IPv6-field 'ucVersionTrafficClass'. */
63     #define raDEFAULT_VERSION_TRAFFIC_CLASS    0x60U
64 
65 /** The default value for the IPv6-field 'ucHopLimit'. */
66     #define raDEFAULT_HOP_LIMIT                255U
67 
68 /*-----------------------------------------------------------*/
69 
70 /* Initialise the Router Advertisement process for a given end-point. */
71     static void vRAProcessInit( NetworkEndPoint_t * pxEndPoint );
72 
73 /* Find a link-local address that is bound to a given interface. */
74     static BaseType_t xGetLinkLocalAddress( const NetworkInterface_t * pxInterface,
75                                             IPv6_Address_t * pxAddress );
76 
77 /* Read the reply received from the RA server. */
78     static ICMPPrefixOption_IPv6_t * vReceiveRA_ReadReply( const NetworkBufferDescriptor_t * pxNetworkBuffer );
79 
80 /* Handle the states that are limited by a timer. See if any of the timers has expired. */
81     static TickType_t xRAProcess_HandleWaitStates( NetworkEndPoint_t * pxEndPoint,
82                                                    TickType_t uxReloadTime );
83 
84 /* Handle the other states. */
85     static TickType_t xRAProcess_HandleOtherStates( NetworkEndPoint_t * pxEndPoint,
86                                                     TickType_t uxReloadTime );
87 
88 
89 /*-----------------------------------------------------------*/
90 
91 /**
92  * @brief Find a link-local address that is bound to a given interface.
93  *
94  * @param[in] pxInterface The interface for which a link-local address is looked up.
95  * @param[out] pxAddress The IP address will be copied to this parameter.
96  *
97  * @return pdPASS in case a link-local address was found, otherwise pdFAIL.
98  */
xGetLinkLocalAddress(const NetworkInterface_t * pxInterface,IPv6_Address_t * pxAddress)99     static BaseType_t xGetLinkLocalAddress( const NetworkInterface_t * pxInterface,
100                                             IPv6_Address_t * pxAddress )
101     {
102         BaseType_t xResult = pdFAIL;
103         NetworkEndPoint_t * pxEndPoint;
104 
105         for( pxEndPoint = FreeRTOS_FirstEndPoint( pxInterface );
106              pxEndPoint != NULL;
107              pxEndPoint = FreeRTOS_NextEndPoint( pxInterface, pxEndPoint ) )
108         {
109             /* Check if it has the link-local prefix FE80::/10 */
110             if( ( pxEndPoint->ipv6_settings.xIPAddress.ucBytes[ 0 ] == 0xfeU ) &&
111                 ( ( pxEndPoint->ipv6_settings.xIPAddress.ucBytes[ 1 ] & 0xc0U ) == 0x80U ) )
112             {
113                 ( void ) memcpy( pxAddress->ucBytes, pxEndPoint->ipv6_settings.xIPAddress.ucBytes, ipSIZE_OF_IPv6_ADDRESS );
114                 xResult = pdPASS;
115                 break;
116             }
117         }
118 
119         return xResult;
120     }
121 /*-----------------------------------------------------------*/
122 
123 /**
124  * @brief Send an ICMPv6 message of the type: Router Solicitation.
125  *
126  * @param[in] pxNetworkBuffer The network buffer which can be used for this.
127  * @param[in] pxIPAddress The target address, normally ff02::2
128  *
129  */
vNDSendRouterSolicitation(NetworkBufferDescriptor_t * pxNetworkBuffer,IPv6_Address_t * pxIPAddress)130     void vNDSendRouterSolicitation( NetworkBufferDescriptor_t * pxNetworkBuffer,
131                                     IPv6_Address_t * pxIPAddress )
132     {
133         ICMPPacket_IPv6_t * pxICMPPacket;
134         ICMPRouterSolicitation_IPv6_t * xRASolicitationRequest;
135         const NetworkEndPoint_t * pxEndPoint = pxNetworkBuffer->pxEndPoint;
136         const size_t uxNeededSize = ipSIZE_OF_ETH_HEADER + ipSIZE_OF_IPv6_HEADER + sizeof( ICMPRouterSolicitation_IPv6_t );
137         MACAddress_t xMultiCastMacAddress;
138         NetworkBufferDescriptor_t * pxDescriptor = pxNetworkBuffer;
139         IPv6_Address_t xSourceAddress;
140         BaseType_t xHasLocal;
141         NetworkBufferDescriptor_t * pxNewDescriptor = NULL;
142 
143         configASSERT( pxEndPoint != NULL );
144         configASSERT( pxEndPoint->bits.bIPv6 != pdFALSE_UNSIGNED );
145 
146         xHasLocal = xGetLinkLocalAddress( pxEndPoint->pxNetworkInterface, &( xSourceAddress ) );
147 
148         if( xHasLocal == pdFAIL )
149         {
150             FreeRTOS_printf( ( "RA: can not find a Link-local address\n" ) );
151             ( void ) memset( xSourceAddress.ucBytes, 0, ipSIZE_OF_IPv6_ADDRESS );
152         }
153         else
154         {
155             FreeRTOS_printf( ( "RA: source %pip\n", ( void * ) xSourceAddress.ucBytes ) );
156         }
157 
158         if( pxDescriptor->xDataLength < uxNeededSize )
159         {
160             pxNewDescriptor = pxDuplicateNetworkBufferWithDescriptor( pxDescriptor, uxNeededSize );
161             vReleaseNetworkBufferAndDescriptor( pxDescriptor );
162             pxDescriptor = pxNewDescriptor;
163         }
164 
165         if( pxDescriptor != NULL )
166         {
167             /* MISRA Ref 11.3.1 [Misaligned access] */
168             /* More details at: https://github.com/FreeRTOS/FreeRTOS-Plus-TCP/blob/main/MISRA.md#rule-113 */
169             /* coverity[misra_c_2012_rule_11_3_violation] */
170             pxICMPPacket = ( ( ICMPPacket_IPv6_t * ) pxDescriptor->pucEthernetBuffer );
171             xRASolicitationRequest = ( ( ICMPRouterSolicitation_IPv6_t * ) &( pxICMPPacket->xICMPHeaderIPv6 ) );
172 
173             pxDescriptor->xDataLength = uxNeededSize;
174 
175             ( void ) eNDGetCacheEntry( pxIPAddress, &( xMultiCastMacAddress ), NULL );
176 
177             /* Set Ethernet header. Will be swapped. */
178             ( void ) memcpy( pxICMPPacket->xEthernetHeader.xSourceAddress.ucBytes, xMultiCastMacAddress.ucBytes, ipMAC_ADDRESS_LENGTH_BYTES );
179             ( void ) memcpy( pxICMPPacket->xEthernetHeader.xDestinationAddress.ucBytes, pxEndPoint->xMACAddress.ucBytes, ipMAC_ADDRESS_LENGTH_BYTES );
180             pxICMPPacket->xEthernetHeader.usFrameType = ipIPv6_FRAME_TYPE;
181 
182             /* Set IP-header. */
183             pxICMPPacket->xIPHeader.ucVersionTrafficClass = raDEFAULT_VERSION_TRAFFIC_CLASS;
184             pxICMPPacket->xIPHeader.ucTrafficClassFlow = 0U;
185             pxICMPPacket->xIPHeader.usFlowLabel = 0U;
186             pxICMPPacket->xIPHeader.usPayloadLength = FreeRTOS_htons( sizeof( ICMPRouterSolicitation_IPv6_t ) );
187             pxICMPPacket->xIPHeader.ucNextHeader = ipPROTOCOL_ICMP_IPv6;
188             pxICMPPacket->xIPHeader.ucHopLimit = raDEFAULT_HOP_LIMIT;
189 
190             /* Normally, the source address is set as 'ipv6_settings.xIPAddress'.
191              * But is some routers will not accept a public IP-address, the original
192              * default address will be used. It must be a link-local address. */
193             ( void ) memcpy( pxICMPPacket->xIPHeader.xSourceAddress.ucBytes, xSourceAddress.ucBytes, ipSIZE_OF_IPv6_ADDRESS );
194 
195             ( void ) memcpy( pxICMPPacket->xIPHeader.xDestinationAddress.ucBytes, pxIPAddress->ucBytes, ipSIZE_OF_IPv6_ADDRESS );
196 
197             /* Set ICMP header. */
198             ( void ) memset( xRASolicitationRequest, 0, sizeof( *xRASolicitationRequest ) );
199             xRASolicitationRequest->ucTypeOfMessage = ipICMP_ROUTER_SOLICITATION_IPv6;
200 
201             /*  __XX__ revisit on why commented out
202              *  xRASolicitationRequest->ucOptionType = ndICMP_SOURCE_LINK_LAYER_ADDRESS;
203              *  xRASolicitationRequest->ucOptionLength = 1;
204              *  ( void ) memcpy( xRASolicitationRequest->ucOptionBytes, pxEndPoint->xMACAddress.ucBytes, ipMAC_ADDRESS_LENGTH_BYTES );
205              */
206 
207             /* Checksums. */
208             #if ( ipconfigDRIVER_INCLUDED_TX_IP_CHECKSUM == 0 )
209                 {
210                     /* calculate the ICMPv6 checksum for outgoing package */
211                     ( void ) usGenerateProtocolChecksum( pxDescriptor->pucEthernetBuffer, pxDescriptor->xDataLength, pdTRUE );
212                 }
213             #else
214                 {
215                     /* Many EMAC peripherals will only calculate the ICMP checksum
216                      * correctly if the field is nulled beforehand. */
217                     xRASolicitationRequest->usChecksum = 0U;
218                 }
219             #endif
220 
221             /* This function will fill in the eth addresses and send the packet */
222             vReturnEthernetFrame( pxDescriptor, pdTRUE );
223         }
224     }
225 /*-----------------------------------------------------------*/
226 
227 /**
228  * @brief Receive a NA ( Neighbour Advertisement ) message to see if a chosen IP-address is already in use.
229  *
230  * @param[in] pxNetworkBuffer The buffer that contains the message.
231  */
vReceiveNA(const NetworkBufferDescriptor_t * pxNetworkBuffer)232     void vReceiveNA( const NetworkBufferDescriptor_t * pxNetworkBuffer )
233     {
234         const NetworkInterface_t * pxInterface = pxNetworkBuffer->pxInterface;
235         NetworkEndPoint_t * pxPoint;
236 
237         /* MISRA Ref 11.3.1 [Misaligned access] */
238         /* More details at: https://github.com/FreeRTOS/FreeRTOS-Plus-TCP/blob/main/MISRA.md#rule-113 */
239         /* coverity[misra_c_2012_rule_11_3_violation] */
240         const ICMPPacket_IPv6_t * pxICMPPacket = ( ( const ICMPPacket_IPv6_t * ) pxNetworkBuffer->pucEthernetBuffer );
241         const ICMPHeader_IPv6_t * pxICMPHeader_IPv6 = ( ( const ICMPHeader_IPv6_t * ) &( pxICMPPacket->xICMPHeaderIPv6 ) );
242 
243         for( pxPoint = FreeRTOS_FirstEndPoint( pxInterface );
244              pxPoint != NULL;
245              pxPoint = FreeRTOS_NextEndPoint( pxInterface, pxPoint ) )
246         {
247             if( ( pxPoint->bits.bWantRA != pdFALSE_UNSIGNED ) && ( pxPoint->xRAData.eRAState == eRAStateIPWait ) )
248             {
249                 if( memcmp( pxPoint->ipv6_settings.xIPAddress.ucBytes, pxICMPHeader_IPv6->xIPv6Address.ucBytes, ipSIZE_OF_IPv6_ADDRESS ) == 0 )
250                 {
251                     pxPoint->xRAData.bits.bIPAddressInUse = pdTRUE_UNSIGNED;
252                     vDHCP_RATimerReload( pxPoint, 100U );
253                 }
254             }
255         }
256     }
257 /*-----------------------------------------------------------*/
258 
259 /**
260  * @brief Read a received RA reply and return the prefix option from the packet.
261  *
262  * @param[in] pxNetworkBuffer The buffer that contains the message.
263  *
264  * @returns Returns the ICMP prefix option pointer, pointing to its location in the
265  *          input RA reply message buffer.
266  */
vReceiveRA_ReadReply(const NetworkBufferDescriptor_t * pxNetworkBuffer)267     static ICMPPrefixOption_IPv6_t * vReceiveRA_ReadReply( const NetworkBufferDescriptor_t * pxNetworkBuffer )
268     {
269         size_t uxIndex = 0U;
270         const size_t uxICMPSize = sizeof( ICMPRouterAdvertisement_IPv6_t );
271         const size_t uxNeededSize = ipSIZE_OF_ETH_HEADER + ipSIZE_OF_IPv6_HEADER + uxICMPSize;
272         /* uxLast points to the first byte after the buffer. */
273         const size_t uxLast = pxNetworkBuffer->xDataLength - uxNeededSize;
274         uint8_t * pucBytes = &( pxNetworkBuffer->pucEthernetBuffer[ uxNeededSize ] );
275         ICMPPrefixOption_IPv6_t * pxPrefixOption = NULL;
276 
277         while( ( uxIndex + 1U ) < uxLast )
278         {
279             uint8_t ucType = pucBytes[ uxIndex ];
280             size_t uxPrefixLength = ( size_t ) pucBytes[ uxIndex + 1U ];
281             size_t uxLength = uxPrefixLength * 8U;
282 
283             if( uxPrefixLength == 0U )
284             {
285                 /* According to RFC 4861, length of the option value 0 is invalid. Hence returning from here */
286                 FreeRTOS_printf( ( "RA: Invalid length of the option value as zero. " ) );
287                 break;
288             }
289 
290             if( uxLast < ( uxIndex + uxLength ) )
291             {
292                 FreeRTOS_printf( ( "RA: Not enough bytes ( %u > %u )\n", ( unsigned ) ( uxIndex + uxLength ), ( unsigned ) uxLast ) );
293                 break;
294             }
295 
296             switch( ucType )
297             {
298                 case ndICMP_SOURCE_LINK_LAYER_ADDRESS: /* 1 */
299                     FreeRTOS_printf( ( "RA: Source = %02x-%02x-%02x-%02x-%02x-%02x\n",
300                                        pucBytes[ uxIndex + 2U ],
301                                        pucBytes[ uxIndex + 3U ],
302                                        pucBytes[ uxIndex + 4U ],
303                                        pucBytes[ uxIndex + 5U ],
304                                        pucBytes[ uxIndex + 6U ],
305                                        pucBytes[ uxIndex + 7U ] ) );
306                     break;
307 
308                 case ndICMP_TARGET_LINK_LAYER_ADDRESS: /* 2 */
309                     break;
310 
311                 case ndICMP_PREFIX_INFORMATION: /* 3 */
312                     /* MISRA Ref 11.3.1 [Misaligned access] */
313                     /* More details at: https://github.com/FreeRTOS/FreeRTOS-Plus-TCP/blob/main/MISRA.md#rule-113 */
314                     /* coverity[misra_c_2012_rule_11_3_violation] */
315                     pxPrefixOption = ( ( ICMPPrefixOption_IPv6_t * ) &( pucBytes[ uxIndex ] ) );
316 
317                     FreeRTOS_printf( ( "RA: Prefix len %d Life %u, %u (%pip)\n",
318                                        pxPrefixOption->ucPrefixLength,
319                                        FreeRTOS_ntohl( pxPrefixOption->ulValidLifeTime ),
320                                        FreeRTOS_ntohl( pxPrefixOption->ulPreferredLifeTime ),
321                                        ( void * ) pxPrefixOption->ucPrefix ) );
322                     break;
323 
324                 case ndICMP_REDIRECTED_HEADER: /* 4 */
325                     break;
326 
327                 case ndICMP_MTU_OPTION: /* 5 */
328                    {
329                        uint32_t ulMTU;
330                        ( void ) ulMTU;
331 
332                        /* ulChar2u32 returns host-endian numbers. */
333                        ulMTU = ulChar2u32( &( pucBytes[ uxIndex + 4U ] ) );
334                        FreeRTOS_printf( ( "RA: MTU = %u\n", ( unsigned int ) ulMTU ) );
335                    }
336                    break;
337 
338                 default:
339                     FreeRTOS_printf( ( "RA: Type 0x%02x not implemented\n", ucType ) );
340                     break;
341             }
342 
343             uxIndex = uxIndex + uxLength;
344         } /* while( ( uxIndex + 1 ) < uxLast ) */
345 
346         return pxPrefixOption;
347     }
348 /*-----------------------------------------------------------*/
349 
350 /**
351  * @brief Receive and analyse a RA ( Router Advertisement ) message.
352  *        If the reply is satisfactory, the end-point will do SLAAC: choose an IP-address using the
353  *        prefix offered, and completed with random bits.  It will start testing if another device
354  *        already exists that uses the same IP-address.
355  *
356  * @param[in] pxNetworkBuffer The buffer that contains the message.
357  */
vReceiveRA(const NetworkBufferDescriptor_t * pxNetworkBuffer)358     void vReceiveRA( const NetworkBufferDescriptor_t * pxNetworkBuffer )
359     {
360         /* MISRA Ref 11.3.1 [Misaligned access] */
361         /* More details at: https://github.com/FreeRTOS/FreeRTOS-Plus-TCP/blob/main/MISRA.md#rule-113 */
362         /* coverity[misra_c_2012_rule_11_3_violation] */
363         const ICMPPacket_IPv6_t * pxICMPPacket = ( ( const ICMPPacket_IPv6_t * ) pxNetworkBuffer->pucEthernetBuffer );
364         const ICMPPrefixOption_IPv6_t * pxPrefixOption = NULL;
365         const size_t uxICMPSize = sizeof( ICMPRouterAdvertisement_IPv6_t );
366         const size_t uxNeededSize = ipSIZE_OF_ETH_HEADER + ipSIZE_OF_IPv6_HEADER + uxICMPSize;
367 
368         /* A Router Advertisement was received, handle it here. */
369         if( uxNeededSize > pxNetworkBuffer->xDataLength )
370         {
371             FreeRTOS_printf( ( "vReceiveRA: The buffer provided is too small\n" ) );
372         }
373         else
374         {
375             /* MISRA Ref 11.3.1 [Misaligned access] */
376             /* More details at: https://github.com/FreeRTOS/FreeRTOS-Plus-TCP/blob/main/MISRA.md#rule-113 */
377             /* coverity[misra_c_2012_rule_11_3_violation] */
378             const ICMPRouterAdvertisement_IPv6_t * pxAdvertisement = ( ( const ICMPRouterAdvertisement_IPv6_t * ) &( pxICMPPacket->xICMPHeaderIPv6 ) );
379             FreeRTOS_printf( ( "RA: Type %02x Srv %02x Checksum %04x Hops %d Flags %02x Life %d\n",
380                                pxAdvertisement->ucTypeOfMessage,
381                                pxAdvertisement->ucTypeOfService,
382                                FreeRTOS_ntohs( pxAdvertisement->usChecksum ),
383                                pxAdvertisement->ucHopLimit,
384                                pxAdvertisement->ucFlags,
385                                FreeRTOS_ntohs( pxAdvertisement->usLifetime ) ) );
386 
387             if( pxAdvertisement->usLifetime != 0U )
388             {
389                 pxPrefixOption = vReceiveRA_ReadReply( pxNetworkBuffer );
390 
391                 configASSERT( pxNetworkBuffer->pxInterface != NULL );
392 
393                 if( pxPrefixOption != NULL )
394                 {
395                     NetworkEndPoint_t * pxEndPoint;
396 
397                     for( pxEndPoint = FreeRTOS_FirstEndPoint( pxNetworkBuffer->pxInterface );
398                          pxEndPoint != NULL;
399                          pxEndPoint = FreeRTOS_NextEndPoint( pxNetworkBuffer->pxInterface, pxEndPoint ) )
400                     {
401                         if( ( pxEndPoint->bits.bWantRA != pdFALSE_UNSIGNED ) && ( pxEndPoint->xRAData.eRAState == eRAStateWait ) )
402                         {
403                             pxEndPoint->ipv6_settings.uxPrefixLength = pxPrefixOption->ucPrefixLength;
404                             ( void ) memcpy( pxEndPoint->ipv6_settings.xPrefix.ucBytes, pxPrefixOption->ucPrefix, ipSIZE_OF_IPv6_ADDRESS );
405                             ( void ) memcpy( pxEndPoint->ipv6_settings.xGatewayAddress.ucBytes, pxICMPPacket->xIPHeader.xSourceAddress.ucBytes, ipSIZE_OF_IPv6_ADDRESS );
406 
407                             pxEndPoint->xRAData.bits.bRouterReplied = pdTRUE_UNSIGNED;
408                             pxEndPoint->xRAData.uxRetryCount = 0U;
409                             pxEndPoint->xRAData.ulPreferredLifeTime = FreeRTOS_ntohl( pxPrefixOption->ulPreferredLifeTime );
410                             /* Force taking a new random IP-address. */
411                             pxEndPoint->xRAData.bits.bIPAddressInUse = pdTRUE_UNSIGNED;
412                             pxEndPoint->xRAData.eRAState = eRAStateIPTest;
413                             vRAProcess( pdFALSE, pxEndPoint );
414                         }
415                     }
416                 }
417             }
418             else
419             {
420                 /* The life-time field contains zero. */
421             }
422         }
423     }
424 /*-----------------------------------------------------------*/
425 
426 /**
427  * @brief Handles the RA wait state and calculates the new timer reload value
428  *        based on the wait state. Also checks if any timer has expired. If its found that
429  *        there is no other device using the same IP-address vIPNetworkUpCalls() is called
430  *        to send the network up event.
431  *
432  * @param[in] pxEndPoint The end point for which RA assignment is required.
433  * @param[out] uxReloadTime Timer reload value in ticks.
434  *
435  * @return New timer reload value.
436  */
xRAProcess_HandleWaitStates(NetworkEndPoint_t * pxEndPoint,TickType_t uxReloadTime)437     static TickType_t xRAProcess_HandleWaitStates( NetworkEndPoint_t * pxEndPoint,
438                                                    TickType_t uxReloadTime )
439     {
440         TickType_t uxNewReloadTime = uxReloadTime;
441 
442         if( pxEndPoint->xRAData.eRAState == eRAStateWait )
443         {
444             /* A Router Solicitation has been sent, waited for a reply, but no came.
445              * All replies will be handled in the function vReceiveRA(). */
446             pxEndPoint->xRAData.uxRetryCount++;
447 
448             if( pxEndPoint->xRAData.uxRetryCount < ( UBaseType_t ) ipconfigRA_SEARCH_COUNT )
449             {
450                 pxEndPoint->xRAData.eRAState = eRAStateApply;
451             }
452             else
453             {
454                 FreeRTOS_printf( ( "RA: Giving up waiting for a Router.\n" ) );
455                 ( void ) memcpy( &( pxEndPoint->ipv6_settings ), &( pxEndPoint->ipv6_defaults ), sizeof( pxEndPoint->ipv6_settings ) );
456 
457                 pxEndPoint->xRAData.bits.bRouterReplied = pdFALSE_UNSIGNED;
458                 pxEndPoint->xRAData.uxRetryCount = 0U;
459                 /* Force taking a new random IP-address. */
460                 pxEndPoint->xRAData.bits.bIPAddressInUse = pdTRUE_UNSIGNED;
461                 pxEndPoint->xRAData.eRAState = eRAStateIPTest;
462             }
463         }
464         else if( pxEndPoint->xRAData.eRAState == eRAStateIPWait )
465         {
466             /* A Neighbour Solicitation has been sent, waited for a reply.
467              * Repeat this 'ipconfigRA_IP_TEST_COUNT' times to be sure. */
468             if( pxEndPoint->xRAData.bits.bIPAddressInUse != pdFALSE_UNSIGNED )
469             {
470                 /* Another device has responded with the same IPv4 address. */
471                 pxEndPoint->xRAData.uxRetryCount = 0U;
472                 pxEndPoint->xRAData.eRAState = eRAStateIPTest;
473                 uxNewReloadTime = pdMS_TO_TICKS( ipconfigRA_IP_TEST_TIME_OUT_MSEC );
474             }
475             else if( pxEndPoint->xRAData.uxRetryCount < ( UBaseType_t ) ipconfigRA_IP_TEST_COUNT )
476             {
477                 /* Try again. */
478                 pxEndPoint->xRAData.uxRetryCount++;
479                 pxEndPoint->xRAData.eRAState = eRAStateIPTest;
480                 uxNewReloadTime = pdMS_TO_TICKS( ipconfigRA_IP_TEST_TIME_OUT_MSEC );
481             }
482             else
483             {
484                 /* Now it is assumed that there is no other device using the same IP-address. */
485                 if( pxEndPoint->xRAData.bits.bRouterReplied != pdFALSE_UNSIGNED )
486                 {
487                     /* Obtained configuration from a router. */
488                     uxNewReloadTime = pdMS_TO_TICKS( 1000U * pxEndPoint->xRAData.ulPreferredLifeTime );
489                     pxEndPoint->xRAData.eRAState = eRAStatePreLease;
490                     iptraceRA_SUCCEDEED( &( pxEndPoint->ipv6_settings.xIPAddress ) );
491                     FreeRTOS_printf( ( "RA: succeeded, using IP address %pip Reload after %u seconds\n",
492                                        ( void * ) pxEndPoint->ipv6_settings.xIPAddress.ucBytes,
493                                        ( unsigned ) pxEndPoint->xRAData.ulPreferredLifeTime ) );
494                 }
495                 else
496                 {
497                     /* Using the default network parameters. */
498                     pxEndPoint->xRAData.eRAState = eRAStateFailed;
499 
500                     iptraceRA_REQUESTS_FAILED_USING_DEFAULT_IP_ADDRESS( &( pxEndPoint->ipv6_settings.xIPAddress ) );
501 
502                     FreeRTOS_printf( ( "RA: failed, using default parameters and IP address %pip\n", ( void * ) pxEndPoint->ipv6_settings.xIPAddress.ucBytes ) );
503                     /* Disable the timer. */
504                     uxNewReloadTime = 0U;
505                 }
506 
507                 /* Now call vIPNetworkUpCalls() to send the network-up event and
508                  * start the ARP timer. */
509                 vIPNetworkUpCalls( pxEndPoint );
510             }
511         }
512         else
513         {
514             /* Do nothing */
515         }
516 
517         return uxNewReloadTime;
518     }
519 /*-----------------------------------------------------------*/
520 
521 /**
522  * @brief Handles the RA states other than the wait states.
523  *
524  * @param[in] pxEndPoint The end point for which RA assignment is required.
525  * @param[out] uxReloadTime Timer reload value in ticks.
526  *
527  * @return New timer reload value.
528  */
xRAProcess_HandleOtherStates(NetworkEndPoint_t * pxEndPoint,TickType_t uxReloadTime)529     static TickType_t xRAProcess_HandleOtherStates( NetworkEndPoint_t * pxEndPoint,
530                                                     TickType_t uxReloadTime )
531     {
532         TickType_t uxNewReloadTime = uxReloadTime;
533 
534         switch( pxEndPoint->xRAData.eRAState )
535         {
536             case eRAStateApply:
537                {
538                    IPv6_Address_t xIPAddress;
539                    size_t uxNeededSize;
540                    NetworkBufferDescriptor_t * pxNetworkBuffer;
541 
542                    /* Send a Router Solicitation to ff02::2 */
543                    ( void ) memset( xIPAddress.ucBytes, 0, sizeof( xIPAddress.ucBytes ) );
544                    xIPAddress.ucBytes[ 0 ] = 0xffU;
545                    xIPAddress.ucBytes[ 1 ] = 0x02U;
546                    xIPAddress.ucBytes[ 15 ] = 0x02U;
547                    uxNeededSize = ipSIZE_OF_ETH_HEADER + ipSIZE_OF_IPv6_HEADER + sizeof( ICMPRouterSolicitation_IPv6_t );
548                    pxNetworkBuffer = pxGetNetworkBufferWithDescriptor( uxNeededSize, raDONT_BLOCK );
549 
550                    if( pxNetworkBuffer != NULL )
551                    {
552                        pxNetworkBuffer->pxEndPoint = pxEndPoint;
553                        vNDSendRouterSolicitation( pxNetworkBuffer, &( xIPAddress ) );
554                    }
555 
556                    FreeRTOS_printf( ( "vRAProcess: Router Solicitation, attempt %lu/%u\n",
557                                       pxEndPoint->xRAData.uxRetryCount + 1U,
558                                       ipconfigRA_SEARCH_COUNT ) );
559                    /* Wait a configurable time for a router advertisement. */
560                    uxNewReloadTime = pdMS_TO_TICKS( ipconfigRA_SEARCH_TIME_OUT_MSEC );
561                    pxEndPoint->xRAData.eRAState = eRAStateWait;
562                }
563                break;
564 
565             case eRAStateIPTest: /* Assuming an IP address, test if another device is using it already. */
566                {
567                    size_t uxNeededSize;
568                    NetworkBufferDescriptor_t * pxNetworkBuffer;
569 
570                    /* Get an IP-address, using the network prefix and a random host address. */
571                    if( pxEndPoint->xRAData.bits.bIPAddressInUse != 0U )
572                    {
573                        pxEndPoint->xRAData.bits.bIPAddressInUse = pdFALSE_UNSIGNED;
574 
575                        ( void ) FreeRTOS_CreateIPv6Address( &pxEndPoint->ipv6_settings.xIPAddress, &pxEndPoint->ipv6_settings.xPrefix, pxEndPoint->ipv6_settings.uxPrefixLength, pdTRUE );
576 
577                        FreeRTOS_printf( ( "RA: Creating a random IP-address\n" ) );
578                    }
579 
580                    FreeRTOS_printf( ( "RA: Neighbour solicitation for %pip\n", ( void * ) pxEndPoint->ipv6_settings.xIPAddress.ucBytes ) );
581 
582                    uxNeededSize = ipSIZE_OF_ETH_HEADER + ipSIZE_OF_IPv6_HEADER + sizeof( ICMPHeader_IPv6_t );
583                    pxNetworkBuffer = pxGetNetworkBufferWithDescriptor( uxNeededSize, raDONT_BLOCK );
584 
585                    if( pxNetworkBuffer != NULL )
586                    {
587                        pxNetworkBuffer->pxEndPoint = pxEndPoint;
588                        vNDSendNeighbourSolicitation( pxNetworkBuffer, &( pxEndPoint->ipv6_settings.xIPAddress ) );
589                    }
590 
591                    uxNewReloadTime = pdMS_TO_TICKS( 1000U );
592                    pxEndPoint->xRAData.eRAState = eRAStateIPWait;
593                }
594                break;
595 
596             case eRAStatePreLease:
597                 pxEndPoint->xRAData.eRAState = eRAStateLease;
598                 break;
599 
600             case eRAStateLease:
601 
602                 vRAProcessInit( pxEndPoint );
603                 uxNewReloadTime = pdMS_TO_TICKS( 1000U );
604 
605                 break;
606 
607             case eRAStateFailed:
608                 break;
609 
610             default:
611                 /* All states were handled. */
612                 break;
613         }
614 
615         return uxNewReloadTime;
616     }
617 /*-----------------------------------------------------------*/
618 
619 /**
620  * @brief Initialise the RA state machine.
621  *
622  * @param[in] pxEndPoint The end-point for which Router Advertisement is required.
623  */
vRAProcessInit(NetworkEndPoint_t * pxEndPoint)624     static void vRAProcessInit( NetworkEndPoint_t * pxEndPoint )
625     {
626         pxEndPoint->xRAData.uxRetryCount = 0U;
627         pxEndPoint->xRAData.eRAState = eRAStateApply;
628     }
629 
630 /**
631  * @brief Do a single cycle of the RA state machine.
632  *
633  * @param[in] xDoReset pdTRUE if the state machine must be reset.
634  * @param[in] pxEndPoint The end-point for which a RA assignment is required.
635  */
vRAProcess(BaseType_t xDoReset,NetworkEndPoint_t * pxEndPoint)636     void vRAProcess( BaseType_t xDoReset,
637                      NetworkEndPoint_t * pxEndPoint )
638     {
639         TickType_t uxReloadTime = pdMS_TO_TICKS( 5000U );
640 
641         configASSERT( pxEndPoint != NULL );
642 
643         #if ( ipconfigHAS_PRINTF == 1 )
644             /* Remember the initial state, just for logging. */
645             eRAState_t eRAState = pxEndPoint->xRAData.eRAState;
646         #endif
647 
648         if( xDoReset != pdFALSE )
649         {
650             vRAProcessInit( pxEndPoint );
651         }
652 
653         /* First handle the states that are limited by a timer. See if some
654          * timer has expired. */
655         uxReloadTime = xRAProcess_HandleWaitStates( pxEndPoint, uxReloadTime );
656 
657         /* Now handle the other states. */
658         uxReloadTime = xRAProcess_HandleOtherStates( pxEndPoint, uxReloadTime );
659 
660         #if ( ipconfigHAS_PRINTF == 1 )
661             {
662                 FreeRTOS_printf( ( "vRAProcess( %ld, %pip) bRouterReplied=%d bIPAddressInUse=%d state %d -> %d\n",
663                                    xDoReset,
664                                    ( void * ) pxEndPoint->ipv6_defaults.xIPAddress.ucBytes,
665                                    pxEndPoint->xRAData.bits.bRouterReplied,
666                                    pxEndPoint->xRAData.bits.bIPAddressInUse,
667                                    eRAState,
668                                    pxEndPoint->xRAData.eRAState ) );
669             }
670         #endif /* ( ipconfigHAS_PRINTF == 1 ) */
671 
672         if( uxReloadTime != 0U )
673         {
674             FreeRTOS_printf( ( "RA: Reload %u seconds\n", ( unsigned ) ( uxReloadTime / 1000U ) ) );
675             vDHCP_RATimerReload( pxEndPoint, uxReloadTime );
676         }
677         else
678         {
679             /* Disable the timer, this function vRAProcess() won't be called anymore for this end-point. */
680             FreeRTOS_printf( ( "RA: Disabled timer.\n" ) );
681             vIPSetDHCP_RATimerEnableState( pxEndPoint, pdFALSE );
682         }
683     }
684 /*-----------------------------------------------------------*/
685 
686 #endif /* ( ipconfigUSE_IPv6 != 0 ) && ( ipconfigUSE_RA != 0 ) */
687