From 247623eeda2a09cc31423237f08e17b7821febc9 Mon Sep 17 00:00:00 2001 From: Lukasz Janyst Date: Sat, 8 Nov 2025 19:48:16 +0100 Subject: [PATCH 1/4] Optionally merge prefix with host part when creating IPv6 address Previous behavior allowed for either randomizing the host part or filling it with zeros. An IPv6 address with the host part set to all zeros is reserved for router anycasts by RFC4291. Instead, we're allowing the caller to either specify the host part of the address or we randomize it if NULL. --- source/FreeRTOS_ND.c | 16 ++++++++-------- source/FreeRTOS_RA.c | 2 +- source/include/FreeRTOS_ND.h | 8 ++++---- 3 files changed, 13 insertions(+), 13 deletions(-) diff --git a/source/FreeRTOS_ND.c b/source/FreeRTOS_ND.c index 8ed81640dc..c7ebe1866c 100644 --- a/source/FreeRTOS_ND.c +++ b/source/FreeRTOS_ND.c @@ -1260,7 +1260,8 @@ * @param[out] pxIPAddress The location where the new IPv6 address will be stored. * @param[in] pxPrefix The prefix to be used. * @param[in] uxPrefixLength The length of the prefix. - * @param[in] xDoRandom A non-zero value if the bits after the prefix should have a random value. + * @param[in] pxHost: Host part of the address. It will be filled with + * a random value if NULL. * * @return pdPASS if the operation was successful. Or pdFAIL in case xApplicationGetRandomNumber() * returned an error. @@ -1268,13 +1269,13 @@ BaseType_t FreeRTOS_CreateIPv6Address( IPv6_Address_t * pxIPAddress, const IPv6_Address_t * pxPrefix, size_t uxPrefixLength, - BaseType_t xDoRandom ) + const IPv6_Address_t * pxHost ) { - uint32_t pulRandom[ 4 ]; + uint32_t pulRandom[ ipSIZE_OF_IPv6_ADDRESS / sizeof( uint32_t ) ]; uint8_t * pucSource; BaseType_t xIndex, xResult = pdPASS; - if( xDoRandom != pdFALSE ) + if( pxHost == NULL ) { /* Create an IP-address, based on a net prefix and a * random host address. @@ -1292,7 +1293,7 @@ } else { - ( void ) memset( pulRandom, 0, sizeof( pulRandom ) ); + ( void ) memcpy( pulRandom, pxHost->ucBytes, ipSIZE_OF_IPv6_ADDRESS ); } if( xResult == pdPASS ) @@ -1317,14 +1318,13 @@ uint8_t ucNetMask = ( uint8_t ) ~( uxHostMask ); pxIPAddress->ucBytes[ uxIndex ] &= ucNetMask; - pxIPAddress->ucBytes[ uxIndex ] |= ( pucSource[ 0 ] & ( ( uint8_t ) uxHostMask ) ); - pucSource = &( pucSource[ 1 ] ); + pxIPAddress->ucBytes[ uxIndex ] |= ( pucSource[ uxIndex ] & ( ( uint8_t ) uxHostMask ) ); uxIndex++; } if( uxIndex < ipSIZE_OF_IPv6_ADDRESS ) { - ( void ) memcpy( &( pxIPAddress->ucBytes[ uxIndex ] ), pucSource, ipSIZE_OF_IPv6_ADDRESS - uxIndex ); + ( void ) memcpy( &( pxIPAddress->ucBytes[ uxIndex ] ), &pucSource[ uxIndex ], ipSIZE_OF_IPv6_ADDRESS - uxIndex ); } } diff --git a/source/FreeRTOS_RA.c b/source/FreeRTOS_RA.c index 9b0afc73fd..69219e7292 100644 --- a/source/FreeRTOS_RA.c +++ b/source/FreeRTOS_RA.c @@ -577,7 +577,7 @@ { pxEndPoint->xRAData.bits.bIPAddressInUse = pdFALSE_UNSIGNED; - ( void ) FreeRTOS_CreateIPv6Address( &pxEndPoint->ipv6_settings.xIPAddress, &pxEndPoint->ipv6_settings.xPrefix, pxEndPoint->ipv6_settings.uxPrefixLength, pdTRUE ); + ( void ) FreeRTOS_CreateIPv6Address( &pxEndPoint->ipv6_settings.xIPAddress, &pxEndPoint->ipv6_settings.xPrefix, pxEndPoint->ipv6_settings.uxPrefixLength, NULL ); FreeRTOS_printf( ( "RA: Creating a random IP-address\n" ) ); } diff --git a/source/include/FreeRTOS_ND.h b/source/include/FreeRTOS_ND.h index 3b78461e91..ee66869b2a 100644 --- a/source/include/FreeRTOS_ND.h +++ b/source/include/FreeRTOS_ND.h @@ -160,14 +160,14 @@ #endif /** - * @brief Create an IPv16 address, based on a prefix. + * @brief Create an IPv6 address, based on a prefix. * * @param[out] pxIPAddress: The location where the new IPv6 address * will be stored. * @param[in] pxPrefix: The prefix to be used. * @param[in] uxPrefixLength: The length of the prefix. - * @param[in] xDoRandom: A non-zero value if the bits after the - * prefix should have a random value. + * @param[in] pxHost: Host part of the address. It will be filled with + * a random value if NULL. * * @return pdPASS if the operation was successful. Or pdFAIL in * case xApplicationGetRandomNumber() @@ -176,7 +176,7 @@ BaseType_t FreeRTOS_CreateIPv6Address( IPv6_Address_t * pxIPAddress, const IPv6_Address_t * pxPrefix, size_t uxPrefixLength, - BaseType_t xDoRandom ); + const IPv6_Address_t * pxHost ); /* Receive a Neighbour Advertisement. */ From 36476349c0f11833feb9d45159209757d480a14b Mon Sep 17 00:00:00 2001 From: Lukasz Janyst Date: Mon, 10 Nov 2025 19:41:28 +0100 Subject: [PATCH 2/4] Update ND and RA tests to use new CreateIPv6Address --- .../unit-test/FreeRTOS_ND/FreeRTOS_ND_utest.c | 36 +++++++++---------- .../unit-test/FreeRTOS_RA/FreeRTOS_RA_stubs.c | 6 ++-- 2 files changed, 21 insertions(+), 21 deletions(-) diff --git a/test/unit-test/FreeRTOS_ND/FreeRTOS_ND_utest.c b/test/unit-test/FreeRTOS_ND/FreeRTOS_ND_utest.c index 4c3fda8ff8..3e6bf51032 100644 --- a/test/unit-test/FreeRTOS_ND/FreeRTOS_ND_utest.c +++ b/test/unit-test/FreeRTOS_ND/FreeRTOS_ND_utest.c @@ -1834,12 +1834,12 @@ void test_FreeRTOS_OutputAdvertiseIPv6_HappyPath( void ) */ void test_FreeRTOS_CreateIPv6Address_RandomFail( void ) { - IPv6_Address_t xIPAddress, xPrefix = { 0 }; - BaseType_t xDoRandom = pdTRUE, xReturn; + IPv6_Address_t xIPAddress, xPrefix = { 0 }, * pxHost = NULL; + BaseType_t xReturn; xApplicationGetRandomNumber_ExpectAnyArgsAndReturn( pdFALSE ); - xReturn = FreeRTOS_CreateIPv6Address( &xIPAddress, &xPrefix, sizeof( xPrefix ), xDoRandom ); + xReturn = FreeRTOS_CreateIPv6Address( &xIPAddress, &xPrefix, sizeof( xPrefix ), pxHost ); TEST_ASSERT_EQUAL( xReturn, pdFAIL ); } @@ -1851,30 +1851,30 @@ void test_FreeRTOS_CreateIPv6Address_RandomFail( void ) */ void test_FreeRTOS_CreateIPv6Address_Assert1( void ) { - IPv6_Address_t xIPAddress, xPrefix = { 0 }; - BaseType_t xDoRandom = pdTRUE, xReturn, xIndex; + IPv6_Address_t xIPAddress, xPrefix = { 0 }, * pxHost = NULL; + BaseType_t xReturn, xIndex; for( xIndex = 0; xIndex < 4; xIndex++ ) { xApplicationGetRandomNumber_ExpectAnyArgsAndReturn( pdTRUE ); } - catch_assert( FreeRTOS_CreateIPv6Address( &xIPAddress, &xPrefix, 0, xDoRandom ) ); + catch_assert( FreeRTOS_CreateIPv6Address( &xIPAddress, &xPrefix, 0, pxHost ) ); } /** * @brief Create an IPv6 address, based on a prefix. - * with the bits after the prefix having random value - * but incorrect prefix length and xDoRandom is 0. + * with the bits after the prefix having a specified value + * but incorrect prefix length. */ void test_FreeRTOS_CreateIPv6Address_Assert2( void ) { - IPv6_Address_t xIPAddress, xPrefix; + IPv6_Address_t xIPAddress, xPrefix, xHost = { 0 }; /* The maximum allowed prefix length was increased to 128 because of the loopback address. */ size_t uxPrefixLength = 8U * ipSIZE_OF_IPv6_ADDRESS + 1; - BaseType_t xDoRandom = pdFALSE, xReturn, xIndex; + BaseType_t xReturn, xIndex; - catch_assert( FreeRTOS_CreateIPv6Address( &xIPAddress, &xPrefix, uxPrefixLength, xDoRandom ) ); + catch_assert( FreeRTOS_CreateIPv6Address( &xIPAddress, &xPrefix, uxPrefixLength, &xHost ) ); } /** @@ -1883,16 +1883,16 @@ void test_FreeRTOS_CreateIPv6Address_Assert2( void ) */ void test_FreeRTOS_CreateIPv6Address_Pass1( void ) { - IPv6_Address_t xIPAddress, xPrefix; + IPv6_Address_t xIPAddress, xPrefix, * pxHost = NULL; size_t uxPrefixLength = 8U; - BaseType_t xDoRandom = pdTRUE, xReturn, xIndex; + BaseType_t xReturn, xIndex; for( xIndex = 0; xIndex < 4; xIndex++ ) { xApplicationGetRandomNumber_ExpectAnyArgsAndReturn( pdTRUE ); } - xReturn = FreeRTOS_CreateIPv6Address( &xIPAddress, &xPrefix, uxPrefixLength, xDoRandom ); + xReturn = FreeRTOS_CreateIPv6Address( &xIPAddress, &xPrefix, uxPrefixLength, pxHost ); TEST_ASSERT_EQUAL( xReturn, pdPASS ); } @@ -1904,7 +1904,7 @@ void test_FreeRTOS_CreateIPv6Address_Pass1( void ) */ void test_FreeRTOS_CreateIPv6Address_Pass2( void ) { - IPv6_Address_t xIPAddress, xPrefix; + IPv6_Address_t xIPAddress, xPrefix, * pxHost = NULL; size_t uxPrefixLength = 7; BaseType_t xDoRandom = pdTRUE, xReturn, xIndex; @@ -1913,7 +1913,7 @@ void test_FreeRTOS_CreateIPv6Address_Pass2( void ) xApplicationGetRandomNumber_ExpectAnyArgsAndReturn( pdTRUE ); } - xReturn = FreeRTOS_CreateIPv6Address( &xIPAddress, &xPrefix, uxPrefixLength, xDoRandom ); + xReturn = FreeRTOS_CreateIPv6Address( &xIPAddress, &xPrefix, uxPrefixLength, pxHost ); TEST_ASSERT_EQUAL( xReturn, pdPASS ); } @@ -1925,7 +1925,7 @@ void test_FreeRTOS_CreateIPv6Address_Pass2( void ) */ void test_FreeRTOS_CreateIPv6Address_Pass3( void ) { - IPv6_Address_t xIPAddress, xPrefix; + IPv6_Address_t xIPAddress, xPrefix, * pxHost = NULL; size_t uxPrefixLength = 128; BaseType_t xDoRandom = pdTRUE, xReturn, xIndex; @@ -1934,7 +1934,7 @@ void test_FreeRTOS_CreateIPv6Address_Pass3( void ) xApplicationGetRandomNumber_ExpectAnyArgsAndReturn( pdTRUE ); } - xReturn = FreeRTOS_CreateIPv6Address( &xIPAddress, &xPrefix, uxPrefixLength, xDoRandom ); + xReturn = FreeRTOS_CreateIPv6Address( &xIPAddress, &xPrefix, uxPrefixLength, pxHost ); TEST_ASSERT_EQUAL( xReturn, pdPASS ); } diff --git a/test/unit-test/FreeRTOS_RA/FreeRTOS_RA_stubs.c b/test/unit-test/FreeRTOS_RA/FreeRTOS_RA_stubs.c index e88ec77474..a35b6e3b04 100644 --- a/test/unit-test/FreeRTOS_RA/FreeRTOS_RA_stubs.c +++ b/test/unit-test/FreeRTOS_RA/FreeRTOS_RA_stubs.c @@ -45,8 +45,8 @@ eResolutionLookupResult_t eNDGetCacheEntry( IPv6_Address_t * pxIPAddress, * will be stored. * @param[in] pxPrefix: The prefix to be used. * @param[in] uxPrefixLength: The length of the prefix. - * @param[in] xDoRandom: A non-zero value if the bits after the - * prefix should have a random value. + * @param[in] pxHost: Host part of the address. It will be filled with + * a random value if NULL. * * @return pdPASS if the operation was successful. Or pdFAIL in * case xApplicationGetRandomNumber() @@ -55,7 +55,7 @@ eResolutionLookupResult_t eNDGetCacheEntry( IPv6_Address_t * pxIPAddress, BaseType_t FreeRTOS_CreateIPv6Address( IPv6_Address_t * pxIPAddress, const IPv6_Address_t * pxPrefix, size_t uxPrefixLength, - BaseType_t xDoRandom ) + const IPv6_Address_t * pxHost ) { } From ec2d4f7de90c1d334488b7bffe9034720deda083 Mon Sep 17 00:00:00 2001 From: Lukasz Janyst Date: Sat, 8 Nov 2025 23:16:01 +0100 Subject: [PATCH 3/4] Add an option to assign an EUI64 address --- source/FreeRTOS_RA.c | 39 ++++++++++++++++++++++++++++--- source/include/FreeRTOS_Routing.h | 1 + 2 files changed, 37 insertions(+), 3 deletions(-) diff --git a/source/FreeRTOS_RA.c b/source/FreeRTOS_RA.c index 69219e7292..fce9a2ae86 100644 --- a/source/FreeRTOS_RA.c +++ b/source/FreeRTOS_RA.c @@ -412,7 +412,7 @@ pxEndPoint->xRAData.bits.bRouterReplied = pdTRUE_UNSIGNED; pxEndPoint->xRAData.uxRetryCount = 0U; pxEndPoint->xRAData.ulPreferredLifeTime = FreeRTOS_ntohl( pxPrefixOption->ulPreferredLifeTime ); - /* Force taking a new random IP-address. */ + /* Force taking a new IP-address. */ pxEndPoint->xRAData.bits.bIPAddressInUse = pdTRUE_UNSIGNED; pxEndPoint->xRAData.eRAState = eRAStateIPTest; vRAProcess( pdFALSE, pxEndPoint ); @@ -521,6 +521,29 @@ return uxNewReloadTime; } + +/*-----------------------------------------------------------*/ + +/** + * @brief Assign an EUI64 address based on MAC address + * + * @param[out] pxIPAddress The assigned IPv6 address. + * @param[in] pxMACAddress The MAC address of the interface. + */ + static void vRAProcessAssignEUI64( IPv6_Address_t * pxIPAddress, + const MACAddress_t * pxMACAddress ) + { + memset( pxIPAddress, 0, sizeof( IPv6_Address_t ) ); + pxIPAddress->ucBytes[ 8 ] = pxMACAddress->ucBytes[ 0 ] ^ 0x2; + pxIPAddress->ucBytes[ 9 ] = pxMACAddress->ucBytes[ 1 ]; + pxIPAddress->ucBytes[ 10 ] = pxMACAddress->ucBytes[ 2 ]; + pxIPAddress->ucBytes[ 11 ] = 0xff; + pxIPAddress->ucBytes[ 12 ] = 0xfe; + pxIPAddress->ucBytes[ 13 ] = pxMACAddress->ucBytes[ 3 ]; + pxIPAddress->ucBytes[ 14 ] = pxMACAddress->ucBytes[ 4 ]; + pxIPAddress->ucBytes[ 15 ] = pxMACAddress->ucBytes[ 5 ]; + } + /*-----------------------------------------------------------*/ /** @@ -571,15 +594,25 @@ { size_t uxNeededSize; NetworkBufferDescriptor_t * pxNetworkBuffer; + IPv6_Address_t xIPAddress; + IPv6_Address_t * pxIPAddress; + + pxIPAddress = NULL; /* 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, NULL ); + if( pxEndPoint->bits.bWantEUI64 != pdFALSE_UNSIGNED ) + { + pxIPAddress = &xIPAddress; + vRAProcessAssignEUI64( pxIPAddress, &pxEndPoint->xMACAddress ); + } + + ( void ) FreeRTOS_CreateIPv6Address( &pxEndPoint->ipv6_settings.xIPAddress, &pxEndPoint->ipv6_settings.xPrefix, pxEndPoint->ipv6_settings.uxPrefixLength, pxIPAddress ); - FreeRTOS_printf( ( "RA: Creating a random IP-address\n" ) ); + FreeRTOS_printf( ( "RA: Creating an IP-address\n" ) ); } FreeRTOS_printf( ( "RA: Neighbour solicitation for %pip\n", ( void * ) pxEndPoint->ipv6_settings.xIPAddress.ucBytes ) ); diff --git a/source/include/FreeRTOS_Routing.h b/source/include/FreeRTOS_Routing.h index 0fa9fab88e..783b4e3926 100644 --- a/source/include/FreeRTOS_Routing.h +++ b/source/include/FreeRTOS_Routing.h @@ -200,6 +200,7 @@ #endif /* ipconfigUSE_DHCP */ #if ( ipconfigUSE_RA != 0 ) bWantRA : 1, /**< This end-point wants to use RA/SLAAC to obtain an IP-address. */ + bWantEUI64 : 1, /**< This end-point wants to use EUI64 for IP-address generation. */ #endif /* ipconfigUSE_RA */ bIPv6 : 1, /**< This end-point has an IP-address of type IPv6. */ #if ( ipconfigUSE_NETWORK_EVENT_HOOK != 0 ) From 1a06f701fc0f1638545af1e4146a49fe1ee48b32 Mon Sep 17 00:00:00 2001 From: Lukasz Janyst Date: Sun, 9 Nov 2025 15:21:18 +0100 Subject: [PATCH 4/4] Add test for the EUI64 branch coverage --- .../unit-test/FreeRTOS_RA/FreeRTOS_RA_utest.c | 50 +++++++++++++++++++ 1 file changed, 50 insertions(+) diff --git a/test/unit-test/FreeRTOS_RA/FreeRTOS_RA_utest.c b/test/unit-test/FreeRTOS_RA/FreeRTOS_RA_utest.c index e39fb67eae..12e2e8e181 100644 --- a/test/unit-test/FreeRTOS_RA/FreeRTOS_RA_utest.c +++ b/test/unit-test/FreeRTOS_RA/FreeRTOS_RA_utest.c @@ -959,6 +959,56 @@ void test_vReceiveRA_vRAProcess( void ) TEST_ASSERT_EQUAL( eRAStateIPWait, pxEndPoint->xRAData.eRAState ); } +/** + * @brief This function verify vReceiveRA success case for an EUI64 address. + */ +void test_vReceiveRA_vRAProcess_EUI64( void ) +{ + NetworkBufferDescriptor_t * pxNetworkBuffer, xNetworkBuffer; + EthernetPacketICMPv6RouterAdvertisementPrefixOption_t xICMPPacket; + NetworkInterface_t xInterface; + NetworkEndPoint_t xEndPoint, * pxEndPoint = &xEndPoint; + ICMPPrefixOption_IPv6_t * pxPrefixOption; + ICMPRouterAdvertisement_IPv6_t * pxAdvertisement; + + memset( &xNetworkBuffer, 0, sizeof( NetworkBufferDescriptor_t ) ); + memset( &xICMPPacket, 0, sizeof( xICMPPacket ) ); + memset( &xInterface, 0, sizeof( NetworkInterface_t ) ); + memset( &xEndPoint, 0, sizeof( NetworkEndPoint_t ) ); + + pxNetworkBuffer = &xNetworkBuffer; + pxNetworkBuffer->pucEthernetBuffer = ( uint8_t * ) &xICMPPacket; + pxNetworkBuffer->pxInterface = &xInterface; + pxNetworkBuffer->xDataLength = raHeaderBytesRA + raPrefixOptionlen; + pxAdvertisement = &xICMPPacket.xAdvertisement; + pxAdvertisement->usLifetime = pdTRUE_UNSIGNED; + + pxPrefixOption = &xICMPPacket.xPrefixOption; + pxPrefixOption->ucType = ndICMP_PREFIX_INFORMATION; + /* Only 1 option */ + pxPrefixOption->ucLength = 1; + + pxEndPoint->bits.bWantRA = pdTRUE_UNSIGNED; + pxEndPoint->bits.bWantEUI64 = pdTRUE_UNSIGNED; + pxEndPoint->xRAData.eRAState = eRAStateWait; + + FreeRTOS_FirstEndPoint_ExpectAnyArgsAndReturn( pxEndPoint ); + FreeRTOS_NextEndPoint_IgnoreAndReturn( NULL ); + + pxGetNetworkBufferWithDescriptor_ExpectAnyArgsAndReturn( NULL ); + vDHCP_RATimerReload_ExpectAnyArgs(); + + vReceiveRA( pxNetworkBuffer ); + + + TEST_ASSERT_EQUAL( pxEndPoint->ipv6_settings.uxPrefixLength, pxPrefixOption->ucPrefixLength ); + TEST_ASSERT_EQUAL( pdTRUE_UNSIGNED, pxEndPoint->xRAData.bits.bRouterReplied ); + TEST_ASSERT_EQUAL( 0, pxEndPoint->xRAData.uxRetryCount ); + TEST_ASSERT_EQUAL( FreeRTOS_ntohl( pxPrefixOption->ulPreferredLifeTime ), pxEndPoint->xRAData.ulPreferredLifeTime ); + TEST_ASSERT_EQUAL( pdFALSE_UNSIGNED, pxEndPoint->xRAData.bits.bIPAddressInUse ); + TEST_ASSERT_EQUAL( eRAStateIPWait, pxEndPoint->xRAData.eRAState ); +} + /** * @brief This function verify RA state machine * with RA NULL endpoint.