From d8a1fb6d4f76d99b9a54f59167d97c3986204b67 Mon Sep 17 00:00:00 2001 From: Rajkamal CV Date: Tue, 21 Jul 2026 07:21:28 +0530 Subject: [PATCH 1/4] RDKB-66077: Ensure the client is disconnected by ethactive flag 1. Use the ethactive flag, to confirm the client is disconnected. 2. Skip FAILED/INCOMPLETE ARP states alone to get valid connectivity of the clients 3. Have a single debounce to check the client is really disconnected. --- source/TR-181/board_sbapi/eth_hal_interface.c | 51 ++++++++++++++++--- 1 file changed, 45 insertions(+), 6 deletions(-) diff --git a/source/TR-181/board_sbapi/eth_hal_interface.c b/source/TR-181/board_sbapi/eth_hal_interface.c index 8ac88b9..a96eff1 100644 --- a/source/TR-181/board_sbapi/eth_hal_interface.c +++ b/source/TR-181/board_sbapi/eth_hal_interface.c @@ -33,6 +33,10 @@ #endif /*_SR213_PRODUCT_REQ_*/ #define ETH_POLLING_PERIOD 180 #define ETH_NODE_HASH_SIZE 256 +/* Number of consecutive zero-associated-device polls required before treating + * it as a genuine all-clients-disconnected event. Debounces transient + * switch-FDB/HAL misses for still-connected idle clients. */ +#define ETH_ZERO_COUNT_THRESHOLD 2 CcspHalExtSw_ethAssociatedDevice_callback AssociatedDevice_callback = NULL; @@ -63,7 +67,12 @@ int ValidateClient(char *mac) FILE *fp1 = NULL; errno_t rc = -1; //Need to ignore brlan1 - XHS clients when during CB case - v_secure_system("ip nei show | grep -v brlan1 | grep -i %s | grep -i REACHABLE > " ARP_CACHE, mac); + /* A still-connected but idle client's neighbour entry decays from + * REACHABLE to STALE/DELAY/PROBE within ~30s, while this poll runs only + * every ETH_POLLING_PERIOD (180s). Treat any known neighbour state as + * valid and reject only FAILED/INCOMPLETE (truly gone) so that idle + * clients are not falsely disconnected. */ + v_secure_system("ip nei show | grep -v brlan1 | grep -i %s | grep -iEv 'FAILED|INCOMPLETE' > " ARP_CACHE, mac); if ( (fp1 = fopen(ARP_CACHE, "r")) == NULL ) { return ret; @@ -390,6 +399,7 @@ void* CcspHalExtSw_AssociatedDeviceMonitorThread( void *arg ) INT iLoopCount; BOOL bProcessFurther = TRUE; static BOOL isDeleteAllDone = FALSE; + static INT uiZeroCountPolls = 0; CcspTraceDebug((" Iteration Start\n") ); @@ -431,11 +441,25 @@ void* CcspHalExtSw_AssociatedDeviceMonitorThread( void *arg ) if( 0 == ulTotalEthDeviceCount ) { /* - * We should not do more than one time when all host disconnected case again - * and again + * The switch FDB/HAL can momentarily report zero associated + * devices for a still-connected idle client (its MAC aged out of + * the switch table). Debounce by requiring ETH_ZERO_COUNT_THRESHOLD + * consecutive zero-count polls before treating this as a real + * all-clients-disconnected event, to avoid false disconnect churn. */ - if( FALSE == isDeleteAllDone ) + uiZeroCountPolls++; + + if( uiZeroCountPolls < ETH_ZERO_COUNT_THRESHOLD ) + { + CcspTraceInfo((" - count is 0 (%d/%d) - deferring DeleteAllHosts\n", + uiZeroCountPolls, ETH_ZERO_COUNT_THRESHOLD ) ); + } + else if( FALSE == isDeleteAllDone ) { + /* + * We should not do more than one time when all host disconnected case again + * and again + */ CcspTraceInfo((" - DeleteAllHosts due to count is 0\n") ); CcspHalExtSw_DeleteAllHosts( eth_device_hashArrayList, TRUE ); isDeleteAllDone = TRUE; @@ -450,6 +474,8 @@ void* CcspHalExtSw_AssociatedDeviceMonitorThread( void *arg ) // Reset isDeleteAllDone variable to proceed further from next iteration isDeleteAllDone = FALSE; + // Clients present again; reset the zero-count debounce counter + uiZeroCountPolls = 0; for( iLoopCount = 0; iLoopCount < (int)ulTotalEthDeviceCount; iLoopCount++ ) { @@ -489,8 +515,21 @@ void* CcspHalExtSw_AssociatedDeviceMonitorThread( void *arg ) mode = 0; } - /* Validate client in non-extender mode only*/ - if (mode != 1) + /* + * Validate client in non-extender mode only. + * + * Trust the HAL presence flag first: a device the HAL returns + * with eth_Active == TRUE is physically present on the switch + * (learnt in the switch FDB on a LAN port). Do NOT disconnect + * such a client based on the ip-neigh/lease heuristic, because + * an idle-but-connected client's neighbour entry is frequently + * STALE/FAILED/absent even though it is still connected. Only + * when the HAL itself reports the device inactive do we fall + * back to ValidateClient(). A genuinely departed client is + * still handled: it drops out of the HAL list (Host(-) loop) + * or the count goes to 0 (debounced DeleteAllHosts). + */ + if ( (mode != 1) && (0 == pstRecvEthDevice[ iLoopCount ].eth_Active) ) { // If valid then it will return 1 // If invalid then it will return 0 From 15e33a0eefb544bfe6ca344323b3005f613e497e Mon Sep 17 00:00:00 2001 From: Rajkamal CV Date: Wed, 22 Jul 2026 16:08:13 +0530 Subject: [PATCH 2/4] RDKB-66077: Debounce per-host HAL list misses to avoid false disconnects 1. Add a per-host consecutive-miss counter (ETH_HOST_MISS_THRESHOLD). 2. In the Host(-) loop, defer DeleteHost until a client is missing from the HAL associated-device list for N consecutive polls (covers count 2->1 switch-FDB aging, not caught by the count==0 debounce). 3. Store the counter inline in the hash node (eth_node_t wrapper); no ABI change to eth_device_t and no separate bookkeeping table. --- source/TR-181/board_sbapi/eth_hal_interface.c | 92 +++++++++++-------- 1 file changed, 56 insertions(+), 36 deletions(-) diff --git a/source/TR-181/board_sbapi/eth_hal_interface.c b/source/TR-181/board_sbapi/eth_hal_interface.c index a96eff1..469e8c7 100644 --- a/source/TR-181/board_sbapi/eth_hal_interface.c +++ b/source/TR-181/board_sbapi/eth_hal_interface.c @@ -33,11 +33,13 @@ #endif /*_SR213_PRODUCT_REQ_*/ #define ETH_POLLING_PERIOD 180 #define ETH_NODE_HASH_SIZE 256 -/* Number of consecutive zero-associated-device polls required before treating - * it as a genuine all-clients-disconnected event. Debounces transient - * switch-FDB/HAL misses for still-connected idle clients. */ +/* Consecutive zero-count polls before an all-clients-disconnected is treated as real. */ #define ETH_ZERO_COUNT_THRESHOLD 2 +/* Consecutive polls a single host may be missing from the HAL list before disconnect + * (debounces switch-FDB aging that drops one idle client, e.g. count 2->1). */ +#define ETH_HOST_MISS_THRESHOLD 2 + CcspHalExtSw_ethAssociatedDevice_callback AssociatedDevice_callback = NULL; /*********** Function Prototype Start**************/ @@ -56,6 +58,14 @@ unsigned int mac_hash( char *str); eth_device_t* eth_device_hashArrayList[ ETH_NODE_HASH_SIZE ]; eth_device_t* eth_device_hashArrayTempList[ ETH_NODE_HASH_SIZE ]; +/* Hash-list node: eth_device_t MUST stay first so an eth_device_t* aliases the + * node and free() releases it. 'misses' = per-host consecutive-miss debounce + * counter for the Host(-) loop; created and freed with the node. */ +typedef struct _eth_node { + eth_device_t dev; /* MUST be first member */ + unsigned int misses; +} eth_node_t; + int ValidateClient(char *mac) { int ret = 0; @@ -67,11 +77,8 @@ int ValidateClient(char *mac) FILE *fp1 = NULL; errno_t rc = -1; //Need to ignore brlan1 - XHS clients when during CB case - /* A still-connected but idle client's neighbour entry decays from - * REACHABLE to STALE/DELAY/PROBE within ~30s, while this poll runs only - * every ETH_POLLING_PERIOD (180s). Treat any known neighbour state as - * valid and reject only FAILED/INCOMPLETE (truly gone) so that idle - * clients are not falsely disconnected. */ + /* Accept any known neighbour state; reject only FAILED/INCOMPLETE, since an + * idle client decays REACHABLE->STALE within ~30s but the poll is 180s. */ v_secure_system("ip nei show | grep -v brlan1 | grep -i %s | grep -iEv 'FAILED|INCOMPLETE' > " ARP_CACHE, mac); if ( (fp1 = fopen(ARP_CACHE, "r")) == NULL ) { @@ -236,7 +243,7 @@ int CcspHalExtSw_AddHost( eth_device_t *pstEthHost, eth_device_t* eth_device_Arr return -1; } - pstEthLocalHost = malloc(sizeof(eth_device_t)); + pstEthLocalHost = malloc(sizeof(eth_node_t)); if (pstEthLocalHost == NULL) { CcspTraceInfo(("%s %d - pstEthLocalHost Null\n" ,__FUNCTION__,__LINE__ )); @@ -252,6 +259,9 @@ int CcspHalExtSw_AddHost( eth_device_t *pstEthHost, eth_device_t* eth_device_Arr return -1; } + //Zero the Host(-) debounce counter + ((eth_node_t *)pstEthLocalHost)->misses = 0; + //MAC Conversion snprintf ( @@ -440,13 +450,8 @@ void* CcspHalExtSw_AssociatedDeviceMonitorThread( void *arg ) //if 0 then delete all nodes and send disconnected notification to Ethernet if( 0 == ulTotalEthDeviceCount ) { - /* - * The switch FDB/HAL can momentarily report zero associated - * devices for a still-connected idle client (its MAC aged out of - * the switch table). Debounce by requiring ETH_ZERO_COUNT_THRESHOLD - * consecutive zero-count polls before treating this as a real - * all-clients-disconnected event, to avoid false disconnect churn. - */ + /* HAL can momentarily report 0 devices for a still-connected idle + * client (MAC aged out of the FDB). Debounce before DeleteAllHosts. */ uiZeroCountPolls++; if( uiZeroCountPolls < ETH_ZERO_COUNT_THRESHOLD ) @@ -515,20 +520,11 @@ void* CcspHalExtSw_AssociatedDeviceMonitorThread( void *arg ) mode = 0; } - /* - * Validate client in non-extender mode only. - * - * Trust the HAL presence flag first: a device the HAL returns - * with eth_Active == TRUE is physically present on the switch - * (learnt in the switch FDB on a LAN port). Do NOT disconnect - * such a client based on the ip-neigh/lease heuristic, because - * an idle-but-connected client's neighbour entry is frequently - * STALE/FAILED/absent even though it is still connected. Only - * when the HAL itself reports the device inactive do we fall - * back to ValidateClient(). A genuinely departed client is - * still handled: it drops out of the HAL list (Host(-) loop) - * or the count goes to 0 (debounced DeleteAllHosts). - */ + /* Non-extender only. Trust the HAL: an eth_Active==TRUE device is on + * the switch FDB, so don't disconnect it on the flaky ip-neigh/lease + * heuristic (idle clients often show STALE/absent). Fall back to + * ValidateClient() only when the HAL says inactive; real departures + * still hit the Host(-) loop / count==0 path. */ if ( (mode != 1) && (0 == pstRecvEthDevice[ iLoopCount ].eth_Active) ) { // If valid then it will return 1 @@ -566,15 +562,39 @@ void* CcspHalExtSw_AssociatedDeviceMonitorThread( void *arg ) //Disconnection Case for( iLoopCount = 0; iLoopCount< ETH_NODE_HASH_SIZE; iLoopCount++ ) { + eth_node_t *pstNode = (eth_node_t *)eth_device_hashArrayList[ iLoopCount ]; + + if ( NULL == pstNode ) + { + continue; + } + // If found then it will give host address // If not found then it will give NULL value - if ( ( NULL != eth_device_hashArrayList[ iLoopCount ] ) &&\ - ( NULL == CcspHalExtSw_FindHost( eth_device_hashArrayList[ iLoopCount ], eth_device_hashArrayTempList, NULL ) ) - ) + if ( NULL != CcspHalExtSw_FindHost( eth_device_hashArrayList[ iLoopCount ], eth_device_hashArrayTempList, NULL ) ) + { + //Host still present in this poll - reset its miss counter + pstNode->misses = 0; + } + else { - //Delete and Need to send notification - CcspTraceDebug(("%s:%d Delete and need to send notification\n", __FUNCTION__, __LINE__)); - CcspHalExtSw_DeleteHost( eth_device_hashArrayList[ iLoopCount ], eth_device_hashArrayList, TRUE ); + /* Host missing this poll. A single idle client's MAC can age out + * of the FDB (count 2->1), not caught by the count==0 debounce; + * require ETH_HOST_MISS_THRESHOLD consecutive misses before delete. */ + if ( ++pstNode->misses < ETH_HOST_MISS_THRESHOLD ) + { + CcspTraceInfo((" - host %02X:%02X:%02X:%02X:%02X:%02X missing (%u/%d) - deferring DeleteHost\n", + pstNode->dev.eth_devMacAddress[0], pstNode->dev.eth_devMacAddress[1], + pstNode->dev.eth_devMacAddress[2], pstNode->dev.eth_devMacAddress[3], + pstNode->dev.eth_devMacAddress[4], pstNode->dev.eth_devMacAddress[5], + pstNode->misses, ETH_HOST_MISS_THRESHOLD )); + } + else + { + //Delete and Need to send notification + CcspTraceDebug(("%s:%d Delete and need to send notification\n", __FUNCTION__, __LINE__)); + CcspHalExtSw_DeleteHost( eth_device_hashArrayList[ iLoopCount ], eth_device_hashArrayList, TRUE ); + } } } From cfb382651e25e11b1ce0efa1b7cad682aadcad87 Mon Sep 17 00:00:00 2001 From: Rajkamal CV Date: Thu, 23 Jul 2026 09:38:41 +0530 Subject: [PATCH 3/4] RDKB-66077: Confirm client is really gone before disconnect 1. Gate the Host(-) disconnect on ValidateClient(): a host missing from the HAL associated-device list is retained if ip-neigh (not FAILED/INCOMPLETE) or a dnsmasq lease still shows it present. This covers sustained switch-FDB aging of an idle-but-connected client, which the miss-count debounce alone could not (HAL dropped the client for >threshold consecutive polls while it was still REACHABLE/leased). 2. Disconnect only when the client is absent from the HAL list AND unvalidated, for ETH_HOST_MISS_THRESHOLD consecutive polls (debounce kept as secondary guard). 3. Remove the count==0 special case (ETH_ZERO_COUNT_THRESHOLD/DeleteAllHosts): an empty HAL list now flows through the same per-client validate+debounce path, so a transient count==0 no longer bulk-deletes present clients. --- source/TR-181/board_sbapi/eth_hal_interface.c | 80 ++++++++----------- 1 file changed, 32 insertions(+), 48 deletions(-) diff --git a/source/TR-181/board_sbapi/eth_hal_interface.c b/source/TR-181/board_sbapi/eth_hal_interface.c index 469e8c7..2964948 100644 --- a/source/TR-181/board_sbapi/eth_hal_interface.c +++ b/source/TR-181/board_sbapi/eth_hal_interface.c @@ -33,8 +33,6 @@ #endif /*_SR213_PRODUCT_REQ_*/ #define ETH_POLLING_PERIOD 180 #define ETH_NODE_HASH_SIZE 256 -/* Consecutive zero-count polls before an all-clients-disconnected is treated as real. */ -#define ETH_ZERO_COUNT_THRESHOLD 2 /* Consecutive polls a single host may be missing from the HAL list before disconnect * (debounces switch-FDB aging that drops one idle client, e.g. count 2->1). */ @@ -408,8 +406,6 @@ void* CcspHalExtSw_AssociatedDeviceMonitorThread( void *arg ) ULONG ulTotalEthDeviceCount = 0; INT iLoopCount; BOOL bProcessFurther = TRUE; - static BOOL isDeleteAllDone = FALSE; - static INT uiZeroCountPolls = 0; CcspTraceDebug((" Iteration Start\n") ); @@ -420,8 +416,8 @@ void* CcspHalExtSw_AssociatedDeviceMonitorThread( void *arg ) bProcessFurther = FALSE; } - CcspTraceDebug(("%s:%d bProcessFurther:%d, ulTotalEthDeviceCount:%lu, isDeleteAllDone:%d\n", - __FUNCTION__, __LINE__, bProcessFurther, ulTotalEthDeviceCount, isDeleteAllDone)); + CcspTraceDebug(("%s:%d bProcessFurther:%d, ulTotalEthDeviceCount:%lu\n", + __FUNCTION__, __LINE__, bProcessFurther, ulTotalEthDeviceCount)); if( bProcessFurther ) { @@ -429,7 +425,7 @@ void* CcspHalExtSw_AssociatedDeviceMonitorThread( void *arg ) * Handle Notification based on Add or Delete cases * ----------------------------------------- * 1. Check whether ulTotalEthDeviceCount is greater than 0 or not. - * 1.1 If 0 then we need to delete all hosts and send notification + * 1.1 If 0 the Host(+) loop runs zero times; the Host(-) loop reconciles all hosts * * 2. Check whether received client mac is valid or not based on "ip nei show" & "dnsmasq.leases" file * @@ -447,41 +443,13 @@ void* CcspHalExtSw_AssociatedDeviceMonitorThread( void *arg ) * 6. Remove all hosts from temp list */ - //if 0 then delete all nodes and send disconnected notification to Ethernet - if( 0 == ulTotalEthDeviceCount ) - { - /* HAL can momentarily report 0 devices for a still-connected idle - * client (MAC aged out of the FDB). Debounce before DeleteAllHosts. */ - uiZeroCountPolls++; - - if( uiZeroCountPolls < ETH_ZERO_COUNT_THRESHOLD ) - { - CcspTraceInfo((" - count is 0 (%d/%d) - deferring DeleteAllHosts\n", - uiZeroCountPolls, ETH_ZERO_COUNT_THRESHOLD ) ); - } - else if( FALSE == isDeleteAllDone ) - { - /* - * We should not do more than one time when all host disconnected case again - * and again - */ - CcspTraceInfo((" - DeleteAllHosts due to count is 0\n") ); - CcspHalExtSw_DeleteAllHosts( eth_device_hashArrayList, TRUE ); - isDeleteAllDone = TRUE; - } - - bProcessFurther = FALSE; - } - - if( bProcessFurther ) + /* No count==0 special case: when the HAL list is empty the Host(+) loop + * simply runs zero times and the Host(-) loop below reconciles every + * known host through the same per-client ValidateClient + miss debounce, + * so a transient count==0 no longer bulk-deletes still-present clients. */ { CcspTraceDebug((" - Host(+) Loop Start\n") ); - // Reset isDeleteAllDone variable to proceed further from next iteration - isDeleteAllDone = FALSE; - // Clients present again; reset the zero-count debounce counter - uiZeroCountPolls = 0; - for( iLoopCount = 0; iLoopCount < (int)ulTotalEthDeviceCount; iLoopCount++ ) { char tmp_mac_id[ 18 ]; @@ -578,16 +546,32 @@ void* CcspHalExtSw_AssociatedDeviceMonitorThread( void *arg ) } else { - /* Host missing this poll. A single idle client's MAC can age out - * of the FDB (count 2->1), not caught by the count==0 debounce; - * require ETH_HOST_MISS_THRESHOLD consecutive misses before delete. */ - if ( ++pstNode->misses < ETH_HOST_MISS_THRESHOLD ) + /* Host missing from this poll's HAL list. The FDB-offload list is + * unreliable for idle clients (can stay dropped for several polls), + * so confirm with an independent signal before disconnecting: + * ValidateClient() (ip neigh not FAILED/INCOMPLETE, or dnsmasq lease). + * Only when that also says gone do we debounce, then DeleteHost. */ + char miss_mac_id[ 18 ] = {0}; + + snprintf + ( + miss_mac_id, + sizeof( miss_mac_id ), + "%02X:%02X:%02X:%02X:%02X:%02X", + pstNode->dev.eth_devMacAddress[0], pstNode->dev.eth_devMacAddress[1], + pstNode->dev.eth_devMacAddress[2], pstNode->dev.eth_devMacAddress[3], + pstNode->dev.eth_devMacAddress[4], pstNode->dev.eth_devMacAddress[5] + ); + + if ( ValidateClient( miss_mac_id ) ) + { + //Independent signal says still present - retain, reset debounce + pstNode->misses = 0; + } + else if ( ++pstNode->misses < ETH_HOST_MISS_THRESHOLD ) { - CcspTraceInfo((" - host %02X:%02X:%02X:%02X:%02X:%02X missing (%u/%d) - deferring DeleteHost\n", - pstNode->dev.eth_devMacAddress[0], pstNode->dev.eth_devMacAddress[1], - pstNode->dev.eth_devMacAddress[2], pstNode->dev.eth_devMacAddress[3], - pstNode->dev.eth_devMacAddress[4], pstNode->dev.eth_devMacAddress[5], - pstNode->misses, ETH_HOST_MISS_THRESHOLD )); + CcspTraceInfo((" - host %s missing+unvalidated (%u/%d) - deferring DeleteHost\n", + miss_mac_id, pstNode->misses, ETH_HOST_MISS_THRESHOLD )); } else { From 1390afa7f014bca41da1e020406278d99ccc4d84 Mon Sep 17 00:00:00 2001 From: Rajkamal CV Date: Fri, 24 Jul 2026 07:21:02 +0530 Subject: [PATCH 4/4] RDKB-66077: Trust eth_Active and debounce per-client HAL list misses 1. Host(+) loop: only fall back to ValidateClient() when the HAL reports the device eth_Active == FALSE. A HAL-active client (present in the switch FDB) is no longer disconnected by the flaky ip-neigh/lease heuristic. 2. Host(-) loop: add a per-host consecutive-miss debounce. A client that drops out of the HAL associated-device list is disconnected only after ETH_HOST_MISS_THRESHOLD consecutive missed polls, covering transient switch-FDB aging that drops a single idle client (e.g. count 2->1). 3. Store the miss counter inline in the hash node (eth_node_t wrapper, with eth_device_t as the first member); no change to the HAL eth_device_t struct and no separate bookkeeping table. --- source/TR-181/board_sbapi/eth_hal_interface.c | 66 +++++++++---------- 1 file changed, 33 insertions(+), 33 deletions(-) diff --git a/source/TR-181/board_sbapi/eth_hal_interface.c b/source/TR-181/board_sbapi/eth_hal_interface.c index 2964948..56ea407 100644 --- a/source/TR-181/board_sbapi/eth_hal_interface.c +++ b/source/TR-181/board_sbapi/eth_hal_interface.c @@ -75,9 +75,7 @@ int ValidateClient(char *mac) FILE *fp1 = NULL; errno_t rc = -1; //Need to ignore brlan1 - XHS clients when during CB case - /* Accept any known neighbour state; reject only FAILED/INCOMPLETE, since an - * idle client decays REACHABLE->STALE within ~30s but the poll is 180s. */ - v_secure_system("ip nei show | grep -v brlan1 | grep -i %s | grep -iEv 'FAILED|INCOMPLETE' > " ARP_CACHE, mac); + v_secure_system("ip nei show | grep -v brlan1 | grep -i %s | grep -i REACHABLE > " ARP_CACHE, mac); if ( (fp1 = fopen(ARP_CACHE, "r")) == NULL ) { return ret; @@ -406,6 +404,7 @@ void* CcspHalExtSw_AssociatedDeviceMonitorThread( void *arg ) ULONG ulTotalEthDeviceCount = 0; INT iLoopCount; BOOL bProcessFurther = TRUE; + static BOOL isDeleteAllDone = FALSE; CcspTraceDebug((" Iteration Start\n") ); @@ -425,7 +424,7 @@ void* CcspHalExtSw_AssociatedDeviceMonitorThread( void *arg ) * Handle Notification based on Add or Delete cases * ----------------------------------------- * 1. Check whether ulTotalEthDeviceCount is greater than 0 or not. - * 1.1 If 0 the Host(+) loop runs zero times; the Host(-) loop reconciles all hosts + * 1.1 If 0 then we need to delete all hosts and send notification * * 2. Check whether received client mac is valid or not based on "ip nei show" & "dnsmasq.leases" file * @@ -443,13 +442,30 @@ void* CcspHalExtSw_AssociatedDeviceMonitorThread( void *arg ) * 6. Remove all hosts from temp list */ - /* No count==0 special case: when the HAL list is empty the Host(+) loop - * simply runs zero times and the Host(-) loop below reconciles every - * known host through the same per-client ValidateClient + miss debounce, - * so a transient count==0 no longer bulk-deletes still-present clients. */ + //if 0 then delete all nodes and send disconnected notification to Ethernet + if( 0 == ulTotalEthDeviceCount ) + { + /* + * We should not do more than one time when all host disconnected case again + * and again + */ + if( FALSE == isDeleteAllDone ) + { + CcspTraceInfo((" - DeleteAllHosts due to count is 0\n") ); + CcspHalExtSw_DeleteAllHosts( eth_device_hashArrayList, TRUE ); + isDeleteAllDone = TRUE; + } + + bProcessFurther = FALSE; + } + + if( bProcessFurther ) { CcspTraceDebug((" - Host(+) Loop Start\n") ); + // Reset the all-deleted guard now that clients are present again + isDeleteAllDone = FALSE; + for( iLoopCount = 0; iLoopCount < (int)ulTotalEthDeviceCount; iLoopCount++ ) { char tmp_mac_id[ 18 ]; @@ -546,32 +562,16 @@ void* CcspHalExtSw_AssociatedDeviceMonitorThread( void *arg ) } else { - /* Host missing from this poll's HAL list. The FDB-offload list is - * unreliable for idle clients (can stay dropped for several polls), - * so confirm with an independent signal before disconnecting: - * ValidateClient() (ip neigh not FAILED/INCOMPLETE, or dnsmasq lease). - * Only when that also says gone do we debounce, then DeleteHost. */ - char miss_mac_id[ 18 ] = {0}; - - snprintf - ( - miss_mac_id, - sizeof( miss_mac_id ), - "%02X:%02X:%02X:%02X:%02X:%02X", - pstNode->dev.eth_devMacAddress[0], pstNode->dev.eth_devMacAddress[1], - pstNode->dev.eth_devMacAddress[2], pstNode->dev.eth_devMacAddress[3], - pstNode->dev.eth_devMacAddress[4], pstNode->dev.eth_devMacAddress[5] - ); - - if ( ValidateClient( miss_mac_id ) ) - { - //Independent signal says still present - retain, reset debounce - pstNode->misses = 0; - } - else if ( ++pstNode->misses < ETH_HOST_MISS_THRESHOLD ) + /* Host missing this poll. A single idle client's MAC can age out + * of the FDB (count 2->1); require ETH_HOST_MISS_THRESHOLD + * consecutive misses before disconnecting to debounce it. */ + if ( ++pstNode->misses < ETH_HOST_MISS_THRESHOLD ) { - CcspTraceInfo((" - host %s missing+unvalidated (%u/%d) - deferring DeleteHost\n", - miss_mac_id, pstNode->misses, ETH_HOST_MISS_THRESHOLD )); + CcspTraceInfo((" - host %02X:%02X:%02X:%02X:%02X:%02X missing (%u/%d) - deferring DeleteHost\n", + pstNode->dev.eth_devMacAddress[0], pstNode->dev.eth_devMacAddress[1], + pstNode->dev.eth_devMacAddress[2], pstNode->dev.eth_devMacAddress[3], + pstNode->dev.eth_devMacAddress[4], pstNode->dev.eth_devMacAddress[5], + pstNode->misses, ETH_HOST_MISS_THRESHOLD )); } else {