[nrfconnect][zephyr] Improvements and Fixes for WiFi according to NCS 2.6.0 (#32711)

* [nrfconnect] wifi: avoid unwanted connect request

It's pointless to issue a connect request in case
no valid SSID has been found.

Signed-off-by: Marcin Kajor <marcin.kajor@nordicsemi.no>

* [nrfconnect] wifi: Fix 5GHz association

Wi-Fi stack recently introduced a check for valid band value and the
default value of 0 (memset) means only 2.4GHz, so, 5GHz Wi-Fi
associations will fail.

Fix the default to Unknown to scan all supported bands.

* [zephyr][nrfconnect] Make Wi-Fi manager use Wi-Fi interface only

Find the Wi-Fi interface at the Wi-Fi manager initialization
and use that interface instead of the default interface when
calling Wi-Fi management functions.

Signed-off-by: Damian Krolik <damian.krolik@nordicsemi.no>

* [nrfconnect] fix handling of LastNetworkID in Wi-Fi driver

This commit makes sure that correct Network ID is provided to the
Network Commissioning cluster from the platform's Wi-Fi driver.

Signed-off-by: Łukasz Duda <lukasz.duda@nordicsemi.no>

* [inet] Combine platform handlers for joining/leaving mcast group

Instead, use a single handler for both joining and leaving
a multicast group to reduce the code duplication.

Signed-off-by: Damian Krolik <damian.krolik@nordicsemi.no>

* [zephyr][nrfconnect] Move handler for joining/leaving mcast group

Move the platform handler for joining and leaving a multicast
group to ConnectivityManagerImpl to support Matter stack on
a system with multiple network interfaces (Thread + Wi-Fi).

Signed-off-by: Damian Krolik <damian.krolik@nordicsemi.no>

* [nrfconnect] Added DNS server refresh after adding new IPv6 address

The Wi-Fi device does not update mDNS queries after obtaining
new IPv6 GUA address, so for some time after assigning prefix,
the Thread Border Routers still use cached link-local address,
which is not routable.

Signed-off-by: Kamil Kasperczyk <kamil.kasperczyk@nordicsemi.no>

* [nrfconnect] [zephyr] Disable synchronous printk

Disable synchronous printk to avoid blocking IRQs which
may affect time sensitive components (like 15.4 radio).

Signed-off-by: Marcin Kajor <marcin.kajor@nordicsemi.no>

* [nrfconnect] Fix various Wi-Fi issues with error code handling

This commit handles a few issues with Wi-Fi connection or scanning:
 - Use wifi_status structure instead of incompatible WiFiRequestStatus
 - On connect error value > 2 do not report success
 - On scan error value > 1 do not report success
 - Provide value of mandatory LastConnectErrorValue attribute

Signed-off-by: Łukasz Duda <lukasz.duda@nordicsemi.no>

* [nrfconnect] Minor Wi-Fi refinements

* error code handling unification
* added GetWantedNetwork getter
  and use it when handling network status change
* minor refactoring

Signed-off-by: Marcin Kajor <marcin.kajor@nordicsemi.no>

* Restyled by clang-format

* [nrfconnect] Provide a workaround for nrfconnect Posix unit tests.

We need to disable all dependencies to the Zephyr net_if module until
we switch unit tests to it.

* Restyled by gn

* Use Enum to indicate an operation instead of bool in MulticastGroupHandler

---------

Signed-off-by: Marcin Kajor <marcin.kajor@nordicsemi.no>
Signed-off-by: Damian Krolik <damian.krolik@nordicsemi.no>
Signed-off-by: Łukasz Duda <lukasz.duda@nordicsemi.no>
Signed-off-by: Kamil Kasperczyk <kamil.kasperczyk@nordicsemi.no>
Co-authored-by: Marcin Kajor <marcin.kajor@nordicsemi.no>
Co-authored-by: Damian Krolik <damian.krolik@nordicsemi.no>
Co-authored-by: Łukasz Duda <lukasz.duda@nordicsemi.no>
Co-authored-by: Kamil Kasperczyk <kamil.kasperczyk@nordicsemi.no>
Co-authored-by: Restyled.io <commits@restyled.io>
diff --git a/config/zephyr/chip-module/Kconfig.defaults b/config/zephyr/chip-module/Kconfig.defaults
index 4cf4ede..d21562c 100644
--- a/config/zephyr/chip-module/Kconfig.defaults
+++ b/config/zephyr/chip-module/Kconfig.defaults
@@ -40,9 +40,11 @@
 
 endif
 
+# disable synchronous printk to avoid blocking IRQs which
+# may affect time sensitive components
 config PRINTK_SYNC
 	bool
-	default y
+	default n
 
 config ASSERT
 	bool
diff --git a/src/inet/UDPEndPointImplSockets.cpp b/src/inet/UDPEndPointImplSockets.cpp
index f5e89e6..23190c1 100644
--- a/src/inet/UDPEndPointImplSockets.cpp
+++ b/src/inet/UDPEndPointImplSockets.cpp
@@ -170,8 +170,7 @@
 } // anonymous namespace
 
 #if CHIP_SYSTEM_CONFIG_USE_PLATFORM_MULTICAST_API
-UDPEndPointImplSockets::MulticastGroupHandler UDPEndPointImplSockets::sJoinMulticastGroupHandler;
-UDPEndPointImplSockets::MulticastGroupHandler UDPEndPointImplSockets::sLeaveMulticastGroupHandler;
+UDPEndPointImplSockets::MulticastGroupHandler UDPEndPointImplSockets::sMulticastGroupHandler;
 #endif // CHIP_SYSTEM_CONFIG_USE_PLATFORM_MULTICAST_API
 
 CHIP_ERROR UDPEndPointImplSockets::BindImpl(IPAddressType addressType, const IPAddress & addr, uint16_t port, InterfaceId interface)
@@ -801,10 +800,9 @@
 CHIP_ERROR UDPEndPointImplSockets::IPv6JoinLeaveMulticastGroupImpl(InterfaceId aInterfaceId, const IPAddress & aAddress, bool join)
 {
 #if CHIP_SYSTEM_CONFIG_USE_PLATFORM_MULTICAST_API
-    MulticastGroupHandler handler = join ? sJoinMulticastGroupHandler : sLeaveMulticastGroupHandler;
-    if (handler != nullptr)
+    if (sMulticastGroupHandler != nullptr)
     {
-        return handler(aInterfaceId, aAddress);
+        return sMulticastGroupHandler(aInterfaceId, aAddress, MulticastOperation::kJoin);
     }
 #endif // CHIP_SYSTEM_CONFIG_USE_PLATFORM_MULTICAST_API
 
diff --git a/src/inet/UDPEndPointImplSockets.h b/src/inet/UDPEndPointImplSockets.h
index c078722..b4e16e8 100644
--- a/src/inet/UDPEndPointImplSockets.h
+++ b/src/inet/UDPEndPointImplSockets.h
@@ -63,13 +63,18 @@
 
 #if CHIP_SYSTEM_CONFIG_USE_PLATFORM_MULTICAST_API
 public:
-    using MulticastGroupHandler = CHIP_ERROR (*)(InterfaceId, const IPAddress &);
-    static void SetJoinMulticastGroupHandler(MulticastGroupHandler handler) { sJoinMulticastGroupHandler = handler; }
-    static void SetLeaveMulticastGroupHandler(MulticastGroupHandler handler) { sLeaveMulticastGroupHandler = handler; }
+    enum class MulticastOperation
+    {
+        kJoin,
+        kLeave
+    };
+
+    using MulticastGroupHandler = CHIP_ERROR (*)(InterfaceId, const IPAddress &, MulticastOperation operation);
+
+    static void SetMulticastGroupHandler(MulticastGroupHandler handler) { sMulticastGroupHandler = handler; }
 
 private:
-    static MulticastGroupHandler sJoinMulticastGroupHandler;
-    static MulticastGroupHandler sLeaveMulticastGroupHandler;
+    static MulticastGroupHandler sMulticastGroupHandler;
 #endif // CHIP_SYSTEM_CONFIG_USE_PLATFORM_MULTICAST_API
 };
 
