Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
17 commits
Select commit Hold shift + click to select a range
5a4deeb
AI agent's initial code modifications as per approved plan
tukken-comcast Apr 27, 2026
83d6f56
fix(gnome-events): address three IP cache gaps from initial implement…
tukken-comcast Apr 27, 2026
a610cd4
fix: replace narrow fe80: string check with correct IPv6 fe80::/10 de…
tukken-comcast Apr 27, 2026
87f879a
fix: fix build errors after IpFamilyCache::globalAddresses type change
tukken-comcast Apr 30, 2026
f378d53
fix(ip-cache): read IP config from device instead of active connection
tukken-comcast May 5, 2026
478ef34
fix(ip-cache): guard DNS array access to prevent out-of-bounds read
tukken-comcast May 5, 2026
d683658
fix(ip-cache): emit IP_LOST events on interface disconnect
tukken-comcast May 8, 2026
2fecf5c
fix(ip-cache): emit IP_LOST events reliably on interface disconnect
tukken-comcast May 13, 2026
8ae1886
refactor: replace named IpFamilyCache fields with map-based storage
tukken-comcast May 15, 2026
d0c9a27
fix(ip-cache): populate IPAddress.ula with actual ULA, not link-local
tukken-comcast May 18, 2026
3ac157b
feat(ip-cache): filter MAC-based EUI-64 global IPv6 from GetIPSettings
tukken-comcast May 18, 2026
4c418c7
refactor(GetIPSettings): remove fallback path, serve exclusively from…
tukken-comcast May 20, 2026
0229d8e
refactor(ip-cache): split address containers and move helpers out of …
tukken-comcast May 20, 2026
9a4aab6
fix: address PR review feedback on IP cache helpers and proxy
tukken-comcast May 22, 2026
2e78990
fix(gnome): use device-level DHCP config API to fix empty dhcpserver …
tukken-comcast May 28, 2026
5fcde20
build(backends): gate legacy IP members and export backend macros
tukken-comcast Jun 3, 2026
0d02c21
test(libnm): update L1 tests for cache-based GetIPSettings and refres…
tukken-comcast Jun 4, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,14 @@ option(USE_RDK_LOGGER "Enable RDK Logger for logging" OFF )
option(ENABLE_UNIT_TESTING "Enable unit tests" OFF)
option(USE_TELEMETRY "Enable Telemetry T2 support" OFF)

# Backend identity macros are consumed by shared headers; define them globally
# so all targets (plugin, tests, tools) compile against the same API surface.
if(ENABLE_GNOME_NETWORKMANAGER AND ENABLE_GNOME_GDBUS)
add_compile_definitions(NM_BACKEND_GDBUS=1)
elseif(NOT ENABLE_GNOME_NETWORKMANAGER)
add_compile_definitions(NM_BACKEND_RDK=1)
endif()

if (USE_TELEMETRY)
find_package(T2 REQUIRED)
add_compile_definitions(USE_TELEMETRY=1)
Expand Down
4 changes: 2 additions & 2 deletions definition/NetworkManager.json
Original file line number Diff line number Diff line change
Expand Up @@ -47,9 +47,9 @@
"example": 24
},
"ula": {
"summary": "The IPv6 Unified Local Address",
"summary": "The IPv6 Unique Local Address",
"type": "string",
"example": "d00:410:2016::"
"example": "fd00:410:2016::"
},
"gateway": {
"summary": "The gateway address",
Expand Down
2 changes: 1 addition & 1 deletion docs/NetworkManagerPlugin.md
Original file line number Diff line number Diff line change
Expand Up @@ -453,7 +453,7 @@ Gets the IP setting for the given interface.
"ipaddress": "192.168.1.101",
"prefix": 24,
"gateway": "192.168.1.1",
"ula": "d00:410:2016::",
"ula": "",
"primarydns": "192.168.1.1",
"secondarydns": "192.168.1.2",
"success": true
Expand Down
158 changes: 153 additions & 5 deletions plugin/NetworkManagerImplementation.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,9 @@

#include <thread>
#include <chrono>
#include <algorithm>
#include <cstring>
#include <cstdio>
#include "NetworkManagerImplementation.h"

#if USE_TELEMETRY
Expand Down Expand Up @@ -654,17 +657,13 @@ namespace WPEFramework
{
Comment thread
tukken-comcast marked this conversation as resolved.
if(interface == "eth0")
{
m_ethIPv4Address = {};
m_ethIPv6Address = {};
m_ethConnected.store(false);
Comment on lines 658 to 660
setDefaultInterface("wlan0"); // If WiFi is connected, make it the default interface
// As default interface is changed to wlan0, switch connectivity monitor to initial check
Comment on lines 658 to 662
connectivityMonitor.switchToInitialCheck();
}
else if(interface == "wlan0")
{
m_wlanIPv4Address = {};
m_wlanIPv6Address = {};
m_wlanConnected.store(false);
bool triggerConnectivityCheck;
Comment on lines 665 to 668
if(m_ethConnected.load())
Comment on lines 665 to 669
Expand Down Expand Up @@ -775,7 +774,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);
}
Expand Down Expand Up @@ -1184,5 +1185,152 @@ namespace WPEFramework
}
#endif
}

bool isIPv4LinkLocal(const std::string& addr)
{
struct in_addr sa{};
return inet_pton(AF_INET, addr.c_str(), &sa) == 1 &&
(ntohl(sa.s_addr) & 0xffff0000u) == 0xa9fe0000u;
}

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;
}

bool isIPv6ULA(const std::string& addr)
{
struct in6_addr sa6{};
return inet_pton(AF_INET6, addr.c_str(), &sa6) == 1 &&
(sa6.s6_addr[0] & 0xfe) == 0xfc;
}
Comment thread
tukken-comcast marked this conversation as resolved.

/* Parse a MAC string into 6 bytes. Accepts both "AA:BB:CC:DD:EE:FF" and "aabbccddeeff". */
static bool parseMac(const std::string& mac, uint8_t out[6])
{
unsigned int b[6];
if (mac.size() >= 17 &&
sscanf(mac.c_str(), "%02x:%02x:%02x:%02x:%02x:%02x",
&b[0], &b[1], &b[2], &b[3], &b[4], &b[5]) == 6)
{
for (int i = 0; i < 6; ++i) out[i] = static_cast<uint8_t>(b[i]);
return true;
}
if (mac.size() >= 12 &&
sscanf(mac.c_str(), "%02x%02x%02x%02x%02x%02x",
&b[0], &b[1], &b[2], &b[3], &b[4], &b[5]) == 6)
{
for (int i = 0; i < 6; ++i) out[i] = static_cast<uint8_t>(b[i]);
return true;
}
return false;
}

bool isIPv6MacBased(const std::string& ipv6Addr, const std::string& macAddr)
{
struct in6_addr sa6{};
uint8_t mac[6];
if (inet_pton(AF_INET6, ipv6Addr.c_str(), &sa6) != 1 || !parseMac(macAddr, mac))
return false;

/* Build 8-byte EUI-64 identifier from 6-byte MAC:
mac[0..2] | ff:fe | mac[3..5], then flip the U/L bit (bit 1) of byte 0. */
uint8_t eui64[8];
eui64[0] = mac[0] ^ 0x02; // flip Universal/Local bit
eui64[1] = mac[1];
eui64[2] = mac[2];
eui64[3] = 0xff;
eui64[4] = 0xfe;
eui64[5] = mac[3];
eui64[6] = mac[4];
eui64[7] = mac[5];

/* Compare against the interface-ID (last 8 bytes) of the IPv6 address. */
if (memcmp(&sa6.s6_addr[8], eui64, 8) == 0)
{
NMLOG_DEBUG("MAC %s based global v6 address %s", macAddr.c_str(), ipv6Addr.c_str());
return true;
}
return false;
}

bool NetworkManagerImplementation::lookupIpCache(
const std::string& iface, const std::string& ipFamily,
Exchange::INetworkManager::IPAddress& out) const
{
std::lock_guard<std::mutex> lock(m_ipCacheMutex);
auto it = m_ipCacheMap.find({iface, ipFamily});
if (it != m_ipCacheMap.end() && it->second.valid) {
out = it->second.toIPAddress();
return true;
Comment on lines +1259 to +1267
}
return false;
}

std::set<std::string> NetworkManagerImplementation::swapIpCache(
const std::string& iface, const std::string& ipFamily,
IpFamilyCache newCache)
{
std::set<std::string> oldKeys;
std::lock_guard<std::mutex> lock(m_ipCacheMutex);
IpFamilyCache& cache = m_ipCacheMap[{iface, ipFamily}];
for (const auto& kv : cache.globalAddresses)
oldKeys.insert(kv.first);
cache = std::move(newCache);
return oldKeys;
}

Exchange::INetworkManager::IPAddress IpFamilyCache::toIPAddress() const
{
Exchange::INetworkManager::IPAddress addr{};
/* Detect IP version from any available address. */
bool isIPv6 = false;
{
const std::string* sample = nullptr;
if (!globalAddresses.empty())
sample = &globalAddresses.begin()->first;
else if (!uniqueLocalAddresses.empty())
sample = &(*uniqueLocalAddresses.begin());
else if (!linkLocalAddresses.empty())
sample = &(*linkLocalAddresses.begin());
if (sample) {
struct in6_addr sa6{};
isIPv6 = (inet_pton(AF_INET6, sample->c_str(), &sa6) == 1);
}
}
addr.ipversion = isIPv6 ? "IPv6" : "IPv4";
addr.autoconfig = autoconfig;
addr.dhcpserver = dhcpserver;
addr.ula = uniqueLocalAddresses.empty() ? "" : *uniqueLocalAddresses.begin();
addr.gateway = gateway;
addr.primarydns = primarydns;
addr.secondarydns = secondarydns;
/* Prefer non-MAC-based global; fall back to MAC-based if all are MAC-based. */
const std::string* bestGlobal = nullptr;
uint32_t bestPrefix = 0;
const std::string* fallbackMac = nullptr;
uint32_t fallbackMacPrefix = 0;
for (const auto& kv : globalAddresses) {
if (kv.second.type == ADDR_GLOBAL) {
bestGlobal = &kv.first;
bestPrefix = kv.second.prefix;
break;
}
if (!fallbackMac) {
fallbackMac = &kv.first;
fallbackMacPrefix = kv.second.prefix;
}
}
if (bestGlobal) {
addr.ipaddress = *bestGlobal;
addr.prefix = bestPrefix;
} else if (fallbackMac) {
addr.ipaddress = *fallbackMac;
addr.prefix = fallbackMacPrefix;
}
return addr;
}
}
}
53 changes: 53 additions & 0 deletions plugin/NetworkManagerImplementation.h
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,9 @@
#include <arpa/inet.h>
#include <linux/rtnetlink.h>
#include <atomic>
#include <map>
#include <mutex>
#include <set>

using namespace std;

Expand Down Expand Up @@ -62,6 +64,47 @@ namespace WPEFramework
{
namespace Plugin
{
/* Returns true if the given string is an IPv4 link-local address (169.254.0.0/16). */
bool isIPv4LinkLocal(const std::string& addr);

/* Returns true if the given string is an IPv6 link-local address (fe80::/10). */
bool isIPv6LinkLocal(const std::string& addr);

/* Returns true if the given string is an IPv6 Unique Local Address (ULA, fc00::/7). */
bool isIPv6ULA(const std::string& addr);

/* Returns true if the given global IPv6 address is derived (EUI-64) from the MAC. */
bool isIPv6MacBased(const std::string& ipv6Addr, const std::string& macAddr);

/* Sub-classification of global-scope addresses in the IP cache. */
enum GlobalAddressType : uint8_t {
ADDR_GLOBAL, // non-MAC-based global (preferred by GetIPSettings)
ADDR_GLOBAL_MAC_BASED, // EUI-64 global derived from interface MAC (fallback)
};

struct GlobalAddressInfo {
uint32_t prefix;
GlobalAddressType type;
GlobalAddressInfo() : prefix(0), type(ADDR_GLOBAL) {}
GlobalAddressInfo(uint32_t p, GlobalAddressType t) : prefix(p), type(t) {}
};

/* Per-interface, per-address-family cache populated by libnm events. */
struct IpFamilyCache {
bool valid = false;
std::map<std::string, GlobalAddressInfo> globalAddresses; // event-diffable global addresses
std::set<std::string> linkLocalAddresses; // fe80::/10 or 169.254.x.x — not diffed for events
std::set<std::string> uniqueLocalAddresses; // fc00::/7 (IPv6 only) — not diffed for events
std::string gateway;
std::string primarydns;
std::string secondarydns;
std::string dhcpserver;
bool autoconfig = false;

Exchange::INetworkManager::IPAddress toIPAddress() const;
void clear() { *this = IpFamilyCache{}; }
};

class NetworkManagerImplementation : public Exchange::INetworkManager
{
enum NetworkEvents
Expand Down Expand Up @@ -315,10 +358,18 @@ namespace WPEFramework
std::mutex m_condVariableMutex;
std::condition_variable m_condVariable;
public:
#if defined(NM_BACKEND_GDBUS) || defined(NM_BACKEND_RDK)
IPAddress m_ethIPv4Address;
IPAddress m_wlanIPv4Address;
IPAddress m_ethIPv6Address;
IPAddress m_wlanIPv6Address;
#endif
bool lookupIpCache(const std::string& iface, const std::string& ipFamily,
Exchange::INetworkManager::IPAddress& out) const;
std::set<std::string> swapIpCache(const std::string& iface,
const std::string& ipFamily,
IpFamilyCache newCache);

std::atomic<bool> m_ethConnected;
std::atomic<bool> m_wlanConnected;
std::atomic<bool> m_ethEnabled;
Expand All @@ -343,6 +394,8 @@ namespace WPEFramework
private:
string m_defaultInterface;
mutable std::mutex m_defaultInterfaceMutex;
std::map<std::pair<std::string, std::string>, IpFamilyCache> m_ipCacheMap;
mutable std::mutex m_ipCacheMutex;
};
}
}
Loading
Loading