From 7a4474248ca19f504228e6caf9fbefc2fb9afe9b Mon Sep 17 00:00:00 2001 From: S-Parthiban-Selvaraj Date: Tue, 13 Oct 2026 16:43:34 +0100 Subject: [PATCH 1/2] SHARMAN-4397 : Implement IPv6 DAD recovery mechanism and enhance address event logging --- source/TR-181/include/wanmgr_dml.h | 1 + source/WanManager/wanmgr_dhcp_event_handler.c | 1 + source/WanManager/wanmgr_interface_sm.c | 83 ++++++++++++++++--- source/WanManager/wanmgr_network_monitor.c | 24 +++++- 4 files changed, 95 insertions(+), 14 deletions(-) diff --git a/source/TR-181/include/wanmgr_dml.h b/source/TR-181/include/wanmgr_dml.h index 720364cb..472cd0b4 100644 --- a/source/TR-181/include/wanmgr_dml.h +++ b/source/TR-181/include/wanmgr_dml.h @@ -437,6 +437,7 @@ typedef struct _DML_WANIFACE_IP BOOL Ipv6Changed; BOOL Ipv4Renewed; BOOL Ipv6Renewed; + BOOL Ipv6DadRecoveryAttempted; /**< TRUE once the WAN IPv6 address has been deleted/re-added during the current DAD failure cycle */ WANMGR_IPV4_DATA Ipv4Data; WANMGR_IPV6_DATA Ipv6Data; WANMGR_IPV6_RA_DATA Ipv6RA; diff --git a/source/WanManager/wanmgr_dhcp_event_handler.c b/source/WanManager/wanmgr_dhcp_event_handler.c index 2dfdc2c6..c481ffde 100644 --- a/source/WanManager/wanmgr_dhcp_event_handler.c +++ b/source/WanManager/wanmgr_dhcp_event_handler.c @@ -248,6 +248,7 @@ void WanMgr_ProcessDhcpClientEvent(DhcpEventThreadArgs *eventData) DHCP_MGR_IPV6_MSG* leaseInfo = &(eventData->lease.v6); copyDhcpv6Data(&(pVirtIf->IP.Ipv6Data), leaseInfo); pVirtIf->IP.Ipv6Changed = TRUE; + pVirtIf->IP.Ipv6DadRecoveryAttempted = FALSE; // new lease/address, allow a fresh DAD recovery cycle CcspTraceInfo(("%s %d - IPv6 - RestartConnectivityCheck triggered. \n", __FUNCTION__, __LINE__)); pVirtIf->IP.RestartConnectivityCheck = TRUE; //TODO : WAN ip creation from IA_PD if required. address assignment on LAN bridge. diff --git a/source/WanManager/wanmgr_interface_sm.c b/source/WanManager/wanmgr_interface_sm.c index 4433ae27..3b82c4f5 100644 --- a/source/WanManager/wanmgr_interface_sm.c +++ b/source/WanManager/wanmgr_interface_sm.c @@ -211,7 +211,7 @@ static ANSC_STATUS WanManager_ClearDHCPData(DML_VIRTUAL_IFACE * pVirtIf); * lan ipv6 address ready to use. * @return RETURN_OK on success else RETURN_ERR *************************************************************************************/ -static int checkIpv6AddressIsReadyToUse(DML_VIRTUAL_IFACE* p_VirtIf); +static int checkIpv6AddressIsReadyToUse(DML_VIRTUAL_IFACE* p_VirtIf, IFACE_TYPE IfaceType); #ifdef FEATURE_DSLITE_V2 /************************************************************************************* @@ -1111,6 +1111,30 @@ static int WanMgr_IsIpv6DADComplete(const char *ifName) return addressFound && !dadIncomplete; } +/* + * Force the kernel to restart Duplicate Address Detection (DAD) for the WAN IPv6 + * address by removing it and adding it back on the same interface. + */ +static void WanMgr_Ipv6DadRecovery_ReaddAddress(DML_VIRTUAL_IFACE* p_VirtIf) +{ + char cmdLine[BUFLEN_256] = {0}; + + if (p_VirtIf->IP.Ipv6Data.address[0] == '\0') + { + return; + } + + snprintf(cmdLine, sizeof(cmdLine), "ip -6 addr del %s/128 dev %s", p_VirtIf->IP.Ipv6Data.address, p_VirtIf->Name); + WanManager_DoSystemActionWithStatus("checkIpv6AddressIsReadyToUse: DAD recovery - delete address", cmdLine); + + snprintf(cmdLine, sizeof(cmdLine), "ip -6 addr add %s/128 dev %s", p_VirtIf->IP.Ipv6Data.address, p_VirtIf->Name); + if (WanManager_DoSystemActionWithStatus("checkIpv6AddressIsReadyToUse: DAD recovery - re-add address", cmdLine) != 0) + { + CcspTraceError(("%s %d - Failed to re-add IPv6 address %s on '%s' during DAD recovery\n", + __FUNCTION__, __LINE__, p_VirtIf->IP.Ipv6Data.address, p_VirtIf->Name)); + } +} + /** * @brief Checks if the IPv6 address is ready to use. * @@ -1118,13 +1142,17 @@ static int WanMgr_IsIpv6DADComplete(const char *ifName) * failure, and verifies that a default route is present. * If the default route is missing, it triggers a Router Solicitation. * + * If DAD does not complete within 15 seconds, the address is deleted and re-added to force + * a fresh DAD cycle. If DAD still hasn't completed 15 seconds after that, the DHCPv6 client + * is restarted on the interface to recover. + * * @param p_VirtIf Pointer to the virtual interface structure. + * @param IfaceType Type of the base WAN interface, needed to restart the DHCPv6 client. * @return int 0 on success, negative value on failure. */ -static int checkIpv6AddressIsReadyToUse(DML_VIRTUAL_IFACE* p_VirtIf) +static int checkIpv6AddressIsReadyToUse(DML_VIRTUAL_IFACE* p_VirtIf, IFACE_TYPE IfaceType) { char buffer[BUFLEN_256] = {0}; - FILE *fp_dad = NULL; FILE *fp_route = NULL; int dad_flag = 0; int route_flag = 0; @@ -1147,6 +1175,38 @@ static int checkIpv6AddressIsReadyToUse(DML_VIRTUAL_IFACE* p_VirtIf) sleep(1); } + if (dad_flag == 0 && p_VirtIf->IP.Ipv6DadRecoveryAttempted == FALSE) + { + CcspTraceError(("%s %d - DAD check failed for %s on '%s' after 15 sec, deleting and re-adding the address to recover\n", + __FUNCTION__, __LINE__, p_VirtIf->IP.Ipv6Data.address, p_VirtIf->Name)); + + WanMgr_Ipv6DadRecovery_ReaddAddress(p_VirtIf); + p_VirtIf->IP.Ipv6DadRecoveryAttempted = TRUE; + + for(int i=0; i<15; i++) + { + if (WanMgr_IsIpv6DADComplete(p_VirtIf->Name)) + { + dad_flag = 1; + break; + } + + sleep(1); + } + + if (dad_flag == 0) + { + CcspTraceError(("%s %d - DAD still failing for %s on '%s' after address recovery, restarting DHCPv6 client with release\n", + __FUNCTION__, __LINE__, p_VirtIf->IP.Ipv6Data.address, p_VirtIf->Name)); + WanManager_RestartDhcpv6Client(p_VirtIf, IfaceType); + } + } + + if (dad_flag == 1) + { + p_VirtIf->IP.Ipv6DadRecoveryAttempted = FALSE; + } + buffer[0] = '\0'; char cmd[128]; snprintf(cmd, sizeof(cmd), "ip -6 ro show default dev %s", p_VirtIf->Name); @@ -1160,7 +1220,6 @@ static int checkIpv6AddressIsReadyToUse(DML_VIRTUAL_IFACE* p_VirtIf) } } - //if DAD failed on WAN interface, log an ERROR message and continue with the WAN process. if(dad_flag == 0 || route_flag == 0) { CcspTraceError(("%s %d dad_flag[%s] route_flag[%s] Failed \n", __FUNCTION__, __LINE__, dad_flag ? "SUCCESS" : "FAILED", route_flag ? "SUCCESS" : "FAILED")); @@ -1754,7 +1813,7 @@ static int wan_setUpIPv6(WanMgr_IfaceSM_Controller_t * pWanIfaceCtrl) * On a lease change the address is flushed and re-added, which restarts DAD; * restarting sshd/dropbear while the address is still tentative makes the bind * fail with "Cannot assign requested address", leaving reverse SSH IPv4-only. */ - if (checkIpv6AddressIsReadyToUse(p_VirtIf) != RETURN_OK) + if (checkIpv6AddressIsReadyToUse(p_VirtIf, pInterface->IfaceType) != RETURN_OK) { CcspTraceError(("%s %d - IPv6 address not ready (DAD/route) on %s before restarting services\n", __FUNCTION__, __LINE__, p_VirtIf->Name)); @@ -4185,7 +4244,7 @@ static eWanState_t wan_state_obtaining_ip_addresses(WanMgr_IfaceSM_Controller_t* p_VirtIf->IP.Ipv6Changed = FALSE; return WAN_STATE_OBTAINING_IP_ADDRESSES; } - if (checkIpv6AddressIsReadyToUse(p_VirtIf) == RETURN_OK) + if (checkIpv6AddressIsReadyToUse(p_VirtIf, pInterface->IfaceType) == RETURN_OK) { return wan_transition_ipv6_up(pWanIfaceCtrl); } @@ -4256,7 +4315,7 @@ static eWanState_t wan_state_standby(WanMgr_IfaceSM_Controller_t* pWanIfaceCtrl) } p_VirtIf->IP.Ipv6Changed = FALSE; } - if (checkIpv6AddressIsReadyToUse(p_VirtIf) == RETURN_OK) + if (checkIpv6AddressIsReadyToUse(p_VirtIf, pInterface->IfaceType) == RETURN_OK) { ret = wan_transition_ipv6_up(pWanIfaceCtrl); CcspTraceInfo((" %s %d - IPv6 Address Assigned to Bridge Yet.\n", __FUNCTION__, __LINE__)); @@ -4365,7 +4424,7 @@ static eWanState_t wan_state_ipv4_leased(WanMgr_IfaceSM_Controller_t* pWanIfaceC p_VirtIf->IP.Ipv6Changed = FALSE; return WAN_STATE_IPV4_LEASED; } - if (checkIpv6AddressIsReadyToUse(p_VirtIf) == RETURN_OK) + if (checkIpv6AddressIsReadyToUse(p_VirtIf, pInterface->IfaceType) == RETURN_OK) { return wan_transition_ipv6_up(pWanIfaceCtrl); } @@ -4473,7 +4532,7 @@ static eWanState_t wan_state_ipv6_leased(WanMgr_IfaceSM_Controller_t* pWanIfaceC } #if defined(FEATURE_MAPT) || defined(FEATURE_SUPPORT_MAPT_NAT46) || defined(FEATURE_MAPE) else if (pInterface->Selection.Status == WAN_IFACE_ACTIVE && - ((p_VirtIf->EnableMAPT == TRUE && p_VirtIf->MAP.MaptStatus == WAN_IFACE_MAPT_STATE_UP && checkIpv6AddressIsReadyToUse(p_VirtIf) ==RETURN_OK) + ((p_VirtIf->EnableMAPT == TRUE && p_VirtIf->MAP.MaptStatus == WAN_IFACE_MAPT_STATE_UP && checkIpv6AddressIsReadyToUse(p_VirtIf, pInterface->IfaceType) ==RETURN_OK) || (p_VirtIf->MAP.dhcp6cMAPparameters.mapType == MAP_TYPE_MAPE && p_VirtIf->Status == WAN_IFACE_STATUS_UP && p_VirtIf->MAP.MapeStatus == WAN_IFACE_MAPE_STATE_UP))) { CcspTraceInfo(("%s %d - calling wan_transition_map_up \n", __FUNCTION__, __LINE__)); @@ -4500,7 +4559,7 @@ static eWanState_t wan_state_ipv6_leased(WanMgr_IfaceSM_Controller_t* pWanIfaceC { if (p_VirtIf->DSLite.Status == WAN_IFACE_DSLITE_STATE_DOWN) { - if (checkIpv6AddressIsReadyToUse(p_VirtIf) == RETURN_OK) + if (checkIpv6AddressIsReadyToUse(p_VirtIf, pInterface->IfaceType) == RETURN_OK) { return wan_transition_dslite_up(pWanIfaceCtrl); } @@ -4628,7 +4687,7 @@ static eWanState_t wan_state_dual_stack_active(WanMgr_IfaceSM_Controller_t* pWan } #if defined(FEATURE_MAPT) || defined(FEATURE_SUPPORT_MAPT_NAT46) || defined(FEATURE_MAPE) else if (pInterface->Selection.Status == WAN_IFACE_ACTIVE && - ((p_VirtIf->EnableMAPT == TRUE && p_VirtIf->MAP.MaptStatus == WAN_IFACE_MAPT_STATE_UP && checkIpv6AddressIsReadyToUse(p_VirtIf) ==RETURN_OK) + ((p_VirtIf->EnableMAPT == TRUE && p_VirtIf->MAP.MaptStatus == WAN_IFACE_MAPT_STATE_UP && checkIpv6AddressIsReadyToUse(p_VirtIf, pInterface->IfaceType) ==RETURN_OK) || (p_VirtIf->MAP.dhcp6cMAPparameters.mapType == MAP_TYPE_MAPE && p_VirtIf->Status == WAN_IFACE_STATUS_UP && p_VirtIf->MAP.MapeStatus == WAN_IFACE_MAPE_STATE_UP))) { CcspTraceInfo(("%s %d - calling wan_transition_map_up \n", __FUNCTION__, __LINE__)); @@ -4655,7 +4714,7 @@ static eWanState_t wan_state_dual_stack_active(WanMgr_IfaceSM_Controller_t* pWan { if (p_VirtIf->DSLite.Status == WAN_IFACE_DSLITE_STATE_DOWN) { - if (checkIpv6AddressIsReadyToUse(p_VirtIf) == RETURN_OK) + if (checkIpv6AddressIsReadyToUse(p_VirtIf, pInterface->IfaceType) == RETURN_OK) { return wan_transition_dslite_up(pWanIfaceCtrl); } diff --git a/source/WanManager/wanmgr_network_monitor.c b/source/WanManager/wanmgr_network_monitor.c index 5dbfb99d..c91ff45d 100644 --- a/source/WanManager/wanmgr_network_monitor.c +++ b/source/WanManager/wanmgr_network_monitor.c @@ -201,11 +201,13 @@ static ANSC_STATUS parse_addrattr(struct nlmsghdr *nlh) { ANSC_STATUS ret = ANSC_STATUS_FAILURE; struct ifaddrmsg *ifa = NLMSG_DATA(nlh); - char ifname[IF_NAMESIZE], - ipv6_addr[INET6_ADDRSTRLEN]; + char ifname[IF_NAMESIZE] = {0}, + ipv6_addr[INET6_ADDRSTRLEN] = {0}; unsigned int prefix_length = 0, pref_lifetime = 0, valid_lifetime = 0; + /* ifa_flags only carries the low 8 bits, the IFA_FLAGS attribute below carries all 32 */ + unsigned int addr_flags = ifa->ifa_flags; IPv6NetLinkAddrEvent stAddrEvent; //Allow only for Global Scope @@ -245,8 +247,17 @@ static ANSC_STATUS parse_addrattr(struct nlmsghdr *nlh) __FUNCTION__, __LINE__, ci->ifa_prefered, ci->ifa_valid)); } + if (rta->rta_type == IFA_FLAGS) { + addr_flags = *(unsigned int *)RTA_DATA(rta); + } } + CcspTraceInfo(("%s-%d [ADDR EVENT] flags=0x%08x%s%s%s%s\n", __FUNCTION__, __LINE__, addr_flags, + (addr_flags & IFA_F_TENTATIVE) ? " TENTATIVE" : "", + (addr_flags & IFA_F_DADFAILED) ? " DADFAILED" : "", + (addr_flags & IFA_F_DEPRECATED) ? " DEPRECATED" : "", + (addr_flags & IFA_F_PERMANENT) ? " PERMANENT" : "")); + if (nlh->nlmsg_type == RTM_NEWADDR) { memset(&stAddrEvent, 0, sizeof(IPv6NetLinkAddrEvent)); @@ -259,6 +270,15 @@ static ANSC_STATUS parse_addrattr(struct nlmsghdr *nlh) CcspTraceInfo(("%s-%d [ADDR EVENT] RTM_NEWADDR (new/updated address) for '%s' interface, Info 'NEWADDR|%s|%u|%u|%u'\n", __FUNCTION__, __LINE__, ifname, ipv6_addr, prefix_length, pref_lifetime, valid_lifetime)); WanMgr_Handle_Dhcpv6_NetLink_Address_Event(&stAddrEvent); + + /* The kernel reports a DAD failure as an RTM_NEWADDR carrying IFA_F_DADFAILED, not as a separate event. + * Recovery is handled by checkIpv6AddressIsReadyToUse() in the interface state machine, not here. */ + if ((addr_flags & IFA_F_DADFAILED) && (ipv6_addr[0] != '\0')) + { + CcspTraceError(("%s-%d [ADDR EVENT] DAD FAILED for address %s/%u on '%s' interface, flags=0x%08x\n", + __FUNCTION__, __LINE__, ipv6_addr, prefix_length, ifname, addr_flags)); + } + ret = ANSC_STATUS_SUCCESS; } else if (nlh->nlmsg_type == RTM_DELADDR) From e3ffed0817e8f10fdd1b2c13bf19ccc7d36f966a Mon Sep 17 00:00:00 2001 From: S-Parthiban-Selvaraj Date: Thu, 15 Oct 2026 15:15:24 +0100 Subject: [PATCH 2/2] Rename DAD recovery function for clarity and update its usage in address readiness check --- source/WanManager/wanmgr_interface_sm.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/source/WanManager/wanmgr_interface_sm.c b/source/WanManager/wanmgr_interface_sm.c index 3b82c4f5..7d2f7495 100644 --- a/source/WanManager/wanmgr_interface_sm.c +++ b/source/WanManager/wanmgr_interface_sm.c @@ -1115,7 +1115,7 @@ static int WanMgr_IsIpv6DADComplete(const char *ifName) * Force the kernel to restart Duplicate Address Detection (DAD) for the WAN IPv6 * address by removing it and adding it back on the same interface. */ -static void WanMgr_Ipv6DadRecovery_ReaddAddress(DML_VIRTUAL_IFACE* p_VirtIf) +static void WanMgr_Ipv6DadRecovery_ReConfigureAddress(DML_VIRTUAL_IFACE* p_VirtIf) { char cmdLine[BUFLEN_256] = {0}; @@ -1180,7 +1180,7 @@ static int checkIpv6AddressIsReadyToUse(DML_VIRTUAL_IFACE* p_VirtIf, IFACE_TYPE CcspTraceError(("%s %d - DAD check failed for %s on '%s' after 15 sec, deleting and re-adding the address to recover\n", __FUNCTION__, __LINE__, p_VirtIf->IP.Ipv6Data.address, p_VirtIf->Name)); - WanMgr_Ipv6DadRecovery_ReaddAddress(p_VirtIf); + WanMgr_Ipv6DadRecovery_ReConfigureAddress(p_VirtIf); p_VirtIf->IP.Ipv6DadRecoveryAttempted = TRUE; for(int i=0; i<15; i++)