diff --git a/src/platform/Zephyr/ConfigurationManagerImpl.cpp b/src/platform/Zephyr/ConfigurationManagerImpl.cpp
index 1c357a9..f41eed0 100644
--- a/src/platform/Zephyr/ConfigurationManagerImpl.cpp
+++ b/src/platform/Zephyr/ConfigurationManagerImpl.cpp
@@ -209,8 +209,8 @@
 CHIP_ERROR ConfigurationManagerImpl::GetPrimaryWiFiMACAddress(uint8_t * buf)
 {
 #if CHIP_DEVICE_CONFIG_ENABLE_WIFI
-    const net_if * const iface = InetUtils::GetInterface();
-    VerifyOrReturnError(iface != nullptr && iface->if_dev != nullptr, CHIP_ERROR_INTERNAL);
+    const net_if * const iface = InetUtils::GetWiFiInterface();
+    VerifyOrReturnError(iface != nullptr, CHIP_ERROR_INTERNAL);
 
     const auto linkAddrStruct = iface->if_dev->link_addr;
     memcpy(buf, linkAddrStruct.addr, linkAddrStruct.len);
diff --git a/src/platform/Zephyr/InetUtils.cpp b/src/platform/Zephyr/InetUtils.cpp
index 1169cf6..07d2a30 100644
--- a/src/platform/Zephyr/InetUtils.cpp
+++ b/src/platform/Zephyr/InetUtils.cpp
@@ -17,11 +17,13 @@
 
 #include "InetUtils.h"
 
+#include <zephyr/net/net_if.h>
+
 namespace chip {
 namespace DeviceLayer {
 namespace InetUtils {
 
-in6_addr ToZephyrAddr(const chip::Inet::IPAddress & address)
+in6_addr ToZephyrAddr(const Inet::IPAddress & address)
 {
     in6_addr zephyrAddr;
 
@@ -31,11 +33,16 @@
     return zephyrAddr;
 }
 
-net_if * GetInterface(chip::Inet::InterfaceId ifaceId)
+net_if * GetInterface(Inet::InterfaceId ifaceId)
 {
     return ifaceId.IsPresent() ? net_if_get_by_index(ifaceId.GetPlatformInterface()) : net_if_get_default();
 }
 
+net_if * GetWiFiInterface()
+{
+    return net_if_get_first_wifi();
+}
+
 } // namespace InetUtils
 } // namespace DeviceLayer
 } // namespace chip
diff --git a/src/platform/Zephyr/InetUtils.h b/src/platform/Zephyr/InetUtils.h
index ad7c5e1..0ba3da6 100644
--- a/src/platform/Zephyr/InetUtils.h
+++ b/src/platform/Zephyr/InetUtils.h
@@ -24,8 +24,9 @@
 namespace DeviceLayer {
 namespace InetUtils {
 
-in6_addr ToZephyrAddr(const chip::Inet::IPAddress & address);
-net_if * GetInterface(chip::Inet::InterfaceId ifaceId = chip::Inet::InterfaceId::Null());
+in6_addr ToZephyrAddr(const Inet::IPAddress & address);
+net_if * GetInterface(Inet::InterfaceId ifaceId = Inet::InterfaceId::Null());
+net_if * GetWiFiInterface();
 
 } // namespace InetUtils
 } // namespace DeviceLayer
diff --git a/src/platform/Zephyr/ThreadStackManagerImpl.cpp b/src/platform/Zephyr/ThreadStackManagerImpl.cpp
index 95bdfc8..a7b3929 100644
--- a/src/platform/Zephyr/ThreadStackManagerImpl.cpp
+++ b/src/platform/Zephyr/ThreadStackManagerImpl.cpp
@@ -27,16 +27,13 @@
 #include <platform/OpenThread/GenericThreadStackManagerImpl_OpenThread.hpp>
 #include <platform/Zephyr/ThreadStackManagerImpl.h>
 
-#include <inet/UDPEndPointImpl.h>
 #include <lib/support/CodeUtils.h>
-#include <platform/OpenThread/OpenThreadUtils.h>
 #include <platform/ThreadStackManager.h>
 
 namespace chip {
 namespace DeviceLayer {
 
 using namespace ::chip::DeviceLayer::Internal;
-using namespace ::chip::Inet;
 
 ThreadStackManagerImpl ThreadStackManagerImpl::sInstance;
 
@@ -46,26 +43,6 @@
 
     ReturnErrorOnFailure(GenericThreadStackManagerImpl_OpenThread<ThreadStackManagerImpl>::DoInit(instance));
 
-    UDPEndPointImplSockets::SetJoinMulticastGroupHandler([](InterfaceId, const IPAddress & address) {
-        const otIp6Address otAddress = ToOpenThreadIP6Address(address);
-
-        ThreadStackMgr().LockThreadStack();
-        const auto otError = otIp6SubscribeMulticastAddress(openthread_get_default_instance(), &otAddress);
-        ThreadStackMgr().UnlockThreadStack();
-
-        return MapOpenThreadError(otError);
-    });
-
-    UDPEndPointImplSockets::SetLeaveMulticastGroupHandler([](InterfaceId, const IPAddress & address) {
-        const otIp6Address otAddress = ToOpenThreadIP6Address(address);
-
-        ThreadStackMgr().LockThreadStack();
-        const auto otError = otIp6UnsubscribeMulticastAddress(openthread_get_default_instance(), &otAddress);
-        ThreadStackMgr().UnlockThreadStack();
-
-        return MapOpenThreadError(otError);
-    });
-
 #if CHIP_DEVICE_CONFIG_ENABLE_THREAD_SRP_CLIENT
     k_sem_init(&mSrpClearAllSemaphore, 0, 1);
 #endif // CHIP_DEVICE_CONFIG_ENABLE_THREAD_SRP_CLIENT
diff --git a/src/platform/Zephyr/wifi/WiFiManager.cpp b/src/platform/Zephyr/wifi/WiFiManager.cpp
index 8b6fdf6..69d2943 100644
--- a/src/platform/Zephyr/wifi/WiFiManager.cpp
+++ b/src/platform/Zephyr/wifi/WiFiManager.cpp
@@ -159,33 +159,33 @@
 CHIP_ERROR WiFiManager::Init()
 {
     // TODO: consider moving these to ConnectivityManagerImpl to be prepared for handling multiple interfaces on a single device.
-    Inet::UDPEndPointImplSockets::SetJoinMulticastGroupHandler([](Inet::InterfaceId interfaceId, const Inet::IPAddress & address) {
-        const in6_addr addr = InetUtils::ToZephyrAddr(address);
-        net_if * iface      = InetUtils::GetInterface(interfaceId);
-        VerifyOrReturnError(iface != nullptr, INET_ERROR_UNKNOWN_INTERFACE);
+    Inet::UDPEndPointImplSockets::SetMulticastGroupHandler(
+        [](Inet::InterfaceId interfaceId, const Inet::IPAddress & address, UDPEndPointImplSockets::MulticastOperation operation) {
+            const in6_addr addr = InetUtils::ToZephyrAddr(address);
+            net_if * iface      = InetUtils::GetInterface(interfaceId);
+            VerifyOrReturnError(iface != nullptr, INET_ERROR_UNKNOWN_INTERFACE);
 
-        net_if_mcast_addr * maddr = net_if_ipv6_maddr_add(iface, &addr);
+            if (operation == UDPEndPointImplSockets::MulticastOperation::kJoin)
+            {
+                net_if_mcast_addr * maddr = net_if_ipv6_maddr_add(iface, &addr);
 
-        if (maddr && !net_if_ipv6_maddr_is_joined(maddr) && !net_ipv6_is_addr_mcast_link_all_nodes(&addr))
-        {
-            net_if_ipv6_maddr_join(iface, maddr);
-        }
+                if (maddr && !net_if_ipv6_maddr_is_joined(maddr) && !net_ipv6_is_addr_mcast_link_all_nodes(&addr))
+                {
+                    net_if_ipv6_maddr_join(iface, maddr);
+                }
+            }
+            else if (operation == UDPEndPointImplSockets::MulticastOperation::kLeave)
+            {
+                VerifyOrReturnError(net_ipv6_is_addr_mcast_link_all_nodes(&addr) || net_if_ipv6_maddr_rm(iface, &addr),
+                                    CHIP_ERROR_INVALID_ADDRESS);
+            }
+            else
+            {
+                return CHIP_ERROR_INCORRECT_STATE;
+            }
 
-        return CHIP_NO_ERROR;
-    });
-
-    Inet::UDPEndPointImplSockets::SetLeaveMulticastGroupHandler([](Inet::InterfaceId interfaceId, const Inet::IPAddress & address) {
-        const in6_addr addr = InetUtils::ToZephyrAddr(address);
-        net_if * iface      = InetUtils::GetInterface(interfaceId);
-        VerifyOrReturnError(iface != nullptr, INET_ERROR_UNKNOWN_INTERFACE);
-
-        if (!net_ipv6_is_addr_mcast_link_all_nodes(&addr) && !net_if_ipv6_maddr_rm(iface, &addr))
-        {
-            return CHIP_ERROR_INVALID_ADDRESS;
-        }
-
-        return CHIP_NO_ERROR;
-    });
+            return CHIP_NO_ERROR;
+        });
 
     net_mgmt_init_event_callback(&mWiFiMgmtClbk, WifiMgmtEventHandler, kWifiManagementEvents);
     net_mgmt_add_event_callback(&mWiFiMgmtClbk);
diff --git a/src/platform/nrfconnect/BUILD.gn b/src/platform/nrfconnect/BUILD.gn
index f6807b3..7fe9dc2 100644
--- a/src/platform/nrfconnect/BUILD.gn
+++ b/src/platform/nrfconnect/BUILD.gn
@@ -77,6 +77,13 @@
     ]
   }
 
+  if (chip_enable_openthread || chip_enable_wifi) {
+    sources += [
+      "../Zephyr/InetUtils.cpp",
+      "../Zephyr/InetUtils.h",
+    ]
+  }
+
   if (chip_enable_openthread) {
     sources += [
       "../OpenThread/OpenThreadUtils.cpp",
@@ -96,8 +103,6 @@
 
   if (chip_enable_wifi) {
     sources += [
-      "../Zephyr/InetUtils.cpp",
-      "../Zephyr/InetUtils.h",
       "OTAImageProcessorImplWiFi.h",
       "wifi/ConnectivityManagerImplWiFi.cpp",
       "wifi/ConnectivityManagerImplWiFi.h",
diff --git a/src/platform/nrfconnect/ConnectivityManagerImpl.cpp b/src/platform/nrfconnect/ConnectivityManagerImpl.cpp
index 504b0d0..ecd1852 100644
--- a/src/platform/nrfconnect/ConnectivityManagerImpl.cpp
+++ b/src/platform/nrfconnect/ConnectivityManagerImpl.cpp
@@ -17,11 +17,12 @@
 
 #include <platform/internal/CHIPDeviceLayerInternal.h>
 
-#include <platform/ConnectivityManager.h>
-#include <platform/internal/BLEManager.h>
-
+#include <inet/UDPEndPointImplSockets.h>
 #include <lib/support/CodeUtils.h>
 #include <lib/support/logging/CHIPLogging.h>
+#include <platform/ConnectivityManager.h>
+#include <platform/Zephyr/InetUtils.h>
+#include <platform/internal/BLEManager.h>
 
 #include <platform/internal/GenericConnectivityManagerImpl_UDP.ipp>
 
@@ -34,16 +35,64 @@
 #endif
 
 #if CHIP_DEVICE_CONFIG_ENABLE_THREAD
+#include <platform/OpenThread/OpenThreadUtils.h>
 #include <platform/internal/GenericConnectivityManagerImpl_Thread.ipp>
 #endif
 
-using namespace ::chip;
-using namespace ::chip::TLV;
+using namespace ::chip::Inet;
 using namespace ::chip::DeviceLayer::Internal;
 
 namespace chip {
 namespace DeviceLayer {
 
+namespace {
+CHIP_ERROR JoinLeaveMulticastGroup(net_if * iface, const Inet::IPAddress & address,
+                                   UDPEndPointImplSockets::MulticastOperation operation)
+{
+#if CHIP_DEVICE_CONFIG_ENABLE_THREAD
+    if (net_if_l2(iface) == &NET_L2_GET_NAME(OPENTHREAD))
+    {
+        const otIp6Address otAddress = ToOpenThreadIP6Address(address);
+        const auto handler = operation == UDPEndPointImplSockets::MulticastOperation::kJoin ? otIp6SubscribeMulticastAddress
+                                                                                            : otIp6UnsubscribeMulticastAddress;
+        otError error;
+
+        ThreadStackMgr().LockThreadStack();
+        error = handler(openthread_get_default_instance(), &otAddress);
+        ThreadStackMgr().UnlockThreadStack();
+
+        return MapOpenThreadError(error);
+    }
+#endif
+
+#if CHIP_DEVICE_CONFIG_ENABLE_WIFI
+    // The following code should also be valid for other interface types, such as Ethernet,
+    // but they are not officially supported, so for now enable it for Wi-Fi only.
+    const in6_addr in6Addr = InetUtils::ToZephyrAddr(address);
+
+    if (operation == UDPEndPointImplSockets::MulticastOperation::kJoin)
+    {
+        net_if_mcast_addr * maddr = net_if_ipv6_maddr_add(iface, &in6Addr);
+
+        if (maddr && !net_if_ipv6_maddr_is_joined(maddr))
+        {
+            net_if_ipv6_maddr_join(iface, maddr);
+        }
+    }
+    else if (operation == UDPEndPointImplSockets::MulticastOperation::kLeave)
+    {
+        VerifyOrReturnError(net_if_ipv6_maddr_rm(iface, &in6Addr), CHIP_ERROR_INVALID_ADDRESS);
+    }
+    else
+    {
+        return CHIP_ERROR_INCORRECT_STATE;
+    }
+#endif
+
+    return CHIP_NO_ERROR;
+}
+} // namespace
+
 ConnectivityManagerImpl ConnectivityManagerImpl::sInstance;
 
 CHIP_ERROR ConnectivityManagerImpl::_Init()
@@ -54,6 +103,28 @@
 #if CHIP_DEVICE_CONFIG_ENABLE_WIFI
     ReturnErrorOnFailure(InitWiFi());
 #endif
+
+#if CHIP_DEVICE_CONFIG_ENABLE_THREAD || CHIP_DEVICE_CONFIG_ENABLE_WIFI
+    UDPEndPointImplSockets::SetMulticastGroupHandler(
+        [](InterfaceId interfaceId, const IPAddress & address, UDPEndPointImplSockets::MulticastOperation operation) {
+            if (interfaceId.IsPresent())
+            {
+                net_if * iface = InetUtils::GetInterface(interfaceId);
+                VerifyOrReturnError(iface != nullptr, INET_ERROR_UNKNOWN_INTERFACE);
+
+                return JoinLeaveMulticastGroup(iface, address, operation);
+            }
+
+            // If the interface is not specified, join or leave the multicast group on all interfaces.
+            for (int i = 1; net_if * iface = net_if_get_by_index(i); i++)
+            {
+                ReturnErrorOnFailure(JoinLeaveMulticastGroup(iface, address, operation));
+            }
+
+            return CHIP_NO_ERROR;
+        });
+#endif // CHIP_DEVICE_CONFIG_ENABLE_THREAD || CHIP_DEVICE_CONFIG_ENABLE_WIFI
+
     return CHIP_NO_ERROR;
 }
 
diff --git a/src/platform/nrfconnect/wifi/NrfWiFiDriver.cpp b/src/platform/nrfconnect/wifi/NrfWiFiDriver.cpp
index 1efe5c8..0cf1b38 100644
--- a/src/platform/nrfconnect/wifi/NrfWiFiDriver.cpp
+++ b/src/platform/nrfconnect/wifi/NrfWiFiDriver.cpp
@@ -106,8 +106,9 @@
 
     if (mStagingNetwork.IsConfigured())
     {
-        WiFiManager::ConnectionHandling handling{ [] { Instance().OnNetworkStatusChanged(Status::kSuccess); },
-                                                  [] { Instance().OnNetworkStatusChanged(Status::kUnknownError); },
+        WiFiManager::ConnectionHandling handling{ [](const wifi_conn_status & connStatus) {
+                                                     Instance().OnNetworkConnStatusChanged(connStatus);
+                                                 },
                                                   System::Clock::Seconds32{ kWiFiConnectNetworkTimeoutSeconds } };
         ReturnErrorOnFailure(
             WiFiManager::Instance().Connect(mStagingNetwork.GetSsidSpan(), mStagingNetwork.GetPassSpan(), handling));
@@ -116,8 +117,11 @@
     return CHIP_NO_ERROR;
 }
 
-void NrfWiFiDriver::OnNetworkStatusChanged(Status status)
+void NrfWiFiDriver::OnNetworkConnStatusChanged(const wifi_conn_status & connStatus)
 {
+    // TODO: check if we can report more accurate errors
+    Status status = connStatus ? Status::kUnknownError : Status::kSuccess;
+
     if (status == Status::kSuccess)
     {
         ConnectivityMgr().SetWiFiStationMode(ConnectivityManager::kWiFiStationMode_Enabled);
@@ -125,7 +129,23 @@
 
     if (mpNetworkStatusChangeCallback)
     {
-        mpNetworkStatusChangeCallback->OnNetworkingStatusChange(status, NullOptional, NullOptional);
+        const uint8_t * ssid{};
+        size_t ssidLen{};
+        WiFiManager::WiFiInfo wifiInfo;
+
+        if (CHIP_NO_ERROR == WiFiManager::Instance().GetWiFiInfo(wifiInfo))
+        {
+            ssid    = wifiInfo.mSsid;
+            ssidLen = wifiInfo.mSsidLen;
+        }
+        else
+        {
+            ssid    = WiFiManager::Instance().GetWantedNetwork().ssid;
+            ssidLen = WiFiManager::Instance().GetWantedNetwork().ssidLen;
+        }
+        mpNetworkStatusChangeCallback->OnNetworkingStatusChange(status, MakeOptional(ByteSpan(wifiInfo.mSsid, wifiInfo.mSsidLen)),
+                                                                connStatus ? MakeOptional(static_cast<int32_t>(connStatus))
+                                                                           : NullOptional);
     }
 
     if (mpConnectCallback)
@@ -167,8 +187,9 @@
 
     if (mStagingNetwork.IsConfigured())
     {
-        WiFiManager::ConnectionHandling handling{ [] { Instance().OnNetworkStatusChanged(Status::kSuccess); },
-                                                  [] { Instance().OnNetworkStatusChanged(Status::kUnknownError); },
+        WiFiManager::ConnectionHandling handling{ [](const wifi_conn_status & connStatus) {
+                                                     Instance().OnNetworkConnStatusChanged(connStatus);
+                                                 },
                                                   System::Clock::Seconds32{ kWiFiConnectNetworkTimeoutSeconds } };
         ReturnErrorOnFailure(
             WiFiManager::Instance().Connect(mStagingNetwork.GetSsidSpan(), mStagingNetwork.GetPassSpan(), handling));
@@ -221,8 +242,9 @@
 void NrfWiFiDriver::ConnectNetwork(ByteSpan networkId, ConnectCallback * callback)
 {
     Status status = Status::kSuccess;
-    WiFiManager::ConnectionHandling handling{ [] { Instance().OnNetworkStatusChanged(Status::kSuccess); },
-                                              [] { Instance().OnNetworkStatusChanged(Status::kUnknownError); },
+    WiFiManager::ConnectionHandling handling{ [](const wifi_conn_status & connStatus) {
+                                                 Instance().OnNetworkConnStatusChanged(connStatus);
+                                             },
                                               System::Clock::Seconds32{ kWiFiConnectNetworkTimeoutSeconds } };
 
     VerifyOrExit(mpConnectCallback == nullptr, status = Status::kUnknownError);
@@ -252,11 +274,10 @@
     mStagingNetwork = network;
 }
 
-void NrfWiFiDriver::OnScanWiFiNetworkDone(WiFiManager::WiFiRequestStatus status)
+void NrfWiFiDriver::OnScanWiFiNetworkDone(const WiFiManager::ScanDoneStatus & status)
 {
     VerifyOrReturn(mScanCallback != nullptr);
-    mScanCallback->OnFinished(status == WiFiManager::WiFiRequestStatus::SUCCESS ? Status::kSuccess : Status::kUnknownError,
-                              CharSpan(), &mScanResponseIterator);
+    mScanCallback->OnFinished(status ? Status::kUnknownError : Status::kSuccess, CharSpan(), &mScanResponseIterator);
     mScanCallback = nullptr;
 }
 
@@ -270,7 +291,7 @@
     mScanCallback    = callback;
     CHIP_ERROR error = WiFiManager::Instance().Scan(
         ssid, [](const WiFiScanResponse & response) { Instance().OnScanWiFiNetworkResult(response); },
-        [](WiFiManager::WiFiRequestStatus status) { Instance().OnScanWiFiNetworkDone(status); });
+        [](const WiFiManager::ScanDoneStatus & status) { Instance().OnScanWiFiNetworkDone(status); });
 
     if (error != CHIP_NO_ERROR)
     {
diff --git a/src/platform/nrfconnect/wifi/NrfWiFiDriver.h b/src/platform/nrfconnect/wifi/NrfWiFiDriver.h
index 7fe1ff1..5422e4e 100644
--- a/src/platform/nrfconnect/wifi/NrfWiFiDriver.h
+++ b/src/platform/nrfconnect/wifi/NrfWiFiDriver.h
@@ -94,9 +94,9 @@
         return sInstance;
     }
 
-    void OnNetworkStatusChanged(Status status);
+    void OnNetworkConnStatusChanged(const wifi_conn_status & connStatus);
     void OnScanWiFiNetworkResult(const WiFiScanResponse & result);
-    void OnScanWiFiNetworkDone(WiFiManager::WiFiRequestStatus status);
+    void OnScanWiFiNetworkDone(const WiFiManager::ScanDoneStatus & status);
 
 private:
     void LoadFromStorage();
diff --git a/src/platform/nrfconnect/wifi/WiFiManager.cpp b/src/platform/nrfconnect/wifi/WiFiManager.cpp
index c3435a9..6e3ff8e 100644
--- a/src/platform/nrfconnect/wifi/WiFiManager.cpp
+++ b/src/platform/nrfconnect/wifi/WiFiManager.cpp
@@ -23,8 +23,6 @@
 #include "WiFiManager.h"
 
 #include <crypto/RandUtils.h>
-#include <inet/InetInterface.h>
-#include <inet/UDPEndPointImplSockets.h>
 #include <lib/support/logging/CHIPLogging.h>
 #include <platform/CHIPDeviceLayer.h>
 #include <platform/Zephyr/InetUtils.h>
@@ -138,16 +136,17 @@
                               { WIFI_STATE_GROUP_HANDSHAKE, WiFiManager::StationStatus::PROVISIONING },
                               { WIFI_STATE_COMPLETED, WiFiManager::StationStatus::FULLY_PROVISIONED } });
 
-const Map<uint32_t, WiFiManager::NetEventHandler, 5>
-    WiFiManager::sEventHandlerMap({ { NET_EVENT_WIFI_SCAN_RESULT, WiFiManager::ScanResultHandler },
-                                    { NET_EVENT_WIFI_SCAN_DONE, WiFiManager::ScanDoneHandler },
-                                    { NET_EVENT_WIFI_CONNECT_RESULT, WiFiManager::ConnectHandler },
-                                    { NET_EVENT_WIFI_DISCONNECT_RESULT, WiFiManager::DisconnectHandler },
-                                    { NET_EVENT_WIFI_DISCONNECT_COMPLETE, WiFiManager::DisconnectHandler } });
+const Map<uint32_t, WiFiManager::NetEventHandler, 5> WiFiManager::sEventHandlerMap({
+    { NET_EVENT_WIFI_SCAN_RESULT, WiFiManager::ScanResultHandler },
+    { NET_EVENT_WIFI_SCAN_DONE, WiFiManager::ScanDoneHandler },
+    { NET_EVENT_WIFI_CONNECT_RESULT, WiFiManager::ConnectHandler },
+    { NET_EVENT_WIFI_DISCONNECT_RESULT, WiFiManager::DisconnectHandler },
+    { NET_EVENT_WIFI_DISCONNECT_COMPLETE, WiFiManager::DisconnectHandler },
+});
 
 void WiFiManager::WifiMgmtEventHandler(net_mgmt_event_callback * cb, uint32_t mgmtEvent, net_if * iface)
 {
-    if (0 == strcmp(iface->if_dev->dev->name, "wlan0"))
+    if (iface == Instance().mNetIf)
     {
         Platform::UniquePtr<uint8_t> eventData(new uint8_t[cb->info_length]);
         VerifyOrReturn(eventData);
@@ -156,39 +155,24 @@
     }
 }
 
+void WiFiManager::IPv6MgmtEventHandler(net_mgmt_event_callback * cb, uint32_t mgmtEvent, net_if * iface)
+{
+    if (((mgmtEvent == NET_EVENT_IPV6_ADDR_ADD) || (mgmtEvent == NET_EVENT_IPV6_ADDR_DEL)) && cb->info)
+    {
+        IPv6AddressChangeHandler(cb->info);
+    }
+}
+
 CHIP_ERROR WiFiManager::Init()
 {
-    // TODO: consider moving these to ConnectivityManagerImpl to be prepared for handling multiple interfaces on a single device.
-    Inet::UDPEndPointImplSockets::SetJoinMulticastGroupHandler([](Inet::InterfaceId interfaceId, const Inet::IPAddress & address) {
-        const in6_addr addr = InetUtils::ToZephyrAddr(address);
-        net_if * iface      = InetUtils::GetInterface(interfaceId);
-        VerifyOrReturnError(iface != nullptr, INET_ERROR_UNKNOWN_INTERFACE);
-
-        net_if_mcast_addr * maddr = net_if_ipv6_maddr_add(iface, &addr);
-
-        if (maddr && !net_if_ipv6_maddr_is_joined(maddr) && !net_ipv6_is_addr_mcast_link_all_nodes(&addr))
-        {
-            net_if_ipv6_maddr_join(iface, maddr);
-        }
-
-        return CHIP_NO_ERROR;
-    });
-
-    Inet::UDPEndPointImplSockets::SetLeaveMulticastGroupHandler([](Inet::InterfaceId interfaceId, const Inet::IPAddress & address) {
-        const in6_addr addr = InetUtils::ToZephyrAddr(address);
-        net_if * iface      = InetUtils::GetInterface(interfaceId);
-        VerifyOrReturnError(iface != nullptr, INET_ERROR_UNKNOWN_INTERFACE);
-
-        if (!net_ipv6_is_addr_mcast_link_all_nodes(&addr) && !net_if_ipv6_maddr_rm(iface, &addr))
-        {
-            return CHIP_ERROR_INVALID_ADDRESS;
-        }
-
-        return CHIP_NO_ERROR;
-    });
+    mNetIf = InetUtils::GetWiFiInterface();
+    VerifyOrReturnError(mNetIf != nullptr, INET_ERROR_UNKNOWN_INTERFACE);
 
     net_mgmt_init_event_callback(&mWiFiMgmtClbk, WifiMgmtEventHandler, kWifiManagementEvents);
+    net_mgmt_init_event_callback(&mIPv6MgmtClbk, IPv6MgmtEventHandler, kIPv6ManagementEvents);
+
     net_mgmt_add_event_callback(&mWiFiMgmtClbk);
+    net_mgmt_add_event_callback(&mIPv6MgmtClbk);
 
     ChipLogDetail(DeviceLayer, "WiFiManager has been initialized");
 
@@ -197,9 +181,6 @@
 CHIP_ERROR WiFiManager::Scan(const ByteSpan & ssid, ScanResultCallback resultCallback, ScanDoneCallback doneCallback,
                              bool internalScan)
 {
-    net_if * iface = InetUtils::GetInterface();
-    VerifyOrReturnError(nullptr != iface, CHIP_ERROR_INTERNAL);
-
     mInternalScan       = internalScan;
     mScanResultCallback = resultCallback;
     mScanDoneCallback   = doneCallback;
@@ -207,7 +188,7 @@
     mWiFiState          = WIFI_STATE_SCANNING;
     mSsidFound          = false;
 
-    if (0 != net_mgmt(NET_REQUEST_WIFI_SCAN, iface, NULL, 0))
+    if (0 != net_mgmt(NET_REQUEST_WIFI_SCAN, mNetIf, NULL, 0))
     {
         ChipLogError(DeviceLayer, "Scan request failed");
         return CHIP_ERROR_INTERNAL;
@@ -229,9 +210,7 @@
 {
     ChipLogDetail(DeviceLayer, "Connecting to WiFi network: %*s", ssid.size(), ssid.data());
 
-    mHandling.mOnConnectionSuccess = handling.mOnConnectionSuccess;
-    mHandling.mOnConnectionFailed  = handling.mOnConnectionFailed;
-    mHandling.mConnectionTimeout   = handling.mConnectionTimeout;
+    mHandling = handling;
 
     mWiFiState = WIFI_STATE_ASSOCIATING;
 
@@ -248,11 +227,8 @@
 
 CHIP_ERROR WiFiManager::Disconnect()
 {
-    net_if * iface = InetUtils::GetInterface();
-    VerifyOrReturnError(nullptr != iface, CHIP_ERROR_INTERNAL);
-
     mApplicationDisconnectRequested = true;
-    int status                      = net_mgmt(NET_REQUEST_WIFI_DISCONNECT, iface, NULL, 0);
+    int status                      = net_mgmt(NET_REQUEST_WIFI_DISCONNECT, mNetIf, NULL, 0);
 
     if (status)
     {
@@ -277,11 +253,9 @@
 
 CHIP_ERROR WiFiManager::GetWiFiInfo(WiFiInfo & info) const
 {
-    net_if * iface = InetUtils::GetInterface();
-    VerifyOrReturnError(nullptr != iface, CHIP_ERROR_INTERNAL);
-    struct wifi_iface_status status = { 0 };
+    wifi_iface_status status = { 0 };
 
-    if (net_mgmt(NET_REQUEST_WIFI_IFACE_STATUS, iface, &status, sizeof(struct wifi_iface_status)))
+    if (net_mgmt(NET_REQUEST_WIFI_IFACE_STATUS, mNetIf, &status, sizeof(wifi_iface_status)))
     {
         ChipLogError(DeviceLayer, "Status request failed");
         return CHIP_ERROR_INTERNAL;
@@ -306,7 +280,7 @@
 CHIP_ERROR WiFiManager::GetNetworkStatistics(NetworkStatistics & stats) const
 {
     net_stats_wifi data{};
-    net_mgmt(NET_REQUEST_STATS_GET_WIFI, InetUtils::GetInterface(), &data, sizeof(data));
+    net_mgmt(NET_REQUEST_STATS_GET_WIFI, mNetIf, &data, sizeof(data));
 
     stats.mPacketMulticastRxCount = data.multicast.rx;
     stats.mPacketMulticastTxCount = data.multicast.tx;
@@ -321,7 +295,7 @@
 void WiFiManager::ScanResultHandler(Platform::UniquePtr<uint8_t> data)
 {
     // Contrary to other handlers, offload accumulating of the scan results from the CHIP thread to the caller's thread
-    const struct wifi_scan_result * scanResult = reinterpret_cast<const struct wifi_scan_result *>(data.get());
+    const wifi_scan_result * scanResult = reinterpret_cast<const wifi_scan_result *>(data.get());
 
     if (Instance().mInternalScan &&
         Instance().mWantedNetwork.GetSsidSpan().data_equal(ByteSpan(scanResult->ssid, scanResult->ssid_length)))
@@ -352,6 +326,7 @@
             Instance().mWiFiParams.mParams.timeout = Instance().mHandling.mConnectionTimeout.count();
             Instance().mWiFiParams.mParams.channel = WIFI_CHANNEL_ANY;
             Instance().mWiFiParams.mRssi           = scanResult->rssi;
+            Instance().mWiFiParams.mParams.band    = WIFI_FREQ_BAND_UNKNOWN;
             Instance().mSsidFound                  = true;
         }
     }
@@ -366,22 +341,22 @@
 {
     CHIP_ERROR err = SystemLayer().ScheduleLambda([capturedData = data.get()] {
         Platform::UniquePtr<uint8_t> safePtr(capturedData);
-        uint8_t * rawData               = safePtr.get();
-        const wifi_status * status      = reinterpret_cast<const wifi_status *>(rawData);
-        WiFiRequestStatus requestStatus = static_cast<WiFiRequestStatus>(status->status);
+        uint8_t * rawData             = safePtr.get();
+        const wifi_status * status    = reinterpret_cast<const wifi_status *>(rawData);
+        ScanDoneStatus scanDoneStatus = status->status;
 
-        if (requestStatus == WiFiRequestStatus::FAILURE)
+        if (scanDoneStatus)
         {
-            ChipLogError(DeviceLayer, "Wi-Fi scan finalization failure (%d)", status->status);
+            ChipLogError(DeviceLayer, "Wi-Fi scan finalization failure (%d)", scanDoneStatus);
         }
         else
         {
-            ChipLogProgress(DeviceLayer, "Wi-Fi scan done (%d)", status->status);
+            ChipLogProgress(DeviceLayer, "Wi-Fi scan done");
         }
 
         if (Instance().mScanDoneCallback && !Instance().mInternalScan)
         {
-            Instance().mScanDoneCallback(requestStatus);
+            Instance().mScanDoneCallback(scanDoneStatus);
             // restore the connection state from before the scan request was issued
             Instance().mWiFiState = Instance().mCachedWiFiState;
             return;
@@ -397,18 +372,18 @@
                 ChipLogProgress(DeviceLayer, "Starting connection recover: re-scanning... (next attempt in %d ms)",
                                 currentTimeout.count());
                 DeviceLayer::SystemLayer().StartTimer(currentTimeout, Recover, nullptr);
+                return;
             }
 
             Instance().mWiFiState = WIFI_STATE_ASSOCIATING;
-            net_if * iface        = InetUtils::GetInterface();
-            VerifyOrReturn(nullptr != iface, CHIP_ERROR_INTERNAL);
 
-            if (net_mgmt(NET_REQUEST_WIFI_CONNECT, iface, &(Instance().mWiFiParams.mParams), sizeof(wifi_connect_req_params)))
+            if (net_mgmt(NET_REQUEST_WIFI_CONNECT, Instance().mNetIf, &(Instance().mWiFiParams.mParams),
+                         sizeof(wifi_connect_req_params)))
             {
                 ChipLogError(DeviceLayer, "Connection request failed");
-                if (Instance().mHandling.mOnConnectionFailed)
+                if (Instance().mHandling.mOnConnectionDone)
                 {
-                    Instance().mHandling.mOnConnectionFailed();
+                    Instance().mHandling.mOnConnectionDone(WIFI_STATUS_CONN_FAIL);
                 }
                 Instance().mWiFiState = WIFI_STATE_DISCONNECTED;
                 return;
@@ -428,20 +403,16 @@
 
 void WiFiManager::SendRouterSolicitation(System::Layer * layer, void * param)
 {
-    net_if * iface = InetUtils::GetInterface();
-    if (iface && iface->if_dev->link_addr.type == NET_LINK_ETHERNET)
+    net_if_start_rs(Instance().mNetIf);
+    Instance().mRouterSolicitationCounter++;
+    if (Instance().mRouterSolicitationCounter < kRouterSolicitationMaxCount)
     {
-        net_if_start_rs(iface);
-        Instance().mRouterSolicitationCounter++;
-        if (Instance().mRouterSolicitationCounter < kRouterSolicitationMaxCount)
-        {
-            DeviceLayer::SystemLayer().StartTimer(System::Clock::Milliseconds32(kRouterSolicitationIntervalMs),
-                                                  SendRouterSolicitation, nullptr);
-        }
-        else
-        {
-            Instance().mRouterSolicitationCounter = 0;
-        }
+        DeviceLayer::SystemLayer().StartTimer(System::Clock::Milliseconds32(kRouterSolicitationIntervalMs), SendRouterSolicitation,
+                                              nullptr);
+    }
+    else
+    {
+        Instance().mRouterSolicitationCounter = 0;
     }
 }
 
@@ -449,17 +420,17 @@
 {
     CHIP_ERROR err = SystemLayer().ScheduleLambda([capturedData = data.get()] {
         Platform::UniquePtr<uint8_t> safePtr(capturedData);
-        uint8_t * rawData               = safePtr.get();
-        const wifi_status * status      = reinterpret_cast<const wifi_status *>(rawData);
-        WiFiRequestStatus requestStatus = static_cast<WiFiRequestStatus>(status->status);
+        uint8_t * rawData           = safePtr.get();
+        const wifi_status * status  = reinterpret_cast<const wifi_status *>(rawData);
+        wifi_conn_status connStatus = status->conn_status;
 
-        if (requestStatus == WiFiRequestStatus::FAILURE || requestStatus == WiFiRequestStatus::TERMINATED)
+        if (connStatus)
         {
             ChipLogProgress(DeviceLayer, "Connection to WiFi network failed or was terminated by another request");
             Instance().mWiFiState = WIFI_STATE_DISCONNECTED;
-            if (Instance().mHandling.mOnConnectionFailed)
+            if (Instance().mHandling.mOnConnectionDone)
             {
-                Instance().mHandling.mOnConnectionFailed();
+                Instance().mHandling.mOnConnectionDone(connStatus);
             }
         }
         else // The connection has been established successfully.
@@ -471,9 +442,9 @@
 
             ChipLogProgress(DeviceLayer, "Connected to WiFi network");
             Instance().mWiFiState = WIFI_STATE_COMPLETED;
-            if (Instance().mHandling.mOnConnectionSuccess)
+            if (Instance().mHandling.mOnConnectionDone)
             {
-                Instance().mHandling.mOnConnectionSuccess();
+                Instance().mHandling.mOnConnectionDone(connStatus);
             }
             Instance().PostConnectivityStatusChange(kConnectivity_Established);
 
@@ -507,6 +478,25 @@
     });
 }
 
+void WiFiManager::IPv6AddressChangeHandler(const void * data)
+{
+    const in6_addr * addr = reinterpret_cast<const in6_addr *>(data);
+
+    // Filter out link-local addresses that are not routable outside of a local network.
+    if (!net_ipv6_is_ll_addr(addr))
+    {
+        // This is needed to send mDNS queries containing updated IPv6 addresses.
+        ChipDeviceEvent event;
+        event.Type = DeviceEventType::kDnssdRestartNeeded;
+
+        CHIP_ERROR error = PlatformMgr().PostEvent(&event);
+        if (error != CHIP_NO_ERROR)
+        {
+            ChipLogError(DeviceLayer, "Cannot post event: %" CHIP_ERROR_FORMAT, error.Format());
+        }
+    }
+}
+
 WiFiManager::StationStatus WiFiManager::GetStationStatus() const
 {
     return WiFiManager::sStatusMap[mWiFiState];
@@ -570,11 +560,10 @@
 
 CHIP_ERROR WiFiManager::SetLowPowerMode(bool onoff)
 {
-    net_if * iface = InetUtils::GetInterface();
-    VerifyOrReturnError(nullptr != iface, CHIP_ERROR_INTERNAL);
+    VerifyOrReturnError(nullptr != mNetIf, CHIP_ERROR_INTERNAL);
 
     wifi_ps_config currentConfig{};
-    if (net_mgmt(NET_REQUEST_WIFI_PS_CONFIG, iface, &currentConfig, sizeof(currentConfig)))
+    if (net_mgmt(NET_REQUEST_WIFI_PS_CONFIG, mNetIf, &currentConfig, sizeof(currentConfig)))
     {
         ChipLogError(DeviceLayer, "Get current low power mode config request failed");
         return CHIP_ERROR_INTERNAL;
@@ -584,7 +573,7 @@
         (currentConfig.ps_params.enabled == WIFI_PS_DISABLED && onoff == true))
     {
         wifi_ps_params params{ .enabled = onoff ? WIFI_PS_ENABLED : WIFI_PS_DISABLED };
-        if (net_mgmt(NET_REQUEST_WIFI_PS, iface, &params, sizeof(params)))
+        if (net_mgmt(NET_REQUEST_WIFI_PS, mNetIf, &params, sizeof(params)))
         {
             ChipLogError(DeviceLayer, "Set low power mode request failed");
             return CHIP_ERROR_INTERNAL;
diff --git a/src/platform/nrfconnect/wifi/WiFiManager.h b/src/platform/nrfconnect/wifi/WiFiManager.h
index 248f7dc..2694b05 100644
--- a/src/platform/nrfconnect/wifi/WiFiManager.h
+++ b/src/platform/nrfconnect/wifi/WiFiManager.h
@@ -88,16 +88,10 @@
 class WiFiManager
 {
 public:
-    enum WiFiRequestStatus : int
-    {
-        SUCCESS    = 0,
-        FAILURE    = 1,
-        TERMINATED = 2
-    };
-
+    using ScanDoneStatus     = decltype(wifi_status::status);
     using ScanResultCallback = void (*)(const NetworkCommissioning::WiFiScanResponse &);
-    using ScanDoneCallback   = void (*)(WiFiRequestStatus);
-    using ConnectionCallback = void (*)();
+    using ScanDoneCallback   = void (*)(const ScanDoneStatus &);
+    using ConnectionCallback = void (*)(const wifi_conn_status &);
 
     enum class StationStatus : uint8_t
     {
@@ -120,8 +114,7 @@
 
     struct ConnectionHandling
     {
-        ConnectionCallback mOnConnectionSuccess{};
-        ConnectionCallback mOnConnectionFailed{};
+        ConnectionCallback mOnConnectionDone{};
         System::Clock::Seconds32 mConnectionTimeout{};
     };
 
@@ -182,6 +175,7 @@
     CHIP_ERROR ClearStationProvisioningData();
     CHIP_ERROR Disconnect();
     CHIP_ERROR GetWiFiInfo(WiFiInfo & info) const;
+    const WiFiNetwork & GetWantedNetwork() const { return mWantedNetwork; }
     CHIP_ERROR GetNetworkStatistics(NetworkStatistics & stats) const;
     void AbortConnectionRecovery();
     CHIP_ERROR SetLowPowerMode(bool onoff);
@@ -198,14 +192,18 @@
     constexpr static uint32_t kWifiManagementEvents = NET_EVENT_WIFI_SCAN_RESULT | NET_EVENT_WIFI_SCAN_DONE |
         NET_EVENT_WIFI_CONNECT_RESULT | NET_EVENT_WIFI_DISCONNECT_RESULT | NET_EVENT_WIFI_IFACE_STATUS;
 
+    constexpr static uint32_t kIPv6ManagementEvents = NET_EVENT_IPV6_ADDR_ADD | NET_EVENT_IPV6_ADDR_DEL;
+
     // Event handling
     static void WifiMgmtEventHandler(net_mgmt_event_callback * cb, uint32_t mgmtEvent, net_if * iface);
+    static void IPv6MgmtEventHandler(net_mgmt_event_callback * cb, uint32_t mgmtEvent, net_if * iface);
     static void ScanResultHandler(Platform::UniquePtr<uint8_t> data);
     static void ScanDoneHandler(Platform::UniquePtr<uint8_t> data);
     static void ConnectHandler(Platform::UniquePtr<uint8_t> data);
     static void DisconnectHandler(Platform::UniquePtr<uint8_t> data);
     static void PostConnectivityStatusChange(ConnectivityChange changeType);
     static void SendRouterSolicitation(System::Layer * layer, void * param);
+    static void IPv6AddressChangeHandler(const void * data);
 
     // Connection Recovery feature
     // This feature allows re-scanning and re-connecting the connection to the known network after
@@ -220,11 +218,13 @@
     void ResetRecoveryTime();
     System::Clock::Milliseconds32 CalculateNextRecoveryTime();
 
+    net_if * mNetIf{ nullptr };
     ConnectionParams mWiFiParams{};
     ConnectionHandling mHandling;
     wifi_iface_state mWiFiState;
     wifi_iface_state mCachedWiFiState;
     net_mgmt_event_callback mWiFiMgmtClbk{};
+    net_mgmt_event_callback mIPv6MgmtClbk{};
     ScanResultCallback mScanResultCallback{ nullptr };
     ScanDoneCallback mScanDoneCallback{ nullptr };
     WiFiNetwork mWantedNetwork{};
diff --git a/src/platform/telink/ThreadStackManagerImpl.cpp b/src/platform/telink/ThreadStackManagerImpl.cpp
index 13ba8be..2ec9b02 100644
--- a/src/platform/telink/ThreadStackManagerImpl.cpp
+++ b/src/platform/telink/ThreadStackManagerImpl.cpp
@@ -48,25 +48,19 @@
 
     ReturnErrorOnFailure(GenericThreadStackManagerImpl_OpenThread<ThreadStackManagerImpl>::DoInit(instance));
 
-    UDPEndPointImplSockets::SetJoinMulticastGroupHandler([](InterfaceId, const IPAddress & address) {
-        const otIp6Address otAddress = ToOpenThreadIP6Address(address);
+    UDPEndPointImplSockets::SetMulticastGroupHandler(
+        [](InterfaceId, const IPAddress & address, UDPEndPointImplSockets::MulticastOperation operation) {
+            const otIp6Address otAddress = ToOpenThreadIP6Address(address);
+            const auto handler = operation == UDPEndPointImplSockets::MulticastOperation::kJoin ? otIp6SubscribeMulticastAddress
+                                                                                                : otIp6UnsubscribeMulticastAddress;
+            otError error;
 
-        ThreadStackMgr().LockThreadStack();
-        const auto otError = otIp6SubscribeMulticastAddress(openthread_get_default_instance(), &otAddress);
-        ThreadStackMgr().UnlockThreadStack();
+            ThreadStackMgr().LockThreadStack();
+            error = handler(openthread_get_default_instance(), &otAddress);
+            ThreadStackMgr().UnlockThreadStack();
 
-        return MapOpenThreadError(otError);
-    });
-
-    UDPEndPointImplSockets::SetLeaveMulticastGroupHandler([](InterfaceId, const IPAddress & address) {
-        const otIp6Address otAddress = ToOpenThreadIP6Address(address);
-
-        ThreadStackMgr().LockThreadStack();
-        const auto otError = otIp6UnsubscribeMulticastAddress(openthread_get_default_instance(), &otAddress);
-        ThreadStackMgr().UnlockThreadStack();
-
-        return MapOpenThreadError(otError);
-    });
+            return MapOpenThreadError(error);
+        });
 
 #if CHIP_DEVICE_CONFIG_ENABLE_THREAD_SRP_CLIENT
     k_sem_init(&mSrpClearAllSemaphore, 0, 1);