/* * FreeRTOS+TCP V2.3.1 * Copyright (C) 2020 Amazon.com, Inc. or its affiliates. All Rights Reserved. * * Permission is hereby granted, free of charge, to any person obtaining a copy of * this software and associated documentation files (the "Software"), to deal in * the Software without restriction, including without limitation the rights to * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of * the Software, and to permit persons to whom the Software is furnished to do so, * subject to the following conditions: * * The above copyright notice and this permission notice shall be included in all * copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. * * http://aws.amazon.com/freertos * http://www.FreeRTOS.org */ /** * @file FreeRTOS_RA.c * @brief A client implementation of Router advertisement protocol. */ /* Standard includes. */ #include #include /* FreeRTOS includes. */ #include "FreeRTOS.h" #include "task.h" /* FreeRTOS+TCP includes. */ #include "FreeRTOS_IP.h" #include "FreeRTOS_Sockets.h" #include "FreeRTOS_IP_Private.h" #include "FreeRTOS_IP_Timers.h" #include "FreeRTOS_ARP.h" #include "FreeRTOS_UDP_IP.h" #include "FreeRTOS_Routing.h" #include "FreeRTOS_ND.h" #if ( ipconfigUSE_LLMNR == 1 ) #include "FreeRTOS_DNS.h" #endif /* ipconfigUSE_LLMNR */ #include "NetworkBufferManagement.h" /* This define may exclude the entire source file. */ #if ( ipconfigUSE_IPv6 != 0 ) && ( ipconfigUSE_RA != 0 ) /*-----------------------------------------------------------*/ /** A block time of 0 simply means "don't block". */ #define raDONT_BLOCK ( ( TickType_t ) 0 ) /** The default value for the IPv6-field 'ucVersionTrafficClass'. */ #define raDEFAULT_VERSION_TRAFFIC_CLASS 0x60U /** The default value for the IPv6-field 'ucHopLimit'. */ #define raDEFAULT_HOP_LIMIT 255U /*-----------------------------------------------------------*/ /* Initialise the Router Advertisement process for a given end-point. */ static void vRAProcessInit( NetworkEndPoint_t * pxEndPoint ); /* Find a link-local address that is bound to a given interface. */ static BaseType_t xGetLinkLocalAddress( const NetworkInterface_t * pxInterface, IPv6_Address_t * pxAddress ); /* Read the reply received from the RA server. */ static ICMPPrefixOption_IPv6_t * vReceiveRA_ReadReply( const NetworkBufferDescriptor_t * pxNetworkBuffer ); /* Handle the states that are limited by a timer. See if any of the timers has expired. */ static TickType_t xRAProcess_HandleWaitStates( NetworkEndPoint_t * pxEndPoint, TickType_t uxReloadTime ); /* Handle the other states. */ static TickType_t xRAProcess_HandleOtherStates( NetworkEndPoint_t * pxEndPoint, TickType_t uxReloadTime ); /*-----------------------------------------------------------*/ /** * @brief Find a link-local address that is bound to a given interface. * * @param[in] pxInterface The interface for which a link-local address is looked up. * @param[out] pxAddress The IP address will be copied to this parameter. * * @return pdPASS in case a link-local address was found, otherwise pdFAIL. */ static BaseType_t xGetLinkLocalAddress( const NetworkInterface_t * pxInterface, IPv6_Address_t * pxAddress ) { BaseType_t xResult = pdFAIL; NetworkEndPoint_t * pxEndPoint; for( pxEndPoint = FreeRTOS_FirstEndPoint( pxInterface ); pxEndPoint != NULL; pxEndPoint = FreeRTOS_NextEndPoint( pxInterface, pxEndPoint ) ) { /* Check if it has the link-local prefix FE80::/10 */ if( ( pxEndPoint->ipv6_settings.xIPAddress.ucBytes[ 0 ] == 0xfeU ) && ( ( pxEndPoint->ipv6_settings.xIPAddress.ucBytes[ 1 ] & 0xc0U ) == 0x80U ) ) { ( void ) memcpy( pxAddress->ucBytes, pxEndPoint->ipv6_settings.xIPAddress.ucBytes, ipSIZE_OF_IPv6_ADDRESS ); xResult = pdPASS; break; } } return xResult; } /*-----------------------------------------------------------*/ /** * @brief Send an ICMPv6 message of the type: Router Solicitation. * * @param[in] pxNetworkBuffer The network buffer which can be used for this. * @param[in] pxIPAddress The target address, normally ff02::2 * */ void vNDSendRouterSolicitation( NetworkBufferDescriptor_t * pxNetworkBuffer, IPv6_Address_t * pxIPAddress ) { ICMPPacket_IPv6_t * pxICMPPacket; ICMPRouterSolicitation_IPv6_t * xRASolicitationRequest; const NetworkEndPoint_t * pxEndPoint = pxNetworkBuffer->pxEndPoint; const size_t uxNeededSize = ipSIZE_OF_ETH_HEADER + ipSIZE_OF_IPv6_HEADER + sizeof( ICMPRouterSolicitation_IPv6_t ); MACAddress_t xMultiCastMacAddress; NetworkBufferDescriptor_t * pxDescriptor = pxNetworkBuffer; IPv6_Address_t xSourceAddress; BaseType_t xHasLocal; NetworkBufferDescriptor_t * pxNewDescriptor = NULL; configASSERT( pxEndPoint != NULL ); configASSERT( pxEndPoint->bits.bIPv6 != pdFALSE_UNSIGNED ); xHasLocal = xGetLinkLocalAddress( pxEndPoint->pxNetworkInterface, &( xSourceAddress ) ); if( xHasLocal == pdFAIL ) { FreeRTOS_printf( ( "RA: can not find a Link-local address\n" ) ); ( void ) memset( xSourceAddress.ucBytes, 0, ipSIZE_OF_IPv6_ADDRESS ); } else { FreeRTOS_printf( ( "RA: source %pip\n", ( void * ) xSourceAddress.ucBytes ) ); } if( pxDescriptor->xDataLength < uxNeededSize ) { pxNewDescriptor = pxDuplicateNetworkBufferWithDescriptor( pxDescriptor, uxNeededSize ); vReleaseNetworkBufferAndDescriptor( pxDescriptor ); pxDescriptor = pxNewDescriptor; } if( pxDescriptor != NULL ) { /* MISRA Ref 11.3.1 [Misaligned access] */ /* More details at: https://github.com/FreeRTOS/FreeRTOS-Plus-TCP/blob/main/MISRA.md#rule-113 */ /* coverity[misra_c_2012_rule_11_3_violation] */ pxICMPPacket = ( ( ICMPPacket_IPv6_t * ) pxDescriptor->pucEthernetBuffer ); xRASolicitationRequest = ( ( ICMPRouterSolicitation_IPv6_t * ) &( pxICMPPacket->xICMPHeaderIPv6 ) ); pxDescriptor->xDataLength = uxNeededSize; ( void ) eNDGetCacheEntry( pxIPAddress, &( xMultiCastMacAddress ), NULL ); /* Set Ethernet header. Will be swapped. */ ( void ) memcpy( pxICMPPacket->xEthernetHeader.xSourceAddress.ucBytes, xMultiCastMacAddress.ucBytes, ipMAC_ADDRESS_LENGTH_BYTES ); ( void ) memcpy( pxICMPPacket->xEthernetHeader.xDestinationAddress.ucBytes, pxEndPoint->xMACAddress.ucBytes, ipMAC_ADDRESS_LENGTH_BYTES ); pxICMPPacket->xEthernetHeader.usFrameType = ipIPv6_FRAME_TYPE; /* Set IP-header. */ pxICMPPacket->xIPHeader.ucVersionTrafficClass = raDEFAULT_VERSION_TRAFFIC_CLASS; pxICMPPacket->xIPHeader.ucTrafficClassFlow = 0U; pxICMPPacket->xIPHeader.usFlowLabel = 0U; pxICMPPacket->xIPHeader.usPayloadLength = FreeRTOS_htons( sizeof( ICMPRouterSolicitation_IPv6_t ) ); pxICMPPacket->xIPHeader.ucNextHeader = ipPROTOCOL_ICMP_IPv6; pxICMPPacket->xIPHeader.ucHopLimit = raDEFAULT_HOP_LIMIT; /* Normally, the source address is set as 'ipv6_settings.xIPAddress'. * But is some routers will not accept a public IP-address, the original * default address will be used. It must be a link-local address. */ ( void ) memcpy( pxICMPPacket->xIPHeader.xSourceAddress.ucBytes, xSourceAddress.ucBytes, ipSIZE_OF_IPv6_ADDRESS ); ( void ) memcpy( pxICMPPacket->xIPHeader.xDestinationAddress.ucBytes, pxIPAddress->ucBytes, ipSIZE_OF_IPv6_ADDRESS ); /* Set ICMP header. */ ( void ) memset( xRASolicitationRequest, 0, sizeof( *xRASolicitationRequest ) ); xRASolicitationRequest->ucTypeOfMessage = ipICMP_ROUTER_SOLICITATION_IPv6; /* __XX__ revisit on why commented out * xRASolicitationRequest->ucOptionType = ndICMP_SOURCE_LINK_LAYER_ADDRESS; * xRASolicitationRequest->ucOptionLength = 1; * ( void ) memcpy( xRASolicitationRequest->ucOptionBytes, pxEndPoint->xMACAddress.ucBytes, ipMAC_ADDRESS_LENGTH_BYTES ); */ /* Checksums. */ #if ( ipconfigDRIVER_INCLUDED_TX_IP_CHECKSUM == 0 ) { /* calculate the ICMPv6 checksum for outgoing package */ ( void ) usGenerateProtocolChecksum( pxDescriptor->pucEthernetBuffer, pxDescriptor->xDataLength, pdTRUE ); } #else { /* Many EMAC peripherals will only calculate the ICMP checksum * correctly if the field is nulled beforehand. */ xRASolicitationRequest->usChecksum = 0U; } #endif /* This function will fill in the eth addresses and send the packet */ vReturnEthernetFrame( pxDescriptor, pdTRUE ); } } /*-----------------------------------------------------------*/ /** * @brief Receive a NA ( Neighbour Advertisement ) message to see if a chosen IP-address is already in use. * * @param[in] pxNetworkBuffer The buffer that contains the message. */ void vReceiveNA( const NetworkBufferDescriptor_t * pxNetworkBuffer ) { const NetworkInterface_t * pxInterface = pxNetworkBuffer->pxInterface; NetworkEndPoint_t * pxPoint; /* MISRA Ref 11.3.1 [Misaligned access] */ /* More details at: https://github.com/FreeRTOS/FreeRTOS-Plus-TCP/blob/main/MISRA.md#rule-113 */ /* coverity[misra_c_2012_rule_11_3_violation] */ const ICMPPacket_IPv6_t * pxICMPPacket = ( ( const ICMPPacket_IPv6_t * ) pxNetworkBuffer->pucEthernetBuffer ); const ICMPHeader_IPv6_t * pxICMPHeader_IPv6 = ( ( const ICMPHeader_IPv6_t * ) &( pxICMPPacket->xICMPHeaderIPv6 ) ); for( pxPoint = FreeRTOS_FirstEndPoint( pxInterface ); pxPoint != NULL; pxPoint = FreeRTOS_NextEndPoint( pxInterface, pxPoint ) ) { if( ( pxPoint->bits.bWantRA != pdFALSE_UNSIGNED ) && ( pxPoint->xRAData.eRAState == eRAStateIPWait ) ) { if( memcmp( pxPoint->ipv6_settings.xIPAddress.ucBytes, pxICMPHeader_IPv6->xIPv6Address.ucBytes, ipSIZE_OF_IPv6_ADDRESS ) == 0 ) { pxPoint->xRAData.bits.bIPAddressInUse = pdTRUE_UNSIGNED; vDHCP_RATimerReload( pxPoint, 100U ); } } } } /*-----------------------------------------------------------*/ /** * @brief Read a received RA reply and return the prefix option from the packet. * * @param[in] pxNetworkBuffer The buffer that contains the message. * * @returns Returns the ICMP prefix option pointer, pointing to its location in the * input RA reply message buffer. */ static ICMPPrefixOption_IPv6_t * vReceiveRA_ReadReply( const NetworkBufferDescriptor_t * pxNetworkBuffer ) { size_t uxIndex = 0U; const size_t uxICMPSize = sizeof( ICMPRouterAdvertisement_IPv6_t ); const size_t uxNeededSize = ipSIZE_OF_ETH_HEADER + ipSIZE_OF_IPv6_HEADER + uxICMPSize; /* uxLast points to the first byte after the buffer. */ const size_t uxLast = pxNetworkBuffer->xDataLength - uxNeededSize; uint8_t * pucBytes = &( pxNetworkBuffer->pucEthernetBuffer[ uxNeededSize ] ); ICMPPrefixOption_IPv6_t * pxPrefixOption = NULL; while( ( uxIndex + 1U ) < uxLast ) { uint8_t ucType = pucBytes[ uxIndex ]; size_t uxPrefixLength = ( size_t ) pucBytes[ uxIndex + 1U ]; size_t uxLength = uxPrefixLength * 8U; if( uxPrefixLength == 0U ) { /* According to RFC 4861, length of the option value 0 is invalid. Hence returning from here */ FreeRTOS_printf( ( "RA: Invalid length of the option value as zero. " ) ); break; } if( uxLast < ( uxIndex + uxLength ) ) { FreeRTOS_printf( ( "RA: Not enough bytes ( %u > %u )\n", ( unsigned ) ( uxIndex + uxLength ), ( unsigned ) uxLast ) ); break; } switch( ucType ) { case ndICMP_SOURCE_LINK_LAYER_ADDRESS: /* 1 */ FreeRTOS_printf( ( "RA: Source = %02x-%02x-%02x-%02x-%02x-%02x\n", pucBytes[ uxIndex + 2U ], pucBytes[ uxIndex + 3U ], pucBytes[ uxIndex + 4U ], pucBytes[ uxIndex + 5U ], pucBytes[ uxIndex + 6U ], pucBytes[ uxIndex + 7U ] ) ); break; case ndICMP_TARGET_LINK_LAYER_ADDRESS: /* 2 */ break; case ndICMP_PREFIX_INFORMATION: /* 3 */ /* MISRA Ref 11.3.1 [Misaligned access] */ /* More details at: https://github.com/FreeRTOS/FreeRTOS-Plus-TCP/blob/main/MISRA.md#rule-113 */ /* coverity[misra_c_2012_rule_11_3_violation] */ pxPrefixOption = ( ( ICMPPrefixOption_IPv6_t * ) &( pucBytes[ uxIndex ] ) ); FreeRTOS_printf( ( "RA: Prefix len %d Life %u, %u (%pip)\n", pxPrefixOption->ucPrefixLength, FreeRTOS_ntohl( pxPrefixOption->ulValidLifeTime ), FreeRTOS_ntohl( pxPrefixOption->ulPreferredLifeTime ), ( void * ) pxPrefixOption->ucPrefix ) ); break; case ndICMP_REDIRECTED_HEADER: /* 4 */ break; case ndICMP_MTU_OPTION: /* 5 */ { uint32_t ulMTU; ( void ) ulMTU; /* ulChar2u32 returns host-endian numbers. */ ulMTU = ulChar2u32( &( pucBytes[ uxIndex + 4U ] ) ); FreeRTOS_printf( ( "RA: MTU = %u\n", ( unsigned int ) ulMTU ) ); } break; default: FreeRTOS_printf( ( "RA: Type 0x%02x not implemented\n", ucType ) ); break; } uxIndex = uxIndex + uxLength; } /* while( ( uxIndex + 1 ) < uxLast ) */ return pxPrefixOption; } /*-----------------------------------------------------------*/ /** * @brief Receive and analyse a RA ( Router Advertisement ) message. * If the reply is satisfactory, the end-point will do SLAAC: choose an IP-address using the * prefix offered, and completed with random bits. It will start testing if another device * already exists that uses the same IP-address. * * @param[in] pxNetworkBuffer The buffer that contains the message. */ void vReceiveRA( const NetworkBufferDescriptor_t * pxNetworkBuffer ) { /* MISRA Ref 11.3.1 [Misaligned access] */ /* More details at: https://github.com/FreeRTOS/FreeRTOS-Plus-TCP/blob/main/MISRA.md#rule-113 */ /* coverity[misra_c_2012_rule_11_3_violation] */ const ICMPPacket_IPv6_t * pxICMPPacket = ( ( const ICMPPacket_IPv6_t * ) pxNetworkBuffer->pucEthernetBuffer ); const ICMPPrefixOption_IPv6_t * pxPrefixOption = NULL; const size_t uxICMPSize = sizeof( ICMPRouterAdvertisement_IPv6_t ); const size_t uxNeededSize = ipSIZE_OF_ETH_HEADER + ipSIZE_OF_IPv6_HEADER + uxICMPSize; /* A Router Advertisement was received, handle it here. */ if( uxNeededSize > pxNetworkBuffer->xDataLength ) { FreeRTOS_printf( ( "vReceiveRA: The buffer provided is too small\n" ) ); } else { /* MISRA Ref 11.3.1 [Misaligned access] */ /* More details at: https://github.com/FreeRTOS/FreeRTOS-Plus-TCP/blob/main/MISRA.md#rule-113 */ /* coverity[misra_c_2012_rule_11_3_violation] */ const ICMPRouterAdvertisement_IPv6_t * pxAdvertisement = ( ( const ICMPRouterAdvertisement_IPv6_t * ) &( pxICMPPacket->xICMPHeaderIPv6 ) ); FreeRTOS_printf( ( "RA: Type %02x Srv %02x Checksum %04x Hops %d Flags %02x Life %d\n", pxAdvertisement->ucTypeOfMessage, pxAdvertisement->ucTypeOfService, FreeRTOS_ntohs( pxAdvertisement->usChecksum ), pxAdvertisement->ucHopLimit, pxAdvertisement->ucFlags, FreeRTOS_ntohs( pxAdvertisement->usLifetime ) ) ); if( pxAdvertisement->usLifetime != 0U ) { pxPrefixOption = vReceiveRA_ReadReply( pxNetworkBuffer ); configASSERT( pxNetworkBuffer->pxInterface != NULL ); if( pxPrefixOption != NULL ) { NetworkEndPoint_t * pxEndPoint; for( pxEndPoint = FreeRTOS_FirstEndPoint( pxNetworkBuffer->pxInterface ); pxEndPoint != NULL; pxEndPoint = FreeRTOS_NextEndPoint( pxNetworkBuffer->pxInterface, pxEndPoint ) ) { if( ( pxEndPoint->bits.bWantRA != pdFALSE_UNSIGNED ) && ( pxEndPoint->xRAData.eRAState == eRAStateWait ) ) { pxEndPoint->ipv6_settings.uxPrefixLength = pxPrefixOption->ucPrefixLength; ( void ) memcpy( pxEndPoint->ipv6_settings.xPrefix.ucBytes, pxPrefixOption->ucPrefix, ipSIZE_OF_IPv6_ADDRESS ); ( void ) memcpy( pxEndPoint->ipv6_settings.xGatewayAddress.ucBytes, pxICMPPacket->xIPHeader.xSourceAddress.ucBytes, ipSIZE_OF_IPv6_ADDRESS ); pxEndPoint->xRAData.bits.bRouterReplied = pdTRUE_UNSIGNED; pxEndPoint->xRAData.uxRetryCount = 0U; pxEndPoint->xRAData.ulPreferredLifeTime = FreeRTOS_ntohl( pxPrefixOption->ulPreferredLifeTime ); /* Force taking a new random IP-address. */ pxEndPoint->xRAData.bits.bIPAddressInUse = pdTRUE_UNSIGNED; pxEndPoint->xRAData.eRAState = eRAStateIPTest; vRAProcess( pdFALSE, pxEndPoint ); } } } } else { /* The life-time field contains zero. */ } } } /*-----------------------------------------------------------*/ /** * @brief Handles the RA wait state and calculates the new timer reload value * based on the wait state. Also checks if any timer has expired. If its found that * there is no other device using the same IP-address vIPNetworkUpCalls() is called * to send the network up event. * * @param[in] pxEndPoint The end point for which RA assignment is required. * @param[out] uxReloadTime Timer reload value in ticks. * * @return New timer reload value. */ static TickType_t xRAProcess_HandleWaitStates( NetworkEndPoint_t * pxEndPoint, TickType_t uxReloadTime ) { TickType_t uxNewReloadTime = uxReloadTime; if( pxEndPoint->xRAData.eRAState == eRAStateWait ) { /* A Router Solicitation has been sent, waited for a reply, but no came. * All replies will be handled in the function vReceiveRA(). */ pxEndPoint->xRAData.uxRetryCount++; if( pxEndPoint->xRAData.uxRetryCount < ( UBaseType_t ) ipconfigRA_SEARCH_COUNT ) { pxEndPoint->xRAData.eRAState = eRAStateApply; } else { FreeRTOS_printf( ( "RA: Giving up waiting for a Router.\n" ) ); ( void ) memcpy( &( pxEndPoint->ipv6_settings ), &( pxEndPoint->ipv6_defaults ), sizeof( pxEndPoint->ipv6_settings ) ); pxEndPoint->xRAData.bits.bRouterReplied = pdFALSE_UNSIGNED; pxEndPoint->xRAData.uxRetryCount = 0U; /* Force taking a new random IP-address. */ pxEndPoint->xRAData.bits.bIPAddressInUse = pdTRUE_UNSIGNED; pxEndPoint->xRAData.eRAState = eRAStateIPTest; } } else if( pxEndPoint->xRAData.eRAState == eRAStateIPWait ) { /* A Neighbour Solicitation has been sent, waited for a reply. * Repeat this 'ipconfigRA_IP_TEST_COUNT' times to be sure. */ if( pxEndPoint->xRAData.bits.bIPAddressInUse != pdFALSE_UNSIGNED ) { /* Another device has responded with the same IPv4 address. */ pxEndPoint->xRAData.uxRetryCount = 0U; pxEndPoint->xRAData.eRAState = eRAStateIPTest; uxNewReloadTime = pdMS_TO_TICKS( ipconfigRA_IP_TEST_TIME_OUT_MSEC ); } else if( pxEndPoint->xRAData.uxRetryCount < ( UBaseType_t ) ipconfigRA_IP_TEST_COUNT ) { /* Try again. */ pxEndPoint->xRAData.uxRetryCount++; pxEndPoint->xRAData.eRAState = eRAStateIPTest; uxNewReloadTime = pdMS_TO_TICKS( ipconfigRA_IP_TEST_TIME_OUT_MSEC ); } else { /* Now it is assumed that there is no other device using the same IP-address. */ if( pxEndPoint->xRAData.bits.bRouterReplied != pdFALSE_UNSIGNED ) { /* Obtained configuration from a router. */ uxNewReloadTime = pdMS_TO_TICKS( 1000U * pxEndPoint->xRAData.ulPreferredLifeTime ); pxEndPoint->xRAData.eRAState = eRAStatePreLease; iptraceRA_SUCCEDEED( &( pxEndPoint->ipv6_settings.xIPAddress ) ); FreeRTOS_printf( ( "RA: succeeded, using IP address %pip Reload after %u seconds\n", ( void * ) pxEndPoint->ipv6_settings.xIPAddress.ucBytes, ( unsigned ) pxEndPoint->xRAData.ulPreferredLifeTime ) ); } else { /* Using the default network parameters. */ pxEndPoint->xRAData.eRAState = eRAStateFailed; iptraceRA_REQUESTS_FAILED_USING_DEFAULT_IP_ADDRESS( &( pxEndPoint->ipv6_settings.xIPAddress ) ); FreeRTOS_printf( ( "RA: failed, using default parameters and IP address %pip\n", ( void * ) pxEndPoint->ipv6_settings.xIPAddress.ucBytes ) ); /* Disable the timer. */ uxNewReloadTime = 0U; } /* Now call vIPNetworkUpCalls() to send the network-up event and * start the ARP timer. */ vIPNetworkUpCalls( pxEndPoint ); } } else { /* Do nothing */ } return uxNewReloadTime; } /*-----------------------------------------------------------*/ /** * @brief Handles the RA states other than the wait states. * * @param[in] pxEndPoint The end point for which RA assignment is required. * @param[out] uxReloadTime Timer reload value in ticks. * * @return New timer reload value. */ static TickType_t xRAProcess_HandleOtherStates( NetworkEndPoint_t * pxEndPoint, TickType_t uxReloadTime ) { TickType_t uxNewReloadTime = uxReloadTime; switch( pxEndPoint->xRAData.eRAState ) { case eRAStateApply: { IPv6_Address_t xIPAddress; size_t uxNeededSize; NetworkBufferDescriptor_t * pxNetworkBuffer; /* Send a Router Solicitation to ff02::2 */ ( void ) memset( xIPAddress.ucBytes, 0, sizeof( xIPAddress.ucBytes ) ); xIPAddress.ucBytes[ 0 ] = 0xffU; xIPAddress.ucBytes[ 1 ] = 0x02U; xIPAddress.ucBytes[ 15 ] = 0x02U; uxNeededSize = ipSIZE_OF_ETH_HEADER + ipSIZE_OF_IPv6_HEADER + sizeof( ICMPRouterSolicitation_IPv6_t ); pxNetworkBuffer = pxGetNetworkBufferWithDescriptor( uxNeededSize, raDONT_BLOCK ); if( pxNetworkBuffer != NULL ) { pxNetworkBuffer->pxEndPoint = pxEndPoint; vNDSendRouterSolicitation( pxNetworkBuffer, &( xIPAddress ) ); } FreeRTOS_printf( ( "vRAProcess: Router Solicitation, attempt %lu/%u\n", pxEndPoint->xRAData.uxRetryCount + 1U, ipconfigRA_SEARCH_COUNT ) ); /* Wait a configurable time for a router advertisement. */ uxNewReloadTime = pdMS_TO_TICKS( ipconfigRA_SEARCH_TIME_OUT_MSEC ); pxEndPoint->xRAData.eRAState = eRAStateWait; } break; case eRAStateIPTest: /* Assuming an IP address, test if another device is using it already. */ { size_t uxNeededSize; NetworkBufferDescriptor_t * pxNetworkBuffer; /* Get an IP-address, using the network prefix and a random host address. */ if( pxEndPoint->xRAData.bits.bIPAddressInUse != 0U ) { pxEndPoint->xRAData.bits.bIPAddressInUse = pdFALSE_UNSIGNED; ( void ) FreeRTOS_CreateIPv6Address( &pxEndPoint->ipv6_settings.xIPAddress, &pxEndPoint->ipv6_settings.xPrefix, pxEndPoint->ipv6_settings.uxPrefixLength, pdTRUE ); FreeRTOS_printf( ( "RA: Creating a random IP-address\n" ) ); } FreeRTOS_printf( ( "RA: Neighbour solicitation for %pip\n", ( void * ) pxEndPoint->ipv6_settings.xIPAddress.ucBytes ) ); uxNeededSize = ipSIZE_OF_ETH_HEADER + ipSIZE_OF_IPv6_HEADER + sizeof( ICMPHeader_IPv6_t ); pxNetworkBuffer = pxGetNetworkBufferWithDescriptor( uxNeededSize, raDONT_BLOCK ); if( pxNetworkBuffer != NULL ) { pxNetworkBuffer->pxEndPoint = pxEndPoint; vNDSendNeighbourSolicitation( pxNetworkBuffer, &( pxEndPoint->ipv6_settings.xIPAddress ) ); } uxNewReloadTime = pdMS_TO_TICKS( 1000U ); pxEndPoint->xRAData.eRAState = eRAStateIPWait; } break; case eRAStatePreLease: pxEndPoint->xRAData.eRAState = eRAStateLease; break; case eRAStateLease: vRAProcessInit( pxEndPoint ); uxNewReloadTime = pdMS_TO_TICKS( 1000U ); break; case eRAStateFailed: break; default: /* All states were handled. */ break; } return uxNewReloadTime; } /*-----------------------------------------------------------*/ /** * @brief Initialise the RA state machine. * * @param[in] pxEndPoint The end-point for which Router Advertisement is required. */ static void vRAProcessInit( NetworkEndPoint_t * pxEndPoint ) { pxEndPoint->xRAData.uxRetryCount = 0U; pxEndPoint->xRAData.eRAState = eRAStateApply; } /** * @brief Do a single cycle of the RA state machine. * * @param[in] xDoReset pdTRUE if the state machine must be reset. * @param[in] pxEndPoint The end-point for which a RA assignment is required. */ void vRAProcess( BaseType_t xDoReset, NetworkEndPoint_t * pxEndPoint ) { TickType_t uxReloadTime = pdMS_TO_TICKS( 5000U ); configASSERT( pxEndPoint != NULL ); #if ( ipconfigHAS_PRINTF == 1 ) /* Remember the initial state, just for logging. */ eRAState_t eRAState = pxEndPoint->xRAData.eRAState; #endif if( xDoReset != pdFALSE ) { vRAProcessInit( pxEndPoint ); } /* First handle the states that are limited by a timer. See if some * timer has expired. */ uxReloadTime = xRAProcess_HandleWaitStates( pxEndPoint, uxReloadTime ); /* Now handle the other states. */ uxReloadTime = xRAProcess_HandleOtherStates( pxEndPoint, uxReloadTime ); #if ( ipconfigHAS_PRINTF == 1 ) { FreeRTOS_printf( ( "vRAProcess( %ld, %pip) bRouterReplied=%d bIPAddressInUse=%d state %d -> %d\n", xDoReset, ( void * ) pxEndPoint->ipv6_defaults.xIPAddress.ucBytes, pxEndPoint->xRAData.bits.bRouterReplied, pxEndPoint->xRAData.bits.bIPAddressInUse, eRAState, pxEndPoint->xRAData.eRAState ) ); } #endif /* ( ipconfigHAS_PRINTF == 1 ) */ if( uxReloadTime != 0U ) { FreeRTOS_printf( ( "RA: Reload %u seconds\n", ( unsigned ) ( uxReloadTime / 1000U ) ) ); vDHCP_RATimerReload( pxEndPoint, uxReloadTime ); } else { /* Disable the timer, this function vRAProcess() won't be called anymore for this end-point. */ FreeRTOS_printf( ( "RA: Disabled timer.\n" ) ); vIPSetDHCP_RATimerEnableState( pxEndPoint, pdFALSE ); } } /*-----------------------------------------------------------*/ #endif /* ( ipconfigUSE_IPv6 != 0 ) && ( ipconfigUSE_RA != 0 ) */