diff --git a/plugin/NetworkManagerImplementation.cpp b/plugin/NetworkManagerImplementation.cpp index b4b5bca6..51ce6013 100644 --- a/plugin/NetworkManagerImplementation.cpp +++ b/plugin/NetworkManagerImplementation.cpp @@ -654,8 +654,6 @@ namespace WPEFramework { if(interface == "eth0") { - m_ethIPv4Address = {}; - m_ethIPv6Address = {}; m_ethConnected.store(false); setDefaultInterface("wlan0"); // If WiFi is connected, make it the default interface // As default interface is changed to wlan0, switch connectivity monitor to initial check @@ -663,8 +661,6 @@ namespace WPEFramework } else if(interface == "wlan0") { - m_wlanIPv4Address = {}; - m_wlanIPv6Address = {}; m_wlanConnected.store(false); bool triggerConnectivityCheck; if(m_ethConnected.load()) @@ -775,7 +771,9 @@ namespace WPEFramework } _notificationLock.Lock(); - NMLOG_INFO("Posting onIPAddressChange %s - %s", ipaddress.c_str(), interface.c_str()); + NMLOG_INFO("Posting onIPAddressChange %s: %s %s %s", + (Exchange::INetworkManager::IP_ACQUIRED == status) ? "IP acquired" : "IP lost", + interface.c_str(), ipversion.c_str(), ipaddress.c_str()); for (const auto callback : _notificationCallbacks) { callback->onIPAddressChange(interface, ipversion, ipaddress, status); } diff --git a/plugin/NetworkManagerImplementation.h b/plugin/NetworkManagerImplementation.h index f5bd49b1..84d853c5 100644 --- a/plugin/NetworkManagerImplementation.h +++ b/plugin/NetworkManagerImplementation.h @@ -26,6 +26,7 @@ #include #include #include +#include #include using namespace std; @@ -62,6 +63,45 @@ namespace WPEFramework { namespace Plugin { + /* Returns true if the given string is an IPv6 link-local address (fe80::/10): + first byte 0xfe, second byte with top two bits == 10 (0x80..0xbf). */ + inline bool isIPv6LinkLocal(const std::string& addr) + { + struct in6_addr sa6{}; + return inet_pton(AF_INET6, addr.c_str(), &sa6) == 1 && + sa6.s6_addr[0] == 0xfe && (sa6.s6_addr[1] & 0xc0) == 0x80; + } + + /* Per-interface, per-address-family cache populated by libnm events. */ + struct IpFamilyCache { + bool valid = false; + std::map globalAddresses; // key=address, value=prefix length + std::string linkLocalAddress; // fe80:: for IPv6 (ula field), or 169.254.x.x for IPv4 + std::string gateway; + std::string primarydns; + std::string secondarydns; + std::string dhcpserver; + bool autoconfig = false; + + Exchange::INetworkManager::IPAddress toIPAddress(bool isIPv6) const { + Exchange::INetworkManager::IPAddress addr{}; + addr.ipversion = isIPv6 ? "IPv6" : "IPv4"; + addr.autoconfig = autoconfig; + addr.dhcpserver = dhcpserver; + addr.ula = linkLocalAddress; + addr.gateway = gateway; + addr.primarydns = primarydns; + addr.secondarydns = secondarydns; + if (!globalAddresses.empty()) { + const auto& first = *globalAddresses.begin(); + addr.ipaddress = first.first; + addr.prefix = first.second; + } + return addr; + } + void clear() { *this = IpFamilyCache{}; } + }; + class NetworkManagerImplementation : public Exchange::INetworkManager { enum NetworkEvents @@ -315,10 +355,11 @@ namespace WPEFramework std::mutex m_condVariableMutex; std::condition_variable m_condVariable; public: - IPAddress m_ethIPv4Address; - IPAddress m_wlanIPv4Address; - IPAddress m_ethIPv6Address; - IPAddress m_wlanIPv6Address; + IpFamilyCache m_ethIPv4Cache; + IpFamilyCache m_wlanIPv4Cache; + IpFamilyCache m_ethIPv6Cache; + IpFamilyCache m_wlanIPv6Cache; + mutable std::mutex m_ipCacheMutex; std::atomic m_ethConnected; std::atomic m_wlanConnected; std::atomic m_ethEnabled; diff --git a/plugin/gnome/NetworkManagerGnomeEvents.cpp b/plugin/gnome/NetworkManagerGnomeEvents.cpp index 761c39fb..033af3ee 100644 --- a/plugin/gnome/NetworkManagerGnomeEvents.cpp +++ b/plugin/gnome/NetworkManagerGnomeEvents.cpp @@ -30,6 +30,11 @@ #include "NetworkManagerGnomeUtils.h" #include "NetworkManagerImplementation.h" #include "INetworkManager.h" +#include +#include +#ifndef IN_IS_ADDR_LINKLOCAL +#define IN_IS_ADDR_LINKLOCAL(a) ((((uint32_t)ntohl(a)) & 0xffff0000U) == 0xa9fe0000U) +#endif #ifdef ENABLE_MIGRATION_MFRMGR_SUPPORT #include "NetworkManagerGnomeMfrMgr.h" #endif @@ -125,11 +130,15 @@ namespace WPEFramework case NM_DEVICE_STATE_UNKNOWN: wifiState = "WIFI_STATE_UNINSTALLED"; GnomeNetworkManagerEvents::onWIFIStateChanged(Exchange::INetworkManager::WIFI_STATE_UNINSTALLED); + refreshIpFamilyCache(device, false); + refreshIpFamilyCache(device, true); GnomeNetworkManagerEvents::onInterfaceStateChangeCb(Exchange::INetworkManager::INTERFACE_REMOVED, nmUtils::wlanIface()); break; case NM_DEVICE_STATE_UNMANAGED: wifiState = "WIFI_STATE_DISABLED"; GnomeNetworkManagerEvents::onWIFIStateChanged(Exchange::INetworkManager::WIFI_STATE_DISABLED); + refreshIpFamilyCache(device, false); + refreshIpFamilyCache(device, true); GnomeNetworkManagerEvents::onInterfaceStateChangeCb(Exchange::INetworkManager::INTERFACE_REMOVED, nmUtils::wlanIface()); isWlanDisabled = true; break; @@ -137,6 +146,8 @@ namespace WPEFramework case NM_DEVICE_STATE_DISCONNECTED: wifiState = "WIFI_STATE_DISCONNECTED"; GnomeNetworkManagerEvents::onWIFIStateChanged(Exchange::INetworkManager::WIFI_STATE_DISCONNECTED); + refreshIpFamilyCache(device, false); + refreshIpFamilyCache(device, true); GnomeNetworkManagerEvents::onInterfaceStateChangeCb(Exchange::INetworkManager::INTERFACE_LINK_DOWN, nmUtils::wlanIface()); break; case NM_DEVICE_STATE_PREPARE: @@ -212,11 +223,15 @@ namespace WPEFramework { case NM_DEVICE_STATE_UNKNOWN: case NM_DEVICE_STATE_UNMANAGED: + refreshIpFamilyCache(device, false); + refreshIpFamilyCache(device, true); GnomeNetworkManagerEvents::onInterfaceStateChangeCb(Exchange::INetworkManager::INTERFACE_REMOVED, nmUtils::ethIface()); isEthDisabled = true; break; case NM_DEVICE_STATE_UNAVAILABLE: case NM_DEVICE_STATE_DISCONNECTED: + refreshIpFamilyCache(device, false); + refreshIpFamilyCache(device, true); GnomeNetworkManagerEvents::onInterfaceStateChangeCb(Exchange::INetworkManager::INTERFACE_LINK_DOWN, nmUtils::ethIface()); break; case NM_DEVICE_STATE_PREPARE: @@ -261,99 +276,202 @@ namespace WPEFramework } } - static void ip4ChangedCb(NMIPConfig *ipConfig, GParamSpec *pspec, gpointer userData) + /* Build a fresh IpFamilyCache from current libnm state for one device/family, + swap it into _instance under the cache mutex, then emit acquired/lost events + for any address-set differences outside the lock. */ + void refreshIpFamilyCache(NMDevice* device, bool isIPv6) { - if (!ipConfig) { - NMLOG_ERROR("IP config is null"); + if (!device || !NM_IS_DEVICE(device) || !_instance) return; - } - - NMDevice *device = (NMDevice*)userData; - if((device == NULL) || (!NM_IS_DEVICE(device))) - return; const char* iface = nm_device_get_iface(device); - if(iface == NULL) - return; + if (!iface) return; std::string ifname = iface; - GPtrArray *addresses = nm_ip_config_get_addresses(ipConfig); - if (!addresses) { - NMLOG_ERROR("No addresses found"); - return; - } - else { - if(addresses->len == 0) { - GnomeNetworkManagerEvents::onAddressChangeCb(ifname, "", false, false); - return; + bool isEth = (ifname == nmUtils::ethIface()); + bool isWlan = (ifname == nmUtils::wlanIface()); + if (!isEth && !isWlan) return; + + /* Build the new snapshot locally (no locks held during NM calls). + * Skip the NM read when the device is in a disconnected/down state + * so that newCache stays empty and the diff emits IP_LOST for every + * address still in the cache. This also prevents spurious + * "IP acquired" events from intermediate NM signals (nameserver, + * gateway clearing) that fire after the cache has been emptied + * but before NM clears addresses on the config object. */ + NMDeviceState devState = nm_device_get_state(device); + bool skipRead = (devState <= NM_DEVICE_STATE_DISCONNECTED); + IpFamilyCache newCache; + NMActiveConnection* conn = skipRead ? nullptr : nm_device_get_active_connection(device); + if (conn) { + /* autoconfig: method "auto" or "dhcp" → true */ + NMConnection* nmConn = NM_CONNECTION(nm_active_connection_get_connection(conn)); + if (nmConn) { + NMSettingIPConfig* ipSetting = isIPv6 + ? NM_SETTING_IP_CONFIG(nm_connection_get_setting_ip6_config(nmConn)) + : NM_SETTING_IP_CONFIG(nm_connection_get_setting_ip4_config(nmConn)); + if (ipSetting) { + const char* method = nm_setting_ip_config_get_method(ipSetting); + newCache.autoconfig = method && + (g_strcmp0(method, "auto") == 0 || g_strcmp0(method, "dhcp") == 0); + } } } - for (guint i = 0; i < addresses->len; ++i) { - NMIPAddress *address = (NMIPAddress *)g_ptr_array_index(addresses, i); - if(address == NULL) - { - NMLOG_WARNING("IPv4 address is null"); - continue; + /* IP config read is device-level and does not require an active connection. */ + NMIPConfig* ipConfig = skipRead ? nullptr + : (isIPv6 ? nm_device_get_ip6_config(device) + : nm_device_get_ip4_config(device)); + + if (ipConfig) { + GPtrArray* addresses = nm_ip_config_get_addresses(ipConfig); + if (addresses) { + for (guint i = 0; i < addresses->len; i++) { + NMIPAddress* addr = (NMIPAddress*)g_ptr_array_index(addresses, i); + if (!addr) continue; + const char* addrStr = nm_ip_address_get_address(addr); + if (!addrStr) continue; + std::string addrString = addrStr; + if (isIPv6) { + if (isIPv6LinkLocal(addrString)) { + newCache.linkLocalAddress = addrString; + } else { + newCache.globalAddresses.emplace(addrString, nm_ip_address_get_prefix(addr)); + } + } else { + struct sockaddr_in sa{}; + if (inet_pton(AF_INET, addrString.c_str(), &sa.sin_addr) == 1 && + IN_IS_ADDR_LINKLOCAL(sa.sin_addr.s_addr)) { + newCache.linkLocalAddress = addrString; + } else { + newCache.globalAddresses.emplace(addrString, nm_ip_address_get_prefix(addr)); + } + } + } } - if (nm_ip_address_get_family(address) == AF_INET) { - const char *ipAddress = nm_ip_address_get_address(address); - if(ipAddress != NULL) { - GnomeNetworkManagerEvents::onAddressChangeCb(iface, ipAddress, true, false); + + const char* gw = nm_ip_config_get_gateway(ipConfig); + if (gw) newCache.gateway = gw; + + const char* const* dnsArr = nm_ip_config_get_nameservers(ipConfig); + if (dnsArr && dnsArr[0]) { + newCache.primarydns = dnsArr[0]; + if (dnsArr[1]) newCache.secondarydns = dnsArr[1]; + } + + if (conn) { + NMDhcpConfig* dhcpConfig = isIPv6 + ? nm_active_connection_get_dhcp6_config(conn) + : nm_active_connection_get_dhcp4_config(conn); + if (dhcpConfig) { + const char* server = nm_dhcp_config_get_one_option(dhcpConfig, "dhcp_server_identifier"); + if (server) newCache.dhcpserver = server; } } + + newCache.valid = true; + } + + /* Swap new snapshot into instance cache under mutex; collect old address keys. */ + std::set oldKeys; + { + std::lock_guard lock(_instance->m_ipCacheMutex); + IpFamilyCache* cache = nullptr; + if (isEth) + cache = isIPv6 ? &_instance->m_ethIPv6Cache : &_instance->m_ethIPv4Cache; + else + cache = isIPv6 ? &_instance->m_wlanIPv6Cache : &_instance->m_wlanIPv4Cache; + for (const auto& kv : cache->globalAddresses) oldKeys.insert(kv.first); + *cache = newCache; + } + + /* Emit address acquired/lost events from map-key diff (outside the lock). */ + std::string family = isIPv6 ? "IPv6" : "IPv4"; + for (const auto& kv : newCache.globalAddresses) { + if (oldKeys.find(kv.first) == oldKeys.end()) { + _instance->ReportIPAddressChange(ifname, family, kv.first, Exchange::INetworkManager::IP_ACQUIRED); + } + } + for (const auto& key : oldKeys) { + if (newCache.globalAddresses.find(key) == newCache.globalAddresses.end()) { + _instance->ReportIPAddressChange(ifname, family, key, Exchange::INetworkManager::IP_LOST); + } } } + static void ip4ChangedCb(NMIPConfig *ipConfig, GParamSpec *pspec, gpointer userData) + { + NMDevice *device = (NMDevice*)userData; + if (!device || !NM_IS_DEVICE(device)) return; + refreshIpFamilyCache(device, false); + } + static void ip6ChangedCb(NMIPConfig *ipConfig, GParamSpec *pspec, gpointer userData) { - if (!ipConfig) { - NMLOG_ERROR("ip config is null"); - return; - } + NMDevice *device = (NMDevice*)userData; + if (!device || !NM_IS_DEVICE(device)) return; + refreshIpFamilyCache(device, true); + } + /* Called when DHCP options change mid-lease (e.g. renewed with different server/options). */ + static void dhcp4OptionsCb(NMDhcpConfig *dhcpConfig, GParamSpec *pspec, gpointer userData) + { NMDevice *device = (NMDevice*)userData; - if( ((device != NULL) && NM_IS_DEVICE(device)) ) - { - const char* iface = nm_device_get_iface(device); - if(iface == NULL) - return; - std::string ifname = iface; - GPtrArray *addresses = nm_ip_config_get_addresses(ipConfig); - if (!addresses) { - NMLOG_ERROR("No addresses found"); - return; - } - else { - if(addresses->len == 0) { - GnomeNetworkManagerEvents::onAddressChangeCb(ifname, "", false, true); - return; - } + if (!device || !NM_IS_DEVICE(device)) return; + refreshIpFamilyCache(device, false); + } + + static void dhcp6OptionsCb(NMDhcpConfig *dhcpConfig, GParamSpec *pspec, gpointer userData) + { + NMDevice *device = (NMDevice*)userData; + if (!device || !NM_IS_DEVICE(device)) return; + refreshIpFamilyCache(device, true); + } + + /* Called when the ip4-config or ip6-config object on a device is replaced + (e.g. after reconnect). Re-attaches notify handlers to the new object. */ + static void ip4ConfigChangedCb(NMDevice *device, GParamSpec *pspec, gpointer userData) + { + if (!device || !NM_IS_DEVICE(device)) return; + NMIPConfig* ipv4Config = nm_device_get_ip4_config(device); + if (ipv4Config) { + g_signal_handlers_disconnect_by_func(ipv4Config, (gpointer)ip4ChangedCb, device); + g_signal_connect(ipv4Config, "notify::addresses", G_CALLBACK(ip4ChangedCb), device); + g_signal_connect(ipv4Config, "notify::gateway", G_CALLBACK(ip4ChangedCb), device); + g_signal_connect(ipv4Config, "notify::nameservers", G_CALLBACK(ip4ChangedCb), device); + } + /* Re-attach DHCP options handler to the (possibly new) DHCP config object. */ + NMActiveConnection* conn4 = nm_device_get_active_connection(device); + if (conn4) { + NMDhcpConfig* dhcp4 = nm_active_connection_get_dhcp4_config(conn4); + if (dhcp4) { + g_signal_handlers_disconnect_by_func(dhcp4, (gpointer)dhcp4OptionsCb, device); + g_signal_connect(dhcp4, "notify::options", G_CALLBACK(dhcp4OptionsCb), device); } + } + refreshIpFamilyCache(device, false); + } - for (guint i = 0; i < addresses->len; ++i) { - NMIPAddress *address = (NMIPAddress *)g_ptr_array_index(addresses, i); - if(address == NULL) - { - NMLOG_WARNING("IPv6 address is null"); - continue; - } - if (nm_ip_address_get_family(address) == AF_INET6) { - const char *ipaddr = nm_ip_address_get_address(address); - //int prefix = nm_ip_address_get_prefix(address); - if(ipaddr != NULL) { - std::string ipAddress = ipaddr; - if (ipAddress.compare(0, 5, "fe80:") == 0 || - ipAddress.compare(0, 6, "fe80::") == 0) { - NMLOG_DEBUG("%s It's link-local ip", ipAddress.c_str()); - continue; // It's link-local so skiping - } - GnomeNetworkManagerEvents::onAddressChangeCb(iface, ipAddress, true, true); - break; // SLAAC protocol may include multip ipv6 address posting only one Global address - } - } + static void ip6ConfigChangedCb(NMDevice *device, GParamSpec *pspec, gpointer userData) + { + if (!device || !NM_IS_DEVICE(device)) return; + NMIPConfig* ipv6Config = nm_device_get_ip6_config(device); + if (ipv6Config) { + g_signal_handlers_disconnect_by_func(ipv6Config, (gpointer)ip6ChangedCb, device); + g_signal_connect(ipv6Config, "notify::addresses", G_CALLBACK(ip6ChangedCb), device); + g_signal_connect(ipv6Config, "notify::gateway", G_CALLBACK(ip6ChangedCb), device); + g_signal_connect(ipv6Config, "notify::nameservers", G_CALLBACK(ip6ChangedCb), device); + } + /* Re-attach DHCP options handler to the (possibly new) DHCP config object. */ + NMActiveConnection* conn6 = nm_device_get_active_connection(device); + if (conn6) { + NMDhcpConfig* dhcp6 = nm_active_connection_get_dhcp6_config(conn6); + if (dhcp6) { + g_signal_handlers_disconnect_by_func(dhcp6, (gpointer)dhcp6OptionsCb, device); + g_signal_connect(dhcp6, "notify::options", G_CALLBACK(dhcp6OptionsCb), device); } } + refreshIpFamilyCache(device, true); } static void deviceAddedCB(NMClient *client, NMDevice *device, NMEvents *nmEvents) @@ -374,15 +492,31 @@ namespace WPEFramework if(ifname == nmUtils::ethIface() || ifname == nmUtils::wlanIface()) { g_signal_connect(device, "notify::" NM_DEVICE_STATE, G_CALLBACK(GnomeNetworkManagerEvents::deviceStateChangeCb), nmEvents); - // TODO call notify::" NM_DEVICE_ACTIVE_CONNECTION if needed + g_signal_connect(device, "notify::ip4-config", G_CALLBACK(ip4ConfigChangedCb), nmEvents); + g_signal_connect(device, "notify::ip6-config", G_CALLBACK(ip6ConfigChangedCb), nmEvents); NMIPConfig *ipv4Config = nm_device_get_ip4_config(device); NMIPConfig *ipv6Config = nm_device_get_ip6_config(device); if (ipv4Config) { - g_signal_connect(ipv4Config, "notify::addresses", G_CALLBACK(ip4ChangedCb), device); + g_signal_connect(ipv4Config, "notify::addresses", G_CALLBACK(ip4ChangedCb), device); + g_signal_connect(ipv4Config, "notify::gateway", G_CALLBACK(ip4ChangedCb), device); + g_signal_connect(ipv4Config, "notify::nameservers", G_CALLBACK(ip4ChangedCb), device); } if (ipv6Config) { - g_signal_connect(ipv6Config, "notify::addresses", G_CALLBACK(ip6ChangedCb), device); + g_signal_connect(ipv6Config, "notify::addresses", G_CALLBACK(ip6ChangedCb), device); + g_signal_connect(ipv6Config, "notify::gateway", G_CALLBACK(ip6ChangedCb), device); + g_signal_connect(ipv6Config, "notify::nameservers", G_CALLBACK(ip6ChangedCb), device); + } + + /* Subscribe to DHCP option changes so dhcpserver stays current mid-lease. */ + NMActiveConnection* connAdded = nm_device_get_active_connection(device); + if (connAdded) { + NMDhcpConfig* dhcp4Added = nm_active_connection_get_dhcp4_config(connAdded); + NMDhcpConfig* dhcp6Added = nm_active_connection_get_dhcp6_config(connAdded); + if (dhcp4Added) + g_signal_connect(dhcp4Added, "notify::options", G_CALLBACK(dhcp4OptionsCb), device); + if (dhcp6Added) + g_signal_connect(dhcp6Added, "notify::options", G_CALLBACK(dhcp6OptionsCb), device); } if (NM_IS_DEVICE_WIFI(device)) @@ -492,6 +626,8 @@ namespace WPEFramework /* Register device state change event */ g_signal_connect(device, "notify::" NM_DEVICE_STATE, G_CALLBACK(GnomeNetworkManagerEvents::deviceStateChangeCb), nmEvents); + g_signal_connect(device, "notify::ip4-config", G_CALLBACK(ip4ConfigChangedCb), nmEvents); + g_signal_connect(device, "notify::ip6-config", G_CALLBACK(ip6ConfigChangedCb), nmEvents); if(NM_IS_DEVICE_WIFI(device)) { nmEvents->wifiDevice = NM_DEVICE_WIFI(device); g_signal_connect(nmEvents->wifiDevice, "notify::" NM_DEVICE_WIFI_LAST_SCAN, G_CALLBACK(GnomeNetworkManagerEvents::onAvailableSSIDsCb), nmEvents); @@ -500,18 +636,35 @@ namespace WPEFramework NMIPConfig *ipv4Config = nm_device_get_ip4_config(device); NMIPConfig *ipv6Config = nm_device_get_ip6_config(device); if (ipv4Config) { - ip4ChangedCb(ipv4Config, NULL, device); // posting event if interface already connected - g_signal_connect(ipv4Config, "notify::addresses", G_CALLBACK(ip4ChangedCb), device); + g_signal_connect(ipv4Config, "notify::addresses", G_CALLBACK(ip4ChangedCb), device); + g_signal_connect(ipv4Config, "notify::gateway", G_CALLBACK(ip4ChangedCb), device); + g_signal_connect(ipv4Config, "notify::nameservers", G_CALLBACK(ip4ChangedCb), device); } else NMLOG_WARNING("IPv4 config is null for device: %s, No IPv4 monitor", ifname.c_str()); if (ipv6Config) { - ip6ChangedCb(ipv6Config, NULL, device); - g_signal_connect(ipv6Config, "notify::addresses", G_CALLBACK(ip6ChangedCb), device); + g_signal_connect(ipv6Config, "notify::addresses", G_CALLBACK(ip6ChangedCb), device); + g_signal_connect(ipv6Config, "notify::gateway", G_CALLBACK(ip6ChangedCb), device); + g_signal_connect(ipv6Config, "notify::nameservers", G_CALLBACK(ip6ChangedCb), device); } else NMLOG_WARNING("IPv6 config is null for device: %s, No IPv6 monitor", ifname.c_str()); + + /* Subscribe to DHCP option changes so dhcpserver stays current mid-lease. */ + NMActiveConnection* connInit = nm_device_get_active_connection(device); + if (connInit) { + NMDhcpConfig* dhcp4Init = nm_active_connection_get_dhcp4_config(connInit); + NMDhcpConfig* dhcp6Init = nm_active_connection_get_dhcp6_config(connInit); + if (dhcp4Init) + g_signal_connect(dhcp4Init, "notify::options", G_CALLBACK(dhcp4OptionsCb), device); + if (dhcp6Init) + g_signal_connect(dhcp6Init, "notify::options", G_CALLBACK(dhcp6OptionsCb), device); + } + + /* Seed the IP cache from current state for already-connected devices. */ + refreshIpFamilyCache(device, false); + refreshIpFamilyCache(device, true); } else NMLOG_DEBUG("device type not eth/wifi %s", ifname.c_str()); @@ -714,53 +867,6 @@ namespace WPEFramework } } - void GnomeNetworkManagerEvents::onAddressChangeCb(std::string iface, std::string ipAddress, bool acquired, bool isIPv6) - { - /* - * notify::addresses g signal only send ipaddress when accuired time only. - * we need to post ip address when ipaddress lost case also so we caching the ip address per interface - */ - static std::map ipv6Map; - static std::map ipv4Map; - - if(acquired) - { - if (isIPv6) - { - if (ipv6Map[iface].find(ipAddress) == std::string::npos) { // same ip comes multiple time so avoding that - ipv6Map[iface] = ipAddress; - } - else // same ip not posting - return; - } - else - { - ipv4Map[iface] = ipAddress; - } - } - else - { - if (isIPv6) - { - ipAddress = ipv6Map[iface]; - ipv6Map[iface].clear(); - } - else - { - ipAddress = ipv4Map[iface]; - ipv4Map[iface].clear(); - } - if(ipAddress.empty()) - return; // empty ip address not posting event - } - Exchange::INetworkManager::IPStatus ipStatus{}; - if (acquired) - ipStatus = Exchange::INetworkManager::IP_ACQUIRED; - if(_instance != nullptr) - _instance->ReportIPAddressChange(iface, isIPv6?"IPv6":"IPv4", ipAddress, ipStatus); - NMLOG_INFO("iface:%s - ipaddress:%s - %s - %s", iface.c_str(), ipAddress.c_str(), acquired?"acquired":"lost", isIPv6?"isIPv6":"isIPv4"); - } - bool GnomeNetworkManagerEvents::apToJsonObject(NMAccessPoint *ap, JsonObject& ssidObj) { GBytes *ssid = NULL; diff --git a/plugin/gnome/NetworkManagerGnomeEvents.h b/plugin/gnome/NetworkManagerGnomeEvents.h index ee29ce6d..8f9ad429 100644 --- a/plugin/gnome/NetworkManagerGnomeEvents.h +++ b/plugin/gnome/NetworkManagerGnomeEvents.h @@ -39,12 +39,16 @@ namespace WPEFramework std::string ifnameEth0; } NMEvents; + /* Refresh the per-interface/per-family IP cache from current libnm state and + emit acquired/lost events for address-set differences. Called from both + GnomeEvents signal callbacks and the GetIPSettings fallback path. */ + void refreshIpFamilyCache(NMDevice* device, bool isIPv6); + class GnomeNetworkManagerEvents { public: static void onInterfaceStateChangeCb(uint8_t newState, std::string iface); // ReportInterfaceStateChange - static void onAddressChangeCb(std::string iface, std::string ipAddress, bool acqired, bool isIPv6); // ReportIPAddressChange static void onActiveInterfaceChangeCb(std::string newInterface); // ReportActiveInterfaceChange static void onAvailableSSIDsCb(NMDeviceWifi *wifiDevice, GParamSpec *pspec, gpointer userData); // ReportAvailableSSIDs static void onWIFIStateChanged(uint8_t state); // ReportWiFiStateChange diff --git a/plugin/gnome/NetworkManagerGnomeProxy.cpp b/plugin/gnome/NetworkManagerGnomeProxy.cpp index b661a8de..a6e77b44 100644 --- a/plugin/gnome/NetworkManagerGnomeProxy.cpp +++ b/plugin/gnome/NetworkManagerGnomeProxy.cpp @@ -714,35 +714,38 @@ namespace WPEFramework ipversionStr = ipversion; } - // Add caching optimization similar to RDK proxy - if (wifiname == interface) + // Serve from event-driven cache when available { - if(nmUtils::caseInsensitiveCompare(ipversionStr, "IPV4") && !m_wlanIPv4Address.ipaddress.empty()) + std::lock_guard lock(m_ipCacheMutex); + if (wifiname == interface) { - NMLOG_DEBUG("%s IPv4 address from cache", wifiname.c_str()); - result = m_wlanIPv4Address; - return Core::ERROR_NONE; - } - else if(nmUtils::caseInsensitiveCompare(ipversion, "IPV6") && !m_wlanIPv6Address.ipaddress.empty()) - { - NMLOG_DEBUG("%s IPv6 address from cache", wifiname.c_str()); - result = m_wlanIPv6Address; - return Core::ERROR_NONE; - } - } - else if (ethname == interface) - { - if(nmUtils::caseInsensitiveCompare(ipversionStr, "IPV4") && !m_ethIPv4Address.ipaddress.empty()) - { - NMLOG_DEBUG("%s IPv4 address from cache", ethname.c_str()); - result = m_ethIPv4Address; - return Core::ERROR_NONE; + if(nmUtils::caseInsensitiveCompare(ipversionStr, "IPV4") && m_wlanIPv4Cache.valid) + { + NMLOG_DEBUG("%s IPv4 address from cache", wifiname.c_str()); + result = m_wlanIPv4Cache.toIPAddress(false); + return Core::ERROR_NONE; + } + else if(nmUtils::caseInsensitiveCompare(ipversion, "IPV6") && m_wlanIPv6Cache.valid) + { + NMLOG_DEBUG("%s IPv6 address from cache", wifiname.c_str()); + result = m_wlanIPv6Cache.toIPAddress(true); + return Core::ERROR_NONE; + } } - else if(nmUtils::caseInsensitiveCompare(ipversion, "IPV6") && !m_ethIPv6Address.ipaddress.empty()) + else if (ethname == interface) { - NMLOG_DEBUG("%s IPv6 address from cache", ethname.c_str()); - result = m_ethIPv6Address; - return Core::ERROR_NONE; + if(nmUtils::caseInsensitiveCompare(ipversionStr, "IPV4") && m_ethIPv4Cache.valid) + { + NMLOG_DEBUG("%s IPv4 address from cache", ethname.c_str()); + result = m_ethIPv4Cache.toIPAddress(false); + return Core::ERROR_NONE; + } + else if(nmUtils::caseInsensitiveCompare(ipversion, "IPV6") && m_ethIPv6Cache.valid) + { + NMLOG_DEBUG("%s IPv6 address from cache", ethname.c_str()); + result = m_ethIPv6Cache.toIPAddress(true); + return Core::ERROR_NONE; + } } } @@ -884,19 +887,27 @@ namespace WPEFramework if(result.ipaddress.empty()) { NMLOG_WARNING("Only link-local IPv4 available on %s, not returning it", interface.c_str()); - // Clear cache for link-local only + std::lock_guard lock(m_ipCacheMutex); if(ethname == interface) - m_ethIPv4Address = IPAddress(); + m_ethIPv4Cache.clear(); else if(wifiname == interface) - m_wlanIPv4Address = IPAddress(); + m_wlanIPv4Cache.clear(); return Core::ERROR_GENERAL; } - // Cache the IPv4 address - if(ethname == interface) - m_ethIPv4Address = result; - else if(wifiname == interface) - m_wlanIPv4Address = result; + // Cache the IPv4 result (fallback — event-driven cache not yet populated) + { + std::lock_guard lock(m_ipCacheMutex); + IpFamilyCache* c = (ethname == interface) ? &m_ethIPv4Cache : &m_wlanIPv4Cache; + c->clear(); + c->globalAddresses.insert({result.ipaddress, result.prefix}); + c->gateway = result.gateway; + c->primarydns = result.primarydns; + c->secondarydns = result.secondarydns; + c->dhcpserver = result.dhcpserver; + c->autoconfig = result.autoconfig; + c->valid = true; + } } } if((result.ipaddress.empty() && !(nmUtils::caseInsensitiveCompare(ipversion, "IPV4"))) || nmUtils::caseInsensitiveCompare(ipversion, "IPV6")) @@ -924,7 +935,7 @@ namespace WPEFramework } if(!ipStr.empty()) { - if (ipStr.compare(0, 5, "fe80:") == 0 || ipStr.compare(0, 6, "fe80::") == 0) + if (isIPv6LinkLocal(ipStr)) { result.ula = ipStr; NMLOG_DEBUG("link-local ip: %s", result.ula.c_str()); @@ -957,11 +968,21 @@ namespace WPEFramework if(dhcpserver) result.dhcpserver = dhcpserver; } - // Cache the IPv6 address - if(ethname == interface) - m_ethIPv6Address = result; - else if(wifiname == interface) - m_wlanIPv6Address = result; + // Cache the IPv6 result (fallback — event-driven cache not yet populated) + { + std::lock_guard lock(m_ipCacheMutex); + IpFamilyCache* c = (ethname == interface) ? &m_ethIPv6Cache : &m_wlanIPv6Cache; + c->clear(); + if (!result.ipaddress.empty()) + c->globalAddresses.insert({result.ipaddress, result.prefix}); + c->linkLocalAddress = result.ula; + c->gateway = result.gateway; + c->primarydns = result.primarydns; + c->secondarydns = result.secondarydns; + c->dhcpserver = result.dhcpserver; + c->autoconfig = result.autoconfig; + c->valid = true; + } } } if(result.ipaddress.empty()) diff --git a/plugin/gnome/gdbus/NetworkManagerGdbusClient.cpp b/plugin/gnome/gdbus/NetworkManagerGdbusClient.cpp index 2b44c109..f7aa827c 100644 --- a/plugin/gnome/gdbus/NetworkManagerGdbusClient.cpp +++ b/plugin/gnome/gdbus/NetworkManagerGdbusClient.cpp @@ -1113,36 +1113,40 @@ namespace WPEFramework ipversionStr = ipversion; } - // Add caching optimization similar to RDK proxy - if (wifiname == interface) + // Serve from event-driven cache when available { - if(nmUtils::caseInsensitiveCompare(ipversionStr, "IPV4") && !_instance->m_wlanIPv4Address.ipaddress.empty()) + std::lock_guard lock(_instance->m_ipCacheMutex); + if (wifiname == interface) { - NMLOG_DEBUG("%s IPv4 address from cache", wifiname.c_str()); - result = _instance->m_wlanIPv4Address; - return true; + if(nmUtils::caseInsensitiveCompare(ipversionStr, "IPV4") && _instance->m_wlanIPv4Cache.valid) + { + NMLOG_DEBUG("%s IPv4 address from cache", wifiname.c_str()); + result = _instance->m_wlanIPv4Cache.toIPAddress(false); + return true; + } + else if(nmUtils::caseInsensitiveCompare(ipversionStr, "IPV6") && _instance->m_wlanIPv6Cache.valid) + { + NMLOG_DEBUG("%s IPv6 address from cache", wifiname.c_str()); + result = _instance->m_wlanIPv6Cache.toIPAddress(true); + return true; + } } - else if(nmUtils::caseInsensitiveCompare(ipversionStr, "IPV6") && !_instance->m_wlanIPv6Address.ipaddress.empty()) + else if (ethname == interface) { - NMLOG_DEBUG("%s IPv6 address from cache", wifiname.c_str()); - result = _instance->m_wlanIPv6Address; - return true; + if(nmUtils::caseInsensitiveCompare(ipversionStr, "IPV4") && _instance->m_ethIPv4Cache.valid) + { + NMLOG_DEBUG("%s IPv4 address from cache", ethname.c_str()); + result = _instance->m_ethIPv4Cache.toIPAddress(false); + return true; + } + else if(nmUtils::caseInsensitiveCompare(ipversionStr, "IPV6") && _instance->m_ethIPv6Cache.valid) + { + NMLOG_DEBUG("%s IPv6 address from cache", ethname.c_str()); + result = _instance->m_ethIPv6Cache.toIPAddress(true); + return true; + } } } - else if (ethname == interface) - { - if(nmUtils::caseInsensitiveCompare(ipversionStr, "IPV4") && !_instance->m_ethIPv4Address.ipaddress.empty()) - { - NMLOG_DEBUG("%s IPv4 address from cache", ethname.c_str()); - result = _instance->m_ethIPv4Address; - return true; - } - else if(nmUtils::caseInsensitiveCompare(ipversionStr, "IPV6") && !_instance->m_ethIPv6Address.ipaddress.empty()) - { - NMLOG_DEBUG("%s IPv6 address from cache", ethname.c_str()); - result = _instance->m_ethIPv6Address; - return true; - } } if(!GnomeUtils::getDeviceInfoByIfname(m_dbus, interface.c_str(), devInfo)) @@ -1551,22 +1555,27 @@ namespace WPEFramework result.secondarydns = ""; } - // Cache the IP address + // Cache the IP address (fallback path — populate IpFamilyCache from result) if(!result.ipaddress.empty()) { + std::lock_guard lock(_instance->m_ipCacheMutex); + bool isIPv6 = (g_strcmp0(result.ipversion.c_str(), "IPv6") == 0); + IpFamilyCache* c = nullptr; if(g_strcmp0(result.ipversion.c_str(), "IPv4") == 0) - { - if(ethname == interface) - _instance->m_ethIPv4Address = result; - else if(wifiname == interface) - _instance->m_wlanIPv4Address = result; - } - else if(g_strcmp0(result.ipversion.c_str(), "IPv6") == 0) - { - if(ethname == interface) - _instance->m_ethIPv6Address = result; - else if(wifiname == interface) - _instance->m_wlanIPv6Address = result; + c = (ethname == interface) ? &_instance->m_ethIPv4Cache : &_instance->m_wlanIPv4Cache; + else if(isIPv6) + c = (ethname == interface) ? &_instance->m_ethIPv6Cache : &_instance->m_wlanIPv6Cache; + if (c) { + c->clear(); + c->globalAddresses.insert(result.ipaddress); + c->linkLocalAddress = result.ula; + c->prefix = result.prefix; + c->gateway = result.gateway; + c->primarydns = result.primarydns; + c->secondarydns = result.secondarydns; + c->dhcpserver = result.dhcpserver; + c->autoconfig = result.autoconfig; + c->valid = true; } } diff --git a/plugin/gnome/gdbus/NetworkManagerGdbusEvent.cpp b/plugin/gnome/gdbus/NetworkManagerGdbusEvent.cpp index d0c50c40..d80f71cf 100644 --- a/plugin/gnome/gdbus/NetworkManagerGdbusEvent.cpp +++ b/plugin/gnome/gdbus/NetworkManagerGdbusEvent.cpp @@ -710,8 +710,7 @@ namespace WPEFramework } else // acquired { - if (ipAddress.compare(0, 5, "fe80:") == 0 || - ipAddress.compare(0, 6, "fe80::") == 0) { + if (isIPv6LinkLocal(ipAddress)) { NMLOG_DEBUG("It's link-local ip"); return; // It's link-local } diff --git a/plugin/gnome/gdbus/NetworkManagerGdbusUtils.cpp b/plugin/gnome/gdbus/NetworkManagerGdbusUtils.cpp index ad1659a9..8e2dc9e4 100644 --- a/plugin/gnome/gdbus/NetworkManagerGdbusUtils.cpp +++ b/plugin/gnome/gdbus/NetworkManagerGdbusUtils.cpp @@ -107,8 +107,7 @@ namespace WPEFramework if(address!= NULL) { ipAddress = address; // strlen of "FFFF:FFFF:FFFF:FFFF:FFFF:FFFF:FFFF:FFFF" - if (ipAddress.compare(0, 5, "fe80:") == 0 || - ipAddress.compare(0, 6, "fe80::") == 0) { // It's link-local starts with fe80 + if (isIPv6LinkLocal(ipAddress)) { // It's link-local (fe80::/10) NMLOG_DEBUG("link-local ip: %s", address); } else diff --git a/plugin/rdk/NetworkManagerRDKProxy.cpp b/plugin/rdk/NetworkManagerRDKProxy.cpp index f218d61c..39cc5124 100644 --- a/plugin/rdk/NetworkManagerRDKProxy.cpp +++ b/plugin/rdk/NetworkManagerRDKProxy.cpp @@ -720,27 +720,29 @@ namespace WPEFramework } if ("wlan0" == interface) { - if("IPv4" == ipversionStr && !m_wlanIPv4Address.ipaddress.empty()) + std::lock_guard lock(m_ipCacheMutex); + if("IPv4" == ipversionStr && m_wlanIPv4Cache.valid) { - address = m_wlanIPv4Address; + address = m_wlanIPv4Cache.toIPAddress(false); return Core::ERROR_NONE; } - else if("IPv6" == ipversionStr && !m_wlanIPv6Address.ipaddress.empty()) + else if("IPv6" == ipversionStr && m_wlanIPv6Cache.valid) { - address = m_wlanIPv6Address; + address = m_wlanIPv6Cache.toIPAddress(true); return Core::ERROR_NONE; } } else if ("eth0" == interface) { - if("IPv4" == ipversionStr && !m_ethIPv4Address.ipaddress.empty()) + std::lock_guard lock(m_ipCacheMutex); + if("IPv4" == ipversionStr && m_ethIPv4Cache.valid) { - address = m_ethIPv4Address; + address = m_ethIPv4Cache.toIPAddress(false); return Core::ERROR_NONE; } - else if("IPv6" == ipversionStr && !m_ethIPv6Address.ipaddress.empty()) + else if("IPv6" == ipversionStr && m_ethIPv6Cache.valid) { - address = m_ethIPv6Address; + address = m_ethIPv6Cache.toIPAddress(true); return Core::ERROR_NONE; } } @@ -776,19 +778,28 @@ namespace WPEFramework { address.ipversion = string ("IPv4"); address.prefix = NetmaskToPrefix(iarmData.netmask); - if("eth0" == interface) - m_ethIPv4Address = address; - else if("wlan0" == interface) - m_wlanIPv4Address = address; + std::lock_guard lock(m_ipCacheMutex); + IpFamilyCache* c = ("eth0" == interface) ? &m_ethIPv4Cache : &m_wlanIPv4Cache; + c->clear(); + c->globalAddresses.insert({address.ipaddress, address.prefix}); + c->gateway = address.gateway; + c->primarydns = address.primarydns; c->secondarydns = address.secondarydns; + c->dhcpserver = address.dhcpserver; c->autoconfig = address.autoconfig; + c->valid = true; } else if (0 == strcasecmp("ipv6", iarmData.ipversion)) { address.ipversion = string ("IPv6"); address.prefix = std::atoi(iarmData.netmask); - if("eth0" == interface) - m_ethIPv6Address = address; - else if("wlan0" == interface) - m_wlanIPv6Address = address; + std::lock_guard lock(m_ipCacheMutex); + IpFamilyCache* c = ("eth0" == interface) ? &m_ethIPv6Cache : &m_wlanIPv6Cache; + c->clear(); + if (!address.ipaddress.empty()) c->globalAddresses.insert({address.ipaddress, address.prefix}); + c->linkLocalAddress = address.ula; + c->gateway = address.gateway; + c->primarydns = address.primarydns; c->secondarydns = address.secondarydns; + c->dhcpserver = address.dhcpserver; c->autoconfig = address.autoconfig; + c->valid = true; } rc = Core::ERROR_NONE;