From 7eddca62f14ee03fbfbdb599ec8231f0e5650923 Mon Sep 17 00:00:00 2001 From: Hein Tibosch Date: Tue, 26 May 2026 00:28:33 +0800 Subject: [PATCH 1/3] Separating ARP/ND cache maintenance, from sending gratuitous ARP and UNA packets. --- source/FreeRTOS_ARP.c | 94 +++++++++++++++++++++++-------------- source/FreeRTOS_IP.c | 13 +++++ source/FreeRTOS_IP_Timers.c | 66 +++++++++++++++++++++++++- source/FreeRTOS_IP_Utils.c | 10 ++-- source/FreeRTOS_ND.c | 78 +++++++++++++++++++++++++++++- source/FreeRTOS_Sockets.c | 8 +++- 6 files changed, 220 insertions(+), 49 deletions(-) diff --git a/source/FreeRTOS_ARP.c b/source/FreeRTOS_ARP.c index 823ca87ec..2256c1223 100644 --- a/source/FreeRTOS_ARP.c +++ b/source/FreeRTOS_ARP.c @@ -64,7 +64,10 @@ /** @brief The time between gratuitous ARPs. */ #ifndef arpGRATUITOUS_ARP_PERIOD - #define arpGRATUITOUS_ARP_PERIOD ( pdMS_TO_TICKS( 20000U ) ) + +/* The normal time interval between advertisements + * is between 450 and 600 seconds */ + #define arpGRATUITOUS_ARP_PERIOD ( pdMS_TO_TICKS( 450000U ) ) #endif /** @brief When there is another device which has the same IP address as the IP address @@ -127,10 +130,6 @@ /*-----------------------------------------------------------*/ -/** @brief The time at which the last gratuitous ARP was sent. Gratuitous ARPs are used - * to ensure ARP tables are up to date and to detect IP address conflicts. */ - static TickType_t xLastGratuitousARPTime = 0U; - /** * @brief Process the ARP packets. * @@ -226,8 +225,8 @@ FreeRTOS_OutputARPRequest_Multi( pxTargetEndPoint, pxTargetEndPoint->ipv4_settings.ulIPAddress ); /* Since an ARP Request for this IP was just sent, do not send a gratuitous - * ARP for arpGRATUITOUS_ARP_PERIOD. */ - xLastGratuitousARPTime = xTaskGetTickCount(); + * ARP for arpGRATUITOUS_ARP_PERIOD, normally 20 seconds. */ + vARPGratuitousReload( pdMS_TO_TICKS( arpGRATUITOUS_ARP_PERIOD ) ); /* Note the time at which this request was sent. */ vTaskSetTimeOutState( &xARPClashTimeOut ); @@ -943,6 +942,18 @@ eReturn = eARPGetCacheEntryGateWay( pulIPAddress, pxMACAddress, ppxEndPoint ); } + if( *ppxEndPoint != NULL ) + { + struct xNetworkEndPoint * pxEndPoint = *ppxEndPoint; + + if( ( pxEndPoint->bits.bEndPointUp == pdFALSE ) || ( pxEndPoint->pxNetworkInterface->bits.bInterfaceUp == pdFALSE ) ) + { + /* _HT_ this printf is only used while testing. */ + FreeRTOS_printf( ( "eARPGetCacheEntry: endpoint that serves %xip is not up.\n", ( unsigned ) ( *pulIPAddress ) ) ); + eReturn = eResolutionFailed; + } + } + return eReturn; } /*-----------------------------------------------------------*/ @@ -1110,9 +1121,16 @@ * When the age reaches zero it is no longer considered valid. */ ( xARPCache[ x ].ucAge )--; + if( xARPCache[ x ].ucAge == 0U ) + { + /* The entry is no longer valid. Wipe it out. */ + iptraceARP_TABLE_ENTRY_EXPIRED( xARPCache[ x ].ulIPAddress ); + xARPCache[ x ].ulIPAddress = 0U; + } + /* If the entry is not yet valid, then it is waiting an ARP * reply, and the ARP request should be retransmitted. */ - if( xARPCache[ x ].ucValid == ( uint8_t ) pdFALSE ) + else if( xARPCache[ x ].ucValid == ( uint8_t ) pdFALSE ) { FreeRTOS_OutputARPRequest( xARPCache[ x ].ulIPAddress ); } @@ -1127,21 +1145,23 @@ { /* The age has just ticked down, with nothing to do. */ } - - if( xARPCache[ x ].ucAge == 0U ) - { - /* The entry is no longer valid. Wipe it out. */ - iptraceARP_TABLE_ENTRY_EXPIRED( xARPCache[ x ].ulIPAddress ); - xARPCache[ x ].ulIPAddress = 0U; } } } +/*-----------------------------------------------------------*/ - xTimeNow = xTaskGetTickCount(); - - if( ( xLastGratuitousARPTime == ( TickType_t ) 0 ) || ( ( xTimeNow - xLastGratuitousARPTime ) > ( TickType_t ) arpGRATUITOUS_ARP_PERIOD ) ) +/** + * @brief Send a Gratuitous ARP packet to allow this node to announce the IP-MAC + * mapping to the entire network. + */ + void vARPSendGratuitous( void ) + { + /* The IP-task is calling, allow it to actually send the packet. */ + if( xIsCallingFromIPTask() ) { - NetworkEndPoint_t * pxEndPoint = pxNetworkEndPoints; + NetworkEndPoint_t * pxEndPoint = FreeRTOS_FirstEndPoint( NULL ); + + FreeRTOS_printf( ( "Time active send event in vARPSendGratuitous from IP-task\n" ) ); while( pxEndPoint != NULL ) { @@ -1153,26 +1173,24 @@ } } - pxEndPoint = pxEndPoint->pxNext; - } - - xLastGratuitousARPTime = xTimeNow; + pxEndPoint = FreeRTOS_NextEndPoint( NULL, pxEndPoint ); } } -/*-----------------------------------------------------------*/ - -/** - * @brief Send a Gratuitous ARP packet to allow this node to announce the IP-MAC - * mapping to the entire network. - */ - void vARPSendGratuitous( void ) + else { - /* Setting xLastGratuitousARPTime to 0 will force a gratuitous ARP the next - * time vARPAgeCache() is called. */ - xLastGratuitousARPTime = ( TickType_t ) 0; - /* Let the IP-task call vARPAgeCache(). */ - ( void ) xSendEventToIPTask( eARPTimerEvent ); + FreeRTOS_printf( ( "Time active send event in vARPSendGratuitous from user API\n" ) ); + ( void ) xSendEventToIPTask( eARPGratuitousEvent ); + } + uint32_t ulRand = 75u; + xApplicationGetRandomNumber( &ulRand ); + if( ulRand > 150u ) + { + ulRand %= 150u; + } + /* The normal time interval between advertisements + * is between 450 and 600 seconds */ + vARPGratuitousReload ( arpGRATUITOUS_ARP_PERIOD + pdMS_TO_TICKS( 1000u * ulRand ) ); } /*-----------------------------------------------------------*/ @@ -1189,7 +1207,11 @@ { NetworkBufferDescriptor_t * pxNetworkBuffer; - if( ( pxEndPoint->bits.bIPv6 == pdFALSE_UNSIGNED ) && + NetworkInterface_t * pxInterface = pxEndPoint->pxNetworkInterface; + + /* If the interface is up, and it is an IPv4 end-point, and it has an IP address. */ + if( ( pxInterface->bits.bInterfaceUp == pdTRUE ) && + ( pxEndPoint->bits.bIPv6 == pdFALSE_UNSIGNED ) && ( pxEndPoint->ipv4_settings.ulIPAddress != 0U ) ) { /* This is called from the context of the IP event task, so a block time @@ -1300,7 +1322,7 @@ if( xLookupResult == eResolutionCacheMiss ) { - const TickType_t uxSleepTime = pdMS_TO_TICKS( 250U ); + const TickType_t uxSleepTime = pdMS_TO_TICKS( 20U ); /* We might use ipconfigMAX_ARP_RETRANSMISSIONS here. */ vTaskSetTimeOutState( &xTimeOut ); diff --git a/source/FreeRTOS_IP.c b/source/FreeRTOS_IP.c index dee5c62ba..fd709b55f 100644 --- a/source/FreeRTOS_IP.c +++ b/source/FreeRTOS_IP.c @@ -323,6 +323,13 @@ static void prvProcessIPEventsAndTimers( void ) #endif /* ( ipconfigUSE_IPv4 != 0 ) */ break; + case eARPGratuitousEvent: + /* Send a Gratuitous ARP request. */ + #if ipconfigIS_ENABLED( ipconfigUSE_IPv4 ) + vARPSendGratuitous(); + #endif /* ( ipconfigUSE_IPv4 != 0 ) */ + break; + case eNDTimerEvent: /* The ND Resolution timer has expired, process the cache. */ #if ipconfigIS_ENABLED( ipconfigUSE_IPv6 ) @@ -330,6 +337,12 @@ static void prvProcessIPEventsAndTimers( void ) #endif /* ( ipconfigUSE_IPv6 != 0 ) */ break; + case eNDSendUNAEvent: + /* The ARP Resolution timer has expired, process the cache. */ + #if ipconfigIS_ENABLED( ipconfigUSE_IPv6 ) + vSendUnsolicitedNeighborAdvertisement(); + #endif /* ( ipconfigUSE_IPv6 != 0 ) */ + break; case eSocketBindEvent: /* FreeRTOS_bind (a user API) wants the IP-task to bind a socket diff --git a/source/FreeRTOS_IP_Timers.c b/source/FreeRTOS_IP_Timers.c index bef32f6a5..436ea9b30 100644 --- a/source/FreeRTOS_IP_Timers.c +++ b/source/FreeRTOS_IP_Timers.c @@ -102,6 +102,9 @@ static void prvIPTimerReload( IPTimer_t * pxTimer, /** @brief ARP timer, to check its table entries. */ static IPTimer_t xARPTimer; + +/** @brief Gratuitous timer, to send our IP-address unsolicited. */ + static IPTimer_t xGratuitousTimer; #endif #if ipconfigIS_ENABLED( ipconfigUSE_IPv6 ) @@ -111,6 +114,9 @@ static void prvIPTimerReload( IPTimer_t * pxTimer, /** @brief ND timer, to check its table entries. */ static IPTimer_t xNDTimer; + +/** @brief Timer to send Unsolicited Neighbor Advertisements (UNA). */ + static IPTimer_t xUnaTimer; #endif #if ( ipconfigUSE_TCP != 0 ) /** @brief TCP timer, to check for timeouts, resends. */ @@ -156,7 +162,15 @@ TickType_t xCalculateSleepTime( void ) uxMaximumSleepTime = xARPTimer.ulRemainingTime; } } - #endif + + if( xGratuitousTimer.bActive != pdFALSE_UNSIGNED ) + { + if( xGratuitousTimer.ulRemainingTime < uxMaximumSleepTime ) + { + uxMaximumSleepTime = xGratuitousTimer.ulRemainingTime; + } + } + #endif /* if ipconfigIS_ENABLED( ipconfigUSE_IPv4 ) */ #if ipconfigIS_ENABLED( ipconfigUSE_IPv6 ) if( xNDTimer.bActive != pdFALSE_UNSIGNED ) @@ -166,7 +180,15 @@ TickType_t xCalculateSleepTime( void ) uxMaximumSleepTime = xNDTimer.ulRemainingTime; } } - #endif + + if( xUnaTimer.bActive != pdFALSE_UNSIGNED ) + { + if( xUnaTimer.ulRemainingTime < uxMaximumSleepTime ) + { + uxMaximumSleepTime = xUnaTimer.ulRemainingTime; + } + } + #endif /* if ipconfigIS_ENABLED( ipconfigUSE_IPv6 ) */ #if ( ipconfigUSE_DHCP == 1 ) || ( ipconfigUSE_RA == 1 ) { @@ -254,6 +276,13 @@ void vCheckNetworkTimers( void ) } #endif /* if ipconfigIS_ENABLED( ipconfigUSE_IPv4 ) */ + #if ipconfigIS_ENABLED( ipconfigUSE_IPv4 ) + if( prvIPTimerCheck( &xGratuitousTimer ) != pdFALSE ) + { + ( void ) xSendEventToIPTask( eARPGratuitousEvent ); + } + #endif /* if ipconfigIS_ENABLED( ipconfigUSE_IPv4 ) */ + #if ipconfigIS_ENABLED( ipconfigUSE_IPv6 ) /* Is it time for ND processing? */ if( prvIPTimerCheck( &xNDTimer ) != pdFALSE ) @@ -279,6 +308,11 @@ void vCheckNetworkTimers( void ) iptraceDELAYED_ND_TIMER_EXPIRED(); } } + + if( prvIPTimerCheck( &xUnaTimer ) != pdFALSE ) + { + ( void ) xSendEventToIPTask( eNDSendUNAEvent ); + } #endif /* if ipconfigIS_ENABLED( ipconfigUSE_IPv6 ) */ #if ( ipconfigUSE_DHCP == 1 ) || ( ipconfigUSE_RA == 1 ) @@ -482,6 +516,34 @@ static void prvIPTimerReload( IPTimer_t * pxTimer, #endif /*-----------------------------------------------------------*/ +#if ipconfigIS_ENABLED( ipconfigUSE_IPv4 ) + +/** + * @brief Sets the reload time of the ARP gratuitous timer and restarts it. + * + * @param[in] xTime Time to be reloaded into the ARP timer. + */ + void vARPGratuitousReload( TickType_t xTime ) + { + prvIPTimerReload( &xGratuitousTimer, xTime ); + } +#endif +/*-----------------------------------------------------------*/ + +#if ipconfigIS_ENABLED( ipconfigUSE_IPv6 ) + +/** + * @brief Sets the reload time of the ARP gratuitous timer. + * + * @param[in] xTime Time to be reloaded into the timer. + */ + void vND_UNA_TimerReload( TickType_t xTime ) + { + prvIPTimerReload( &xUnaTimer, xTime ); + } +#endif +/*-----------------------------------------------------------*/ + #if ipconfigIS_ENABLED( ipconfigUSE_IPv6 ) /** diff --git a/source/FreeRTOS_IP_Utils.c b/source/FreeRTOS_IP_Utils.c index 45672d035..f0fbdb7b9 100644 --- a/source/FreeRTOS_IP_Utils.c +++ b/source/FreeRTOS_IP_Utils.c @@ -822,13 +822,9 @@ void prvProcessNetworkDownEvent( struct xNetworkInterface * pxInterface ) configASSERT( pxInterface != NULL ); configASSERT( pxInterface->pfInitialise != NULL ); - /* Stop the Address Resolution timer while there is no network. */ - #if ipconfigIS_ENABLED( ipconfigUSE_IPv4 ) - vIPSetARPTimerEnableState( pdFALSE ); - #endif - #if ipconfigIS_ENABLED( ipconfigUSE_IPv6 ) - vIPSetNDTimerEnableState( pdFALSE ); - #endif + + /* Let the ARP and ND-timers vIPSetARPTimerEnableState() + * and vIPSetNDTimerEnableState() running. */ /* The first network down event is generated by the IP stack itself to * initialise the network hardware, so do not call the network down event diff --git a/source/FreeRTOS_ND.c b/source/FreeRTOS_ND.c index 7fd2e7662..e0b317e90 100644 --- a/source/FreeRTOS_ND.c +++ b/source/FreeRTOS_ND.c @@ -72,6 +72,15 @@ */ #define ndMAX_CACHE_AGE_BEFORE_NEW_ND_SOLICITATION ( 3U ) +/** @brief The time at which the last Unsolicited Neighbor Advertisements (UNA) was sent. + * UNAs are used to ensure ND tables are up to date and to detect IPv6 address conflicts. */ + #define ipconfigHAS_UNSOLICITED_NEIGHBOR_ADVERTISEMENT 1 + + #ifndef ndGRATUITOUS_UNA_PERIOD + #define ndGRATUITOUS_UNA_PERIOD pdMS_TO_TICKS( 20000 ) + #endif + void vSendUnsolicitedNeighborAdvertisement( void ); + /** @brief All nodes on the local network segment: IP address. */ const uint8_t pcLOCAL_ALL_NODES_MULTICAST_IP[ ipSIZE_OF_IPv6_ADDRESS ] = { 0xffU, 0x02U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x01U }; /* ff02::1 */ /** @brief All nodes on the local network segment: MAC address. */ @@ -98,6 +107,8 @@ static NDCacheRow_t xNDCache[ ipconfigND_CACHE_ENTRIES ]; + static const IPv6_Address_t xDefaultIPv6Address = { { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 } }; /*-----------------------------------------------------------*/ /* @@ -422,6 +433,63 @@ } /*-----------------------------------------------------------*/ + #if ( ipconfigHAS_UNSOLICITED_NEIGHBOR_ADVERTISEMENT != 0 ) + void vSendUnsolicitedNeighborAdvertisement( void ) + { + /* The IP-task is calling, allow it to actually send the packet. */ + if( xIsCallingFromIPTask() ) + { + NetworkEndPoint_t * pxEndPoint = FreeRTOS_FirstEndPoint( NULL ); + BaseType_t xCount = 0; + + while( pxEndPoint != NULL ) + { + BaseType_t isZero = ( memcmp( pxEndPoint->ipv6_settings.xIPAddress.ucBytes, xDefaultIPv6Address.ucBytes, sizeof( xDefaultIPv6Address.ucBytes ) ) == 0 ) ? pdTRUE : pdFALSE; + + if( ( pxEndPoint->bits.bEndPointUp != pdFALSE_UNSIGNED ) && ( isZero == pdFALSE ) ) + { + if( pxEndPoint->bits.bIPv6 == pdTRUE_UNSIGNED ) /* LCOV_EXCL_BR_LINE */ + { + IPv6_Type_t xType = xIPv6_GetIPType( &pxEndPoint->ipv6_settings.xIPAddress ); + BaseType_t xAdvertiseIt = pdFALSE; + + switch( xType ) + { + case eIPv6_Global: + case eIPv6_LinkLocal: + case eIPv6_UniqueLocal: + xAdvertiseIt = pdTRUE; + break; + + default: + break; + } + + if( xAdvertiseIt != pdFALSE ) + { + FreeRTOS_OutputAdvertiseIPv6( pxEndPoint ); + xCount++; + } + } + } + + pxEndPoint = FreeRTOS_NextEndPoint( NULL, pxEndPoint ); + } + } /* if( xIsCallingFromIPTask() ) */ + uint32_t ulRand = 75u; + xApplicationGetRandomNumber( &ulRand ); + if( ulRand > 150u ) + { + ulRand %= 150u; + } + /* The normal time interval between advertisements + * is between 450 and 600 seconds */ + TickType_t uxPeriod = pdMS_TO_TICKS( ndGRATUITOUS_UNA_PERIOD ) + pdMS_TO_TICKS( 1000u * ulRand ); + vND_UNA_TimerReload ( ); + FreeRTOS_printf( ("vND_UNA_TimerReload = %u\n", uxPeriod ) ); + } + #endif /* if ipconfigHAS_UNSOLICITED_NEIGHBOR_ADVERTISEMENT > 0 */ + /** * @brief A call to this function will clear the ND cache. * @param[in] pxEndPoint only clean entries with this end-point, or when NULL, @@ -1148,6 +1216,7 @@ { pxICMPHeader_IPv6->ucTypeOfMessage = ipICMP_NEIGHBOR_ADVERTISEMENT_IPv6; pxICMPHeader_IPv6->ucTypeOfService = 0U; + pxICMPHeader_IPv6->ulReserved = ndICMPv6_FLAG_SOLICITED | ndICMPv6_FLAG_UPDATE; pxICMPHeader_IPv6->ulReserved = FreeRTOS_htonl( pxICMPHeader_IPv6->ulReserved ); @@ -1281,7 +1350,8 @@ uxICMPSize = sizeof( ICMPHeader_IPv6_t ); pxICMPHeader_IPv6->ucTypeOfMessage = ipICMP_NEIGHBOR_ADVERTISEMENT_IPv6; pxICMPHeader_IPv6->ucTypeOfService = 0; - pxICMPHeader_IPv6->ulReserved = ndICMPv6_FLAG_SOLICITED | ndICMPv6_FLAG_UPDATE; + /* The advertisement is not ndICMPv6_FLAG_SOLICITED. */ + pxICMPHeader_IPv6->ulReserved = ndICMPv6_FLAG_UPDATE; pxICMPHeader_IPv6->ulReserved = FreeRTOS_htonl( pxICMPHeader_IPv6->ulReserved ); /* Type of option. */ @@ -1454,7 +1524,11 @@ ( void ) memset( &( pcName ), 0, sizeof( pcName ) ); eResult = eNDGetCacheEntry( pxIPAddress, &xMACAddress, &pxEndPoint ); - FreeRTOS_printf( ( "xCheckRequiresNDResolution: eResult %s with EP %s\n", ( eResult == eResolutionCacheMiss ) ? "Miss" : ( eResult == eResolutionCacheHit ) ? "Hit" : "Error", pcEndpointName( pxEndPoint, pcName, sizeof pcName ) ) ); + + if( eResult != eResolutionCacheHit ) + { + FreeRTOS_printf( ( "xCheckRequiresNDResolution: eResult %s with EP %s\n", ( eResult == eResolutionCacheMiss ) ? "Miss" : ( eResult == eResolutionCacheHit ) ? "Hit" : "Error", pcEndpointName( pxEndPoint, pcName, sizeof pcName ) ) ); + } if( eResult == eResolutionCacheMiss ) { diff --git a/source/FreeRTOS_Sockets.c b/source/FreeRTOS_Sockets.c index 11fa83305..5e2d2b2ae 100644 --- a/source/FreeRTOS_Sockets.c +++ b/source/FreeRTOS_Sockets.c @@ -1813,7 +1813,11 @@ static BaseType_t prvSocketBindAdd( FreeRTOS_Socket_t * pxSocket, #if ( ipconfigUSE_IPv6 != 0 ) if( pxAddress->sin_family == ( uint8_t ) FREERTOS_AF_INET6 ) { - ( void ) memcpy( pxSocket->xLocalAddress.xIP_IPv6.ucBytes, pxAddress->sin_address.xIP_IPv6.ucBytes, sizeof( pxSocket->xLocalAddress.xIP_IPv6.ucBytes ) ); + if( memcmp( FreeRTOS_in6addr_any.ucBytes, pxAddress->sin_address.xIP_IPv6.ucBytes, ipSIZE_OF_IPv6_ADDRESS ) != 0 ) + { + ( void ) memcpy( pxSocket->xLocalAddress.xIP_IPv6.ucBytes, pxAddress->sin_address.xIP_IPv6.ucBytes, sizeof( pxSocket->xLocalAddress.xIP_IPv6.ucBytes ) ); + pxSocket->pxEndPoint = FreeRTOS_FindEndPointOnIP_IPv6( &( pxAddress->sin_address.xIP_IPv6 ) ); + } } else #endif /* ( ipconfigUSE_IPv6 != 0 ) */ @@ -1831,7 +1835,7 @@ static BaseType_t prvSocketBindAdd( FreeRTOS_Socket_t * pxSocket, } #if ( ipconfigUSE_IPv4 != 0 ) - if( pxSocket->pxEndPoint != NULL ) + if( ( pxSocket->pxEndPoint != NULL ) && ( pxAddress->sin_family == ( uint8_t ) FREERTOS_AF_INET4 ) ) { pxSocket->xLocalAddress.ulIP_IPv4 = FreeRTOS_ntohl( pxSocket->pxEndPoint->ipv4_settings.ulIPAddress ); } From e2bec105494120222aff00511aba1ad53ac50ccd Mon Sep 17 00:00:00 2001 From: Hein Tibosch Date: Tue, 26 May 2026 00:41:15 +0800 Subject: [PATCH 2/3] Some forgotten changes --- source/include/FreeRTOSIPConfigDefaults.h | 8 ++++++++ source/include/FreeRTOS_IP_Private.h | 22 ++++++++++++---------- source/include/FreeRTOS_IP_Timers.h | 10 ++++++++++ 3 files changed, 30 insertions(+), 10 deletions(-) diff --git a/source/include/FreeRTOSIPConfigDefaults.h b/source/include/FreeRTOSIPConfigDefaults.h index 2b2bfbe17..750a6cb27 100644 --- a/source/include/FreeRTOSIPConfigDefaults.h +++ b/source/include/FreeRTOSIPConfigDefaults.h @@ -2774,6 +2774,14 @@ STATIC_ASSERT( ipconfigDNS_SEND_BLOCK_TIME_TICKS <= portMAX_DELAY ); #error ipconfigARP_CACHE_ENTRIES overflows a size_t #endif +#indef ipARP_TIMER_PERIOD_MS + #define ipARP_TIMER_PERIOD_MS +#endif + +#indef ipND_TIMER_PERIOD_MS + #define ipND_TIMER_PERIOD_MS +#endif + /*---------------------------------------------------------------------------*/ /* diff --git a/source/include/FreeRTOS_IP_Private.h b/source/include/FreeRTOS_IP_Private.h index 881adfccf..5e9c7064f 100644 --- a/source/include/FreeRTOS_IP_Private.h +++ b/source/include/FreeRTOS_IP_Private.h @@ -76,16 +76,18 @@ typedef enum eNetworkTxEvent, /* 2: Let the IP-task send a network packet. */ eARPTimerEvent, /* 3: The ARP timer expired. */ eNDTimerEvent, /* 4: The ND timer expired. */ - eStackTxEvent, /* 5: The software stack has queued a packet to transmit. */ - eDHCPEvent, /* 6: Process the DHCP state machine. */ - eTCPTimerEvent, /* 7: See if any TCP socket needs attention. */ - eTCPAcceptEvent, /* 8: Client API FreeRTOS_accept() waiting for client connections. */ - eTCPNetStat, /* 9: IP-task is asked to produce a netstat listing. */ - eSocketBindEvent, /*10: Send a message to the IP-task to bind a socket to a port. */ - eSocketCloseEvent, /*11: Send a message to the IP-task to close a socket. */ - eSocketSelectEvent, /*12: Send a message to the IP-task for select(). */ - eSocketSignalEvent, /*13: A socket must be signalled. */ - eSocketSetDeleteEvent /*14: A socket set must be deleted. */ + eARPGratuitousEvent, /* 5: For IPv4 : send a Gratuitous ARP . */ + eNDSendUNAEvent, /* 6: For IPv6 : send an UNA packet. */ + eStackTxEvent, /* 7: The software stack has queued a packet to transmit. */ + eDHCPEvent, /* 8: Process the DHCP state machine. */ + eTCPTimerEvent, /* 9: See if any TCP socket needs attention. */ + eTCPAcceptEvent, /*10: Client API FreeRTOS_accept() waiting for client connections. */ + eTCPNetStat, /*11: IP-task is asked to produce a netstat listing. */ + eSocketBindEvent, /*12: Send a message to the IP-task to bind a socket to a port. */ + eSocketCloseEvent, /*13: Send a message to the IP-task to close a socket. */ + eSocketSelectEvent, /*14: Send a message to the IP-task for select(). */ + eSocketSignalEvent, /*15: A socket must be signalled. */ + eSocketSetDeleteEvent /*16: A socket set must be deleted. */ } eIPEvent_t; /** diff --git a/source/include/FreeRTOS_IP_Timers.h b/source/include/FreeRTOS_IP_Timers.h index 27471c8ca..df32b57fb 100644 --- a/source/include/FreeRTOS_IP_Timers.h +++ b/source/include/FreeRTOS_IP_Timers.h @@ -94,6 +94,11 @@ void vIPSetTCPTimerExpiredState( BaseType_t xExpiredState ); */ void vIPSetARPTimerEnableState( BaseType_t xEnableState ); +/* + * Set the pediod of the Gratuitous ARP timer. + */ + void vARPGratuitousReload( TickType_t xTime ); + /* * Enable or disable the ARP resolution timer. */ @@ -107,6 +112,11 @@ void vIPSetTCPTimerExpiredState( BaseType_t xExpiredState ); */ void vNDTimerReload( TickType_t xTime ); +/** + * Sets the reload time of sending an UNA and restarts it. + */ +void vND_UNA_TimerReload( TickType_t xTime ); + /* * Start an ND Resolution timer. */ From d7f94e2f2da3365611f383e32b4ec0a3a35466d6 Mon Sep 17 00:00:00 2001 From: Hein Tibosch Date: Sun, 31 May 2026 13:57:20 +0800 Subject: [PATCH 3/3] Cleaned up the changes, tested in real hardware --- source/FreeRTOS_ARP.c | 44 ++++++------------- source/FreeRTOS_IP.c | 5 +++ source/FreeRTOS_IP_Timers.c | 53 +++++++++++++++++++---- source/FreeRTOS_ND.c | 23 +++------- source/FreeRTOS_Sockets.c | 10 ++--- source/include/FreeRTOSIPConfigDefaults.h | 19 ++++++-- source/include/FreeRTOS_IP_Timers.h | 2 +- 7 files changed, 89 insertions(+), 67 deletions(-) diff --git a/source/FreeRTOS_ARP.c b/source/FreeRTOS_ARP.c index 2256c1223..6b70acd4d 100644 --- a/source/FreeRTOS_ARP.c +++ b/source/FreeRTOS_ARP.c @@ -62,14 +62,6 @@ * entry is still valid and can therefore be refreshed. */ #define arpMAX_ARP_AGE_BEFORE_NEW_ARP_REQUEST ( 3U ) -/** @brief The time between gratuitous ARPs. */ - #ifndef arpGRATUITOUS_ARP_PERIOD - -/* The normal time interval between advertisements - * is between 450 and 600 seconds */ - #define arpGRATUITOUS_ARP_PERIOD ( pdMS_TO_TICKS( 450000U ) ) - #endif - /** @brief When there is another device which has the same IP address as the IP address * of this device, a defensive ARP request should be sent out. However, according to * RFC 5227 section 1.1, there must be a minimum interval of 10 seconds between @@ -225,8 +217,8 @@ FreeRTOS_OutputARPRequest_Multi( pxTargetEndPoint, pxTargetEndPoint->ipv4_settings.ulIPAddress ); /* Since an ARP Request for this IP was just sent, do not send a gratuitous - * ARP for arpGRATUITOUS_ARP_PERIOD, normally 20 seconds. */ - vARPGratuitousReload( pdMS_TO_TICKS( arpGRATUITOUS_ARP_PERIOD ) ); + * ARP for arpGRATUITOUS_ARP_PERIOD_MS, normally 300 seconds. */ + vGARP_TimerReload( arpGRATUITOUS_ARP_PERIOD_MS ); /* Note the time at which this request was sent. */ vTaskSetTimeOutState( &xARPClashTimeOut ); @@ -1145,9 +1137,9 @@ { /* The age has just ticked down, with nothing to do. */ } - } } } + } /*-----------------------------------------------------------*/ /** @@ -1161,8 +1153,6 @@ { NetworkEndPoint_t * pxEndPoint = FreeRTOS_FirstEndPoint( NULL ); - FreeRTOS_printf( ( "Time active send event in vARPSendGratuitous from IP-task\n" ) ); - while( pxEndPoint != NULL ) { if( ( pxEndPoint->bits.bEndPointUp != pdFALSE_UNSIGNED ) && ( pxEndPoint->ipv4_settings.ulIPAddress != 0U ) ) @@ -1174,23 +1164,15 @@ } pxEndPoint = FreeRTOS_NextEndPoint( NULL, pxEndPoint ); + } } - } else - { - /* Let the IP-task call vARPAgeCache(). */ - FreeRTOS_printf( ( "Time active send event in vARPSendGratuitous from user API\n" ) ); + { + /* Let the IP-task call vARPAgeCache(). */ ( void ) xSendEventToIPTask( eARPGratuitousEvent ); } - uint32_t ulRand = 75u; - xApplicationGetRandomNumber( &ulRand ); - if( ulRand > 150u ) - { - ulRand %= 150u; - } - /* The normal time interval between advertisements - * is between 450 and 600 seconds */ - vARPGratuitousReload ( arpGRATUITOUS_ARP_PERIOD + pdMS_TO_TICKS( 1000u * ulRand ) ); + + vGARP_TimerReload( arpGRATUITOUS_ARP_PERIOD_MS ); } /*-----------------------------------------------------------*/ @@ -1207,11 +1189,11 @@ { NetworkBufferDescriptor_t * pxNetworkBuffer; - NetworkInterface_t * pxInterface = pxEndPoint->pxNetworkInterface; + NetworkInterface_t * pxInterface = pxEndPoint->pxNetworkInterface; - /* If the interface is up, and it is an IPv4 end-point, and it has an IP address. */ - if( ( pxInterface->bits.bInterfaceUp == pdTRUE ) && - ( pxEndPoint->bits.bIPv6 == pdFALSE_UNSIGNED ) && + /* If the interface is up, and it is an IPv4 end-point, and it has an IP address. */ + if( ( pxInterface->bits.bInterfaceUp == pdTRUE ) && + ( pxEndPoint->bits.bIPv6 == pdFALSE_UNSIGNED ) && ( pxEndPoint->ipv4_settings.ulIPAddress != 0U ) ) { /* This is called from the context of the IP event task, so a block time @@ -1322,7 +1304,7 @@ if( xLookupResult == eResolutionCacheMiss ) { - const TickType_t uxSleepTime = pdMS_TO_TICKS( 20U ); + const TickType_t uxSleepTime = pdMS_TO_TICKS( 20U ); /* We might use ipconfigMAX_ARP_RETRANSMISSIONS here. */ vTaskSetTimeOutState( &xTimeOut ); diff --git a/source/FreeRTOS_IP.c b/source/FreeRTOS_IP.c index fd709b55f..15d52d9fa 100644 --- a/source/FreeRTOS_IP.c +++ b/source/FreeRTOS_IP.c @@ -343,6 +343,7 @@ static void prvProcessIPEventsAndTimers( void ) vSendUnsolicitedNeighborAdvertisement(); #endif /* ( ipconfigUSE_IPv6 != 0 ) */ break; + case eSocketBindEvent: /* FreeRTOS_bind (a user API) wants the IP-task to bind a socket @@ -534,11 +535,15 @@ static void prvIPTask_Initialise( void ) #if ipconfigIS_ENABLED( ipconfigUSE_IPv4 ) /* Mark the ARP timer as inactive since we are not waiting on any resolution as of now. */ vIPSetARPResolutionTimerEnableState( pdFALSE ); + /* Make sure the GARP timer is started. */ + vGARP_TimerReload( arpGRATUITOUS_ARP_PERIOD_MS ); #endif #if ipconfigIS_ENABLED( ipconfigUSE_IPv6 ) /* Mark the ND timer as inactive since we are not waiting on any resolution as of now. */ vIPSetNDResolutionTimerEnableState( pdFALSE ); + /* Make sure the UNA timer is started. */ + vND_UNA_TimerReload( ndGRATUITOUS_UNA_PERIOD_MS ); #endif #if ( ( ipconfigDNS_USE_CALLBACKS != 0 ) && ( ipconfigUSE_DNS != 0 ) ) diff --git a/source/FreeRTOS_IP_Timers.c b/source/FreeRTOS_IP_Timers.c index 436ea9b30..afd23f443 100644 --- a/source/FreeRTOS_IP_Timers.c +++ b/source/FreeRTOS_IP_Timers.c @@ -523,25 +523,62 @@ static void prvIPTimerReload( IPTimer_t * pxTimer, * * @param[in] xTime Time to be reloaded into the ARP timer. */ - void vARPGratuitousReload( TickType_t xTime ) + void vGARP_TimerReload( const TickType_t uxTimeMs ) { - prvIPTimerReload( &xGratuitousTimer, xTime ); + /* ulRandMs gives a random variation expressed in ms. */ + /* E.g. every 300 seconds +/- 10% (randomized per endpoint). */ + uint32_t ulRandMs = uxTimeMs; + TickType_t uxPeriod; + const TickType_t ulMaxMs = uxTimeMs / 10u; + + xApplicationGetRandomNumber( &ulRandMs ); + + if( ulRandMs > ulMaxMs ) + { + ulRandMs %= ulMaxMs; + } + + uxPeriod = pdMS_TO_TICKS( uxTimeMs ) + pdMS_TO_TICKS( ulRandMs ); + prvIPTimerReload( &xGratuitousTimer, uxPeriod ); + FreeRTOS_printf( ( "vGARP_TimerReload: %u + %u = %u ms\n", + ( unsigned ) uxTimeMs, + ( unsigned ) ulRandMs, + ( unsigned ) uxPeriod ) ); } -#endif +#endif /* if ipconfigIS_ENABLED( ipconfigUSE_IPv4 ) */ /*-----------------------------------------------------------*/ #if ipconfigIS_ENABLED( ipconfigUSE_IPv6 ) /** - * @brief Sets the reload time of the ARP gratuitous timer. + * @brief Sets the reload time of a UNA: + * Unsolicited Neighbor Advertisements. * - * @param[in] xTime Time to be reloaded into the timer. + * @param[in] uxTimeMs Time (ms) to be reloaded into the timer. */ - void vND_UNA_TimerReload( TickType_t xTime ) + void vND_UNA_TimerReload( const TickType_t uxTimeMs ) { - prvIPTimerReload( &xUnaTimer, xTime ); + /* ulRandMs gives a random variation expressed in ms. */ + /* E.g. every 300 seconds +/- 10% (randomized per endpoint). */ + uint32_t ulRandMs = uxTimeMs; + TickType_t uxPeriod; + const TickType_t ulMaxMs = uxTimeMs / 10u; + + xApplicationGetRandomNumber( &ulRandMs ); + + if( ulRandMs > ulMaxMs ) + { + ulRandMs %= ulMaxMs; + } + + uxPeriod = pdMS_TO_TICKS( uxTimeMs ) + pdMS_TO_TICKS( ulRandMs ); + prvIPTimerReload( &xUnaTimer, uxPeriod ); + FreeRTOS_printf( ( "vSUNA_TimerReload: %u + %u = %u ms\n", + ( unsigned ) uxTimeMs, + ( unsigned ) ulRandMs, + ( unsigned ) uxPeriod ) ); } -#endif +#endif /* if ipconfigIS_ENABLED( ipconfigUSE_IPv6 ) */ /*-----------------------------------------------------------*/ #if ipconfigIS_ENABLED( ipconfigUSE_IPv6 ) diff --git a/source/FreeRTOS_ND.c b/source/FreeRTOS_ND.c index e0b317e90..dd203d0ee 100644 --- a/source/FreeRTOS_ND.c +++ b/source/FreeRTOS_ND.c @@ -72,13 +72,6 @@ */ #define ndMAX_CACHE_AGE_BEFORE_NEW_ND_SOLICITATION ( 3U ) -/** @brief The time at which the last Unsolicited Neighbor Advertisements (UNA) was sent. - * UNAs are used to ensure ND tables are up to date and to detect IPv6 address conflicts. */ - #define ipconfigHAS_UNSOLICITED_NEIGHBOR_ADVERTISEMENT 1 - - #ifndef ndGRATUITOUS_UNA_PERIOD - #define ndGRATUITOUS_UNA_PERIOD pdMS_TO_TICKS( 20000 ) - #endif void vSendUnsolicitedNeighborAdvertisement( void ); /** @brief All nodes on the local network segment: IP address. */ @@ -434,6 +427,9 @@ /*-----------------------------------------------------------*/ #if ( ipconfigHAS_UNSOLICITED_NEIGHBOR_ADVERTISEMENT != 0 ) + +/* vSendUnsolicitedNeighborAdvertisement() will be called + * when an 'eNDSendUNAEvent' is received. */ void vSendUnsolicitedNeighborAdvertisement( void ) { /* The IP-task is calling, allow it to actually send the packet. */ @@ -476,17 +472,8 @@ pxEndPoint = FreeRTOS_NextEndPoint( NULL, pxEndPoint ); } } /* if( xIsCallingFromIPTask() ) */ - uint32_t ulRand = 75u; - xApplicationGetRandomNumber( &ulRand ); - if( ulRand > 150u ) - { - ulRand %= 150u; - } - /* The normal time interval between advertisements - * is between 450 and 600 seconds */ - TickType_t uxPeriod = pdMS_TO_TICKS( ndGRATUITOUS_UNA_PERIOD ) + pdMS_TO_TICKS( 1000u * ulRand ); - vND_UNA_TimerReload ( ); - FreeRTOS_printf( ("vND_UNA_TimerReload = %u\n", uxPeriod ) ); + + vND_UNA_TimerReload( ndGRATUITOUS_UNA_PERIOD_MS ); } #endif /* if ipconfigHAS_UNSOLICITED_NEIGHBOR_ADVERTISEMENT > 0 */ diff --git a/source/FreeRTOS_Sockets.c b/source/FreeRTOS_Sockets.c index 5e2d2b2ae..29c890e5e 100644 --- a/source/FreeRTOS_Sockets.c +++ b/source/FreeRTOS_Sockets.c @@ -1813,11 +1813,11 @@ static BaseType_t prvSocketBindAdd( FreeRTOS_Socket_t * pxSocket, #if ( ipconfigUSE_IPv6 != 0 ) if( pxAddress->sin_family == ( uint8_t ) FREERTOS_AF_INET6 ) { - if( memcmp( FreeRTOS_in6addr_any.ucBytes, pxAddress->sin_address.xIP_IPv6.ucBytes, ipSIZE_OF_IPv6_ADDRESS ) != 0 ) - { - ( void ) memcpy( pxSocket->xLocalAddress.xIP_IPv6.ucBytes, pxAddress->sin_address.xIP_IPv6.ucBytes, sizeof( pxSocket->xLocalAddress.xIP_IPv6.ucBytes ) ); - pxSocket->pxEndPoint = FreeRTOS_FindEndPointOnIP_IPv6( &( pxAddress->sin_address.xIP_IPv6 ) ); - } + if( memcmp( FreeRTOS_in6addr_any.ucBytes, pxAddress->sin_address.xIP_IPv6.ucBytes, ipSIZE_OF_IPv6_ADDRESS ) != 0 ) + { + ( void ) memcpy( pxSocket->xLocalAddress.xIP_IPv6.ucBytes, pxAddress->sin_address.xIP_IPv6.ucBytes, sizeof( pxSocket->xLocalAddress.xIP_IPv6.ucBytes ) ); + pxSocket->pxEndPoint = FreeRTOS_FindEndPointOnIP_IPv6( &( pxAddress->sin_address.xIP_IPv6 ) ); + } } else #endif /* ( ipconfigUSE_IPv6 != 0 ) */ diff --git a/source/include/FreeRTOSIPConfigDefaults.h b/source/include/FreeRTOSIPConfigDefaults.h index 750a6cb27..661deefd6 100644 --- a/source/include/FreeRTOSIPConfigDefaults.h +++ b/source/include/FreeRTOSIPConfigDefaults.h @@ -2774,12 +2774,12 @@ STATIC_ASSERT( ipconfigDNS_SEND_BLOCK_TIME_TICKS <= portMAX_DELAY ); #error ipconfigARP_CACHE_ENTRIES overflows a size_t #endif -#indef ipARP_TIMER_PERIOD_MS - #define ipARP_TIMER_PERIOD_MS +#ifndef ipARP_TIMER_PERIOD_MS + #define ipARP_TIMER_PERIOD_MS 300000u #endif -#indef ipND_TIMER_PERIOD_MS - #define ipND_TIMER_PERIOD_MS +#ifndef ipND_TIMER_PERIOD_MS + #define ipND_TIMER_PERIOD_MS 300000u #endif /*---------------------------------------------------------------------------*/ @@ -3524,6 +3524,17 @@ STATIC_ASSERT( ipconfigDNS_SEND_BLOCK_TIME_TICKS <= portMAX_DELAY ); #define ipconfigISO_STRICTNESS_VIOLATION_END #endif +/* To be documented: */ + +#ifndef ipconfigUSE_NAMED_ENDPOINTS + /* or DISABLE depending on your desired default */ + #define ipconfigUSE_NAMED_ENDPOINTS ipconfigENABLE +#endif + +#if ( ipconfigUSE_NAMED_ENDPOINTS == ipconfigENABLE ) + #define ipconfigENDPOINT_NAME_LENGTH 32 +#endif + /*---------------------------------------------------------------------------*/ /* Should only be included here, ensures trace config is set first. */ diff --git a/source/include/FreeRTOS_IP_Timers.h b/source/include/FreeRTOS_IP_Timers.h index df32b57fb..259a6b4aa 100644 --- a/source/include/FreeRTOS_IP_Timers.h +++ b/source/include/FreeRTOS_IP_Timers.h @@ -115,7 +115,7 @@ void vIPSetTCPTimerExpiredState( BaseType_t xExpiredState ); /** * Sets the reload time of sending an UNA and restarts it. */ -void vND_UNA_TimerReload( TickType_t xTime ); + void vND_UNA_TimerReload( TickType_t xTime ); /* * Start an ND Resolution timer.