From 08dd34c4337f49f01dc7d7293b088aa5f303e81f Mon Sep 17 00:00:00 2001 From: "Garpathi, Uday Krishna" Date: Wed, 10 Dec 2025 11:37:21 -0500 Subject: [PATCH 1/6] RDK-59986 : NetworkconnectionRecovery.sh migration to C/C++ Module --- networkstats/NetworkConnectionStats.cpp | 192 ++++++++++++++++++++ networkstats/NetworkConnectionStats.h | 105 +++++++++++ networkstats/NetworkConnectionStatsData.cpp | 92 ++++++++++ 3 files changed, 389 insertions(+) create mode 100644 networkstats/NetworkConnectionStats.cpp create mode 100644 networkstats/NetworkConnectionStats.h create mode 100644 networkstats/NetworkConnectionStatsData.cpp diff --git a/networkstats/NetworkConnectionStats.cpp b/networkstats/NetworkConnectionStats.cpp new file mode 100644 index 00000000..da0bd4b8 --- /dev/null +++ b/networkstats/NetworkConnectionStats.cpp @@ -0,0 +1,192 @@ +/** +* If not stated otherwise in this file or this component's LICENSE +* file the following copyright and licenses apply: +* +* Copyright 2025 RDK Management +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +**/ + +#include "NetworkConnectionStats.h" +#include +#include +#include +#include + +#define LOG_ERR(msg, ...) g_printerr("ERROR: " msg "\n", ##__VA_ARGS__) +#define LOG_INFO(msg, ...) g_print("INFO: " msg "\n", ##__VA_ARGS__) + +namespace WPEFramework { +namespace Plugin { + +/* @brief Singleton instance pointer */ +NetworkConnectionStats* NetworkConnectionStats::m_instance = nullptr; + +/* @brief Constructor */ +NetworkConnectionStats::NetworkConnectionStats() +{ + LOG_INFO("NetworkConnectionStats constructor"); + // TODO: Initialize member variables +} + +/* @brief Destructor */ +NetworkConnectionStats::~NetworkConnectionStats() +{ + LOG_INFO("NetworkConnectionStats destructor"); + // TODO: Cleanup resources +} + +/* @brief Get singleton instance of NetworkConnectionStats */ +NetworkConnectionStats* NetworkConnectionStats::getInstance() +{ + if (m_instance == nullptr) + { + m_instance = new NetworkConnectionStats(); + } + return m_instance; +} + +/* @brief Check and validate current connection type */ +void NetworkConnectionStats::connectionTypeCheck() +{ + LOG_INFO("Checking connection type"); + // TODO: Implement connection type check logic +} + +/* @brief Verify IP address configuration */ +void NetworkConnectionStats::connectionIpCheck() +{ + LOG_INFO("Checking connection IP"); + // TODO: Implement IP address verification logic +} + +/* @brief Check default IPv4 route availability */ +void NetworkConnectionStats::defaultIpv4RouteCheck() +{ + LOG_INFO("Checking default IPv4 route"); + // TODO: Implement IPv4 route check logic +} + +/* @brief Check default IPv6 route availability */ +void NetworkConnectionStats::defaultIpv6RouteCheck() +{ + LOG_INFO("Checking default IPv6 route"); + // TODO: Implement IPv6 route check logic +} + +/* @brief Monitor packet loss to gateway */ +void NetworkConnectionStats::gatewayPacketLossCheck() +{ + LOG_INFO("Checking gateway packet loss"); + // TODO: Implement packet loss monitoring logic +} + +/* @brief Validate DNS configuration and resolution */ +void NetworkConnectionStats::networkDnsCheck() +{ + LOG_INFO("Checking network DNS"); + // TODO: Implement DNS validation logic +} + +/* @brief Generate network diagnostics report */ +void NetworkConnectionStats::generateReport() +{ + LOG_INFO("Generating network diagnostics report"); + // TODO: Implement report generation logic +} +} // namespace WPEFramework + +#if ENABLE_NETWORK_STATS_MAIN + +/* @brief Thread function to invoke all member functions every 10 minutes */ +void networkStatsThread(WPEFramework::Plugin::NetworkConnectionStats* statsManager, const std::string& interface) +{ + while (true) + { + LOG_INFO("Starting network diagnostics cycle..."); + + // Populate network data + statsManager->populateNetworkData(); + + // Run all diagnostic checks + statsManager->connectionTypeCheck(); + statsManager->connectionIpCheck(); + statsManager->defaultIpv4RouteCheck(); + statsManager->defaultIpv6RouteCheck(); + statsManager->gatewayPacketLossCheck(); + statsManager->networkDnsCheck(); + + // Get interface information + std::string ipv4 = statsManager->getIpv4Address(interface); + LOG_INFO("IPv4 Address for %s: %s", interface.c_str(), ipv4.c_str()); + + std::string ipv6 = statsManager->getIpv6Address(interface); + LOG_INFO("IPv6 Address for %s: %s", interface.c_str(), ipv6.c_str()); + + std::string connType = statsManager->getConnectionType(); + LOG_INFO("Connection Type: %s", connType.c_str()); + + std::string dnsEntries = statsManager->getDnsEntries(); + LOG_INFO("DNS Entries: %s", dnsEntries.c_str()); + + // Generate report + statsManager->generateReport(); + + LOG_INFO("Network diagnostics cycle completed. Sleeping for 10 minutes..."); + + // Sleep for 10 minutes (600 seconds) + std::this_thread::sleep_for(std::chrono::minutes(10)); + } +} + +/* @brief Main function to parse command line arguments and initiate class objects */ +int main(int argc, char *argv[]) +{ + std::string interface = "wlan0"; // Default interface + + if (argc >= 2) + { + interface = argv[1]; + LOG_INFO("Using interface: %s", interface.c_str()); + } + else + { + LOG_INFO("Usage: %s ", argv[0]); + LOG_INFO("Using default interface: %s", interface.c_str()); + } + + // Get singleton instance + WPEFramework::Plugin::NetworkConnectionStats* statsManager = + WPEFramework::Plugin::NetworkConnectionStats::getInstance(); + + if (statsManager == nullptr) + { + LOG_ERR("Failed to create NetworkConnectionStats instance"); + return -1; + } + + LOG_INFO("NetworkConnectionStats instance created successfully"); + LOG_INFO("Starting network diagnostics thread (runs every 10 minutes)..."); + + // Create and launch the thread + std::thread diagThread(networkStatsThread, statsManager, interface); + + // Wait for the thread to complete (it runs indefinitely) + diagThread.join(); + + LOG_INFO("Program completed"); + return 0; +} +#endifturn 0; +} +#endif diff --git a/networkstats/NetworkConnectionStats.h b/networkstats/NetworkConnectionStats.h new file mode 100644 index 00000000..5da13d79 --- /dev/null +++ b/networkstats/NetworkConnectionStats.h @@ -0,0 +1,105 @@ +/** +* If not stated otherwise in this file or this component's LICENSE +* file the following copyright and licenses apply: +* +* Copyright 2025 RDK Management +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +**/ + +#ifndef __NETWORKCONNECTIONSTATS_H__ +#define __NETWORKCONNECTIONSTATS_H__ + +#include +#include +#include +#include + + +namespace WPEFramework { +namespace Plugin { + +class NetworkConnectionStatsInterfaceData +{ + public: + ~NetworkConnectionStatsInterfaceData(); + + /* @brief Retrieve IPv4 address for specified interface */ + std::string getIpv4Address(std::string interface_name); + + /* @brief Retrieve IPv6 address for specified interface */ + std::string getIpv6Address(std::string interface_name); + + /* @brief Get current network connection type */ + std::string getConnectionType(); + + /* @brief Get DNS server entries */ + std::string getDnsEntries(); + + /* @brief Populate network interface data */ + void populateNetworkData(); + + private: + NetworkConnectionStatsInterfaceData(); + + /* @brief Handle for JSON-RPC communication */ + WPEFramework::JSONRPC::LinkType* controller = nullptr; + + /* @brief Handle for NetworkManager events */ + WPEFramework::JSONRPC::LinkType* networkManager = nullptr; + +}; + +class NetworkConnectionStats : public WPEFramework::Plugin::Service, public NetworkConnectionStatsInterfaceData { +public: + NetworkConnectionStats(const NetworkConnectionStats&) = delete; + + NetworkConnectionStats& operator=(const NetworkConnectionStats&) = delete; + NetworkConnectionStats(); + + virtual ~NetworkConnectionStats(); + + /* @brief Get singleton instance of NetworkConnectionStats */ + static NetworkConnectionStats* getInstance(); + + /* @brief Check and validate current connection type */ + void connectionTypeCheck(); + + /* @brief Verify IP address configuration */ + void connectionIpCheck(); + + /* @brief Check default IPv4 route availability */ + void defaultIpv4RouteCheck(); + + /* @brief Check default IPv6 route availability */ + void defaultIpv6RouteCheck(); + + /* @brief Monitor packet loss to gateway */ + void gatewayPacketLossCheck(); + + /* @brief Validate DNS configuration and resolution */ + void networkDnsCheck(); + + /* @brief Generate network diagnostics report */ + void generateReport(); + +private: + /* @brief Singleton instance pointer */ + static NetworkConnectionStats* m_instance; +}; + +} // namespace Plugin +} // namespace WPEFramework + + +#endif /* __NETWORKCONNECTIONSTATS_H__ */ diff --git a/networkstats/NetworkConnectionStatsData.cpp b/networkstats/NetworkConnectionStatsData.cpp new file mode 100644 index 00000000..647ee0de --- /dev/null +++ b/networkstats/NetworkConnectionStatsData.cpp @@ -0,0 +1,92 @@ +/** +* If not stated otherwise in this file or this component's LICENSE +* file the following copyright and licenses apply: +* +* Copyright 2025 RDK Management +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +**/ + +#include "NetworkConnectionStats.h" +#include +#include + +#define LOG_ERR(msg, ...) g_printerr("ERROR: " msg "\n", ##__VA_ARGS__) +#define LOG_INFO(msg, ...) g_print("INFO: " msg "\n", ##__VA_ARGS__) + +namespace WPEFramework { +namespace Plugin { + +/* @brief Constructor */ +NetworkConnectionStatsInterfaceData::NetworkConnectionStatsInterfaceData() +{ + LOG_INFO("NetworkConnectionStatsInterfaceData constructor"); + // TODO: Initialize JSON-RPC handles +} + +/* @brief Destructor */ +NetworkConnectionStatsInterfaceData::~NetworkConnectionStatsInterfaceData() +{ + LOG_INFO("NetworkConnectionStatsInterfaceData destructor"); + // TODO: Cleanup JSON-RPC handles + if (controller != nullptr) + { + controller = nullptr; + } + if (networkManager != nullptr) + { + networkManager = nullptr; + } +} + +/* @brief Retrieve IPv4 address for specified interface */ +std::string NetworkConnectionStatsInterfaceData::getIpv4Address(std::string interface_name) +{ + LOG_INFO("Getting IPv4 address for interface: %s", interface_name.c_str()); + // TODO: Implement IPv4 address retrieval logic + return ""; +} + +/* @brief Retrieve IPv6 address for specified interface */ +std::string NetworkConnectionStatsInterfaceData::getIpv6Address(std::string interface_name) +{ + LOG_INFO("Getting IPv6 address for interface: %s", interface_name.c_str()); + // TODO: Implement IPv6 address retrieval logic + return ""; +} + +/* @brief Get current network connection type */ +std::string NetworkConnectionStatsInterfaceData::getConnectionType() +{ + LOG_INFO("Getting connection type"); + // TODO: Implement connection type retrieval logic + return ""; +} + +/* @brief Get DNS server entries */ +std::string NetworkConnectionStatsInterfaceData::getDnsEntries() +{ + LOG_INFO("Getting DNS entries"); + // TODO: Implement DNS entries retrieval logic + return ""; +} + +/* @brief Populate network interface data */ +void NetworkConnectionStatsInterfaceData::populateNetworkData() +{ + LOG_INFO("Populating network data"); + // TODO: Implement network data population logic +} + +} // namespace Plugin +} // namespace WPEFramework From ce7a8c7e7ef9ba91bd92f1467ad27a9ac00726f2 Mon Sep 17 00:00:00 2001 From: "Garpathi, Uday Krishna" Date: Wed, 10 Dec 2025 11:37:21 -0500 Subject: [PATCH 2/6] RDK-59986 : NetworkconnectionRecovery.sh migration to C/C++ Module --- networkstats/NetworkConnectionStats.cpp | 25 ++++++++------------- networkstats/NetworkConnectionStats.h | 3 +++ networkstats/NetworkConnectionStatsData.cpp | 8 +++++++ 3 files changed, 20 insertions(+), 16 deletions(-) diff --git a/networkstats/NetworkConnectionStats.cpp b/networkstats/NetworkConnectionStats.cpp index da0bd4b8..4b53bb41 100644 --- a/networkstats/NetworkConnectionStats.cpp +++ b/networkstats/NetworkConnectionStats.cpp @@ -109,7 +109,7 @@ void NetworkConnectionStats::generateReport() #if ENABLE_NETWORK_STATS_MAIN /* @brief Thread function to invoke all member functions every 10 minutes */ -void networkStatsThread(WPEFramework::Plugin::NetworkConnectionStats* statsManager, const std::string& interface) +void networkStatsThread(WPEFramework::Plugin::NetworkConnectionStats* statsManager) { while (true) { @@ -118,6 +118,10 @@ void networkStatsThread(WPEFramework::Plugin::NetworkConnectionStats* statsManag // Populate network data statsManager->populateNetworkData(); + // Get current interface + std::string interface = statsManager->getInterface(); + LOG_INFO("Active interface: %s", interface.c_str()); + // Run all diagnostic checks statsManager->connectionTypeCheck(); statsManager->connectionIpCheck(); @@ -149,21 +153,10 @@ void networkStatsThread(WPEFramework::Plugin::NetworkConnectionStats* statsManag } } -/* @brief Main function to parse command line arguments and initiate class objects */ +/* @brief Main function to initiate class objects */ int main(int argc, char *argv[]) { - std::string interface = "wlan0"; // Default interface - - if (argc >= 2) - { - interface = argv[1]; - LOG_INFO("Using interface: %s", interface.c_str()); - } - else - { - LOG_INFO("Usage: %s ", argv[0]); - LOG_INFO("Using default interface: %s", interface.c_str()); - } + LOG_INFO("Starting NetworkConnectionStats application..."); // Get singleton instance WPEFramework::Plugin::NetworkConnectionStats* statsManager = @@ -179,12 +172,12 @@ int main(int argc, char *argv[]) LOG_INFO("Starting network diagnostics thread (runs every 10 minutes)..."); // Create and launch the thread - std::thread diagThread(networkStatsThread, statsManager, interface); + std::thread diagThread(networkStatsThread, statsManager); // Wait for the thread to complete (it runs indefinitely) diagThread.join(); - LOG_INFO("Program completed"); + LOG_INFO("Done"); return 0; } #endifturn 0; diff --git a/networkstats/NetworkConnectionStats.h b/networkstats/NetworkConnectionStats.h index 5da13d79..85946b0b 100644 --- a/networkstats/NetworkConnectionStats.h +++ b/networkstats/NetworkConnectionStats.h @@ -49,6 +49,9 @@ class NetworkConnectionStatsInterfaceData /* @brief Populate network interface data */ void populateNetworkData(); + /* @brief Get current active interface name */ + std::string getInterface(); + private: NetworkConnectionStatsInterfaceData(); diff --git a/networkstats/NetworkConnectionStatsData.cpp b/networkstats/NetworkConnectionStatsData.cpp index 647ee0de..d688d33a 100644 --- a/networkstats/NetworkConnectionStatsData.cpp +++ b/networkstats/NetworkConnectionStatsData.cpp @@ -88,5 +88,13 @@ void NetworkConnectionStatsInterfaceData::populateNetworkData() // TODO: Implement network data population logic } +/* @brief Get current active interface name */ +std::string NetworkConnectionStatsInterfaceData::getInterface() +{ + LOG_INFO("Getting active interface name"); + // TODO: Implement logic to get active interface name + return "wlan0"; // Default interface +} + } // namespace Plugin } // namespace WPEFramework From 8a6412e14a8e56d92647704b51245cbbea0a41bb Mon Sep 17 00:00:00 2001 From: Balaji Punnuru Date: Wed, 10 Dec 2025 15:06:51 -0500 Subject: [PATCH 3/6] Initial Skeleton. Signed-off-by: Balaji Punnuru --- networkstats/INetworkData.h | 55 +++++++++++++++++++++++++ networkstats/NetworkConnectionStats.cpp | 4 +- networkstats/NetworkConnectionStats.h | 43 ++++--------------- networkstats/ThunderProvider.cpp | 55 +++++++++++++++++++++++++ networkstats/ThunderProvider.h | 53 ++++++++++++++++++++++++ 5 files changed, 174 insertions(+), 36 deletions(-) create mode 100644 networkstats/INetworkData.h create mode 100644 networkstats/ThunderProvider.cpp create mode 100644 networkstats/ThunderProvider.h diff --git a/networkstats/INetworkData.h b/networkstats/INetworkData.h new file mode 100644 index 00000000..f24118b2 --- /dev/null +++ b/networkstats/INetworkData.h @@ -0,0 +1,55 @@ +/** +* If not stated otherwise in this file or this component's LICENSE +* file the following copyright and licenses apply: +* +* Copyright 2025 RDK Management +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +**/ + +#ifndef __NETWORKCONNECTIONSTATS_H__ +#define __NETWORKCONNECTIONSTATS_H__ + +#include + + +class INetworkData +{ + public: + ~NetworkConnectionStatsInterfaceData(); + + /* @brief Retrieve IPv4 address for specified interface */ + std::string getIpv4Address(std::string interface_name) =0; + + /* @brief Retrieve IPv6 address for specified interface */ + std::string getIpv6Address(std::string interface_name) =0; + + /* @brief Get current network connection type */ + std::string getConnectionType() =0; + + /* @brief Get DNS server entries */ + std::string getDnsEntries() =0; + + /* @brief Populate network interface data */ + void populateNetworkData() =0; + + /* @brief Get current active interface name */ + std::string getInterface() =0; + + private: + NetworkConnectionStatsInterfaceData(); + +}; + + +#endif /* __NETWORKCONNECTIONSTATS_H__ */ diff --git a/networkstats/NetworkConnectionStats.cpp b/networkstats/NetworkConnectionStats.cpp index 4b53bb41..cd0d3b8e 100644 --- a/networkstats/NetworkConnectionStats.cpp +++ b/networkstats/NetworkConnectionStats.cpp @@ -33,10 +33,12 @@ namespace Plugin { NetworkConnectionStats* NetworkConnectionStats::m_instance = nullptr; /* @brief Constructor */ -NetworkConnectionStats::NetworkConnectionStats() +NetworkConnectionStats::NetworkConnectionStats():iprovider(NULL) { LOG_INFO("NetworkConnectionStats constructor"); // TODO: Initialize member variables + iprovider = new ThunderDataProvider(); + } /* @brief Destructor */ diff --git a/networkstats/NetworkConnectionStats.h b/networkstats/NetworkConnectionStats.h index 85946b0b..d900957d 100644 --- a/networkstats/NetworkConnectionStats.h +++ b/networkstats/NetworkConnectionStats.h @@ -29,39 +29,6 @@ namespace WPEFramework { namespace Plugin { -class NetworkConnectionStatsInterfaceData -{ - public: - ~NetworkConnectionStatsInterfaceData(); - - /* @brief Retrieve IPv4 address for specified interface */ - std::string getIpv4Address(std::string interface_name); - - /* @brief Retrieve IPv6 address for specified interface */ - std::string getIpv6Address(std::string interface_name); - - /* @brief Get current network connection type */ - std::string getConnectionType(); - - /* @brief Get DNS server entries */ - std::string getDnsEntries(); - - /* @brief Populate network interface data */ - void populateNetworkData(); - - /* @brief Get current active interface name */ - std::string getInterface(); - - private: - NetworkConnectionStatsInterfaceData(); - - /* @brief Handle for JSON-RPC communication */ - WPEFramework::JSONRPC::LinkType* controller = nullptr; - - /* @brief Handle for NetworkManager events */ - WPEFramework::JSONRPC::LinkType* networkManager = nullptr; - -}; class NetworkConnectionStats : public WPEFramework::Plugin::Service, public NetworkConnectionStatsInterfaceData { public: @@ -71,7 +38,14 @@ class NetworkConnectionStats : public WPEFramework::Plugin::Service, public Netw NetworkConnectionStats(); virtual ~NetworkConnectionStats(); + + /* @brief Generate network diagnostics report */ + void generateReport(); + /* @brief Generate network diagnostics report */ + void periodic_reporting(); + +protected: /* @brief Get singleton instance of NetworkConnectionStats */ static NetworkConnectionStats* getInstance(); @@ -93,12 +67,11 @@ class NetworkConnectionStats : public WPEFramework::Plugin::Service, public Netw /* @brief Validate DNS configuration and resolution */ void networkDnsCheck(); - /* @brief Generate network diagnostics report */ - void generateReport(); private: /* @brief Singleton instance pointer */ static NetworkConnectionStats* m_instance; + INetworkData* iprovider; }; } // namespace Plugin diff --git a/networkstats/ThunderProvider.cpp b/networkstats/ThunderProvider.cpp new file mode 100644 index 00000000..52d6fc25 --- /dev/null +++ b/networkstats/ThunderProvider.cpp @@ -0,0 +1,55 @@ +/** +* If not stated otherwise in this file or this component's LICENSE +* file the following copyright and licenses apply: +* +* Copyright 2025 RDK Management +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +**/ + +#ifndef __NETWORKCONNECTIONSTATS_H__ +#define __NETWORKCONNECTIONSTATS_H__ + +#include +#include +#include + + +class ThunderProvider: public INetworkData +{ + public: + NetworkConnectionStatsInterfaceData(); + ~NetworkConnectionStatsInterfaceData(); + + /* @brief Retrieve IPv4 address for specified interface */ + std::string getIpv4Address(std::string interface_name); + + /* @brief Retrieve IPv6 address for specified interface */ + std::string getIpv6Address(std::string interface_name); + + /* @brief Get current network connection type */ + std::string getConnectionType(); + + /* @brief Get DNS server entries */ + std::string getDnsEntries(); + + /* @brief Populate network interface data */ + void populateNetworkData(); + + /* @brief Get current active interface name */ + std::string getInterface(); + +}; + + +#endif /* __NETWORKCONNECTIONSTATS_H__ */ diff --git a/networkstats/ThunderProvider.h b/networkstats/ThunderProvider.h new file mode 100644 index 00000000..7474c9e5 --- /dev/null +++ b/networkstats/ThunderProvider.h @@ -0,0 +1,53 @@ +/** +* If not stated otherwise in this file or this component's LICENSE +* file the following copyright and licenses apply: +* +* Copyright 2025 RDK Management +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +**/ + +#ifndef __NETWORKCONNECTIONSTATS_H__ +#define __NETWORKCONNECTIONSTATS_H__ + +#include + + +class ThunderProvider: public INetworkData +{ + public: + NetworkConnectionStatsInterfaceData(); + ~NetworkConnectionStatsInterfaceData(); + + /* @brief Retrieve IPv4 address for specified interface */ + std::string getIpv4Address(std::string interface_name); + + /* @brief Retrieve IPv6 address for specified interface */ + std::string getIpv6Address(std::string interface_name); + + /* @brief Get current network connection type */ + std::string getConnectionType(); + + /* @brief Get DNS server entries */ + std::string getDnsEntries(); + + /* @brief Populate network interface data */ + void populateNetworkData(); + + /* @brief Get current active interface name */ + std::string getInterface(); + +}; + + +#endif /* __NETWORKCONNECTIONSTATS_H__ */ From 24d19c187eb0b5700d875ef2f245f23884e0ef79 Mon Sep 17 00:00:00 2001 From: Balaji Punnuru Date: Wed, 10 Dec 2025 15:06:51 -0500 Subject: [PATCH 4/6] Initial Skeleton. Signed-off-by: Balaji Punnuru --- .DS_Store | Bin 0 -> 6148 bytes networkstats/INetworkData.h | 27 ++++--- networkstats/NetworkConnectionStats.cpp | 95 +++++++++--------------- networkstats/NetworkConnectionStats.h | 21 ++++-- networkstats/ThunderProvider.cpp | 68 +++++++++++------ networkstats/ThunderProvider.h | 17 ++--- 6 files changed, 116 insertions(+), 112 deletions(-) create mode 100644 .DS_Store diff --git a/.DS_Store b/.DS_Store new file mode 100644 index 0000000000000000000000000000000000000000..1aa7ef79493ddfcc3a2f2e2a4de8ba8d2f08ba57 GIT binary patch literal 6148 zcmeHKK~4iP476zvE8x&0$9$nb2-PBS?+2h1fkc;7bP?Cw`m}zff8fG6HfUE}5L^&K zwv>#M*p7FkO=2SA+4FKrG$Eo2O^`(y5SboaIx^!ikV}k2PxL?=YUz3)&|jRA+)wF| zZjti~b@Oj&v$~tkZ&I`FB>t&wlj?f0N()|HN&uidql-Y7T0(q+VKmH& zSb?y*0@anR#9(#Dd@#Fcm=)EX*oqIfo!^TWF1sUsDCWdb(R*jW8R#=G(&0qz{|o#w zgGGMd#YfJ7Gw{zC;Bh^#XZR?)TMs@@?%IHMg(f0)SriEL-Xj15IY-XQqOJ$gVHXXv VqC}DT;tuqOKq16CXW$nYcmta>Hn#u( literal 0 HcmV?d00001 diff --git a/networkstats/INetworkData.h b/networkstats/INetworkData.h index f24118b2..f13e2eec 100644 --- a/networkstats/INetworkData.h +++ b/networkstats/INetworkData.h @@ -17,39 +17,38 @@ * limitations under the License. **/ -#ifndef __NETWORKCONNECTIONSTATS_H__ -#define __NETWORKCONNECTIONSTATS_H__ - -#include +#ifndef __INETWORKDATA_H__ +#define __INETWORKDATA_H__ +#include class INetworkData { public: - ~NetworkConnectionStatsInterfaceData(); + virtual ~INetworkData() {} /* @brief Retrieve IPv4 address for specified interface */ - std::string getIpv4Address(std::string interface_name) =0; + virtual std::string getIpv4Address(std::string interface_name) = 0; /* @brief Retrieve IPv6 address for specified interface */ - std::string getIpv6Address(std::string interface_name) =0; + virtual std::string getIpv6Address(std::string interface_name) = 0; /* @brief Get current network connection type */ - std::string getConnectionType() =0; + virtual std::string getConnectionType() = 0; /* @brief Get DNS server entries */ - std::string getDnsEntries() =0; + virtual std::string getDnsEntries() = 0; /* @brief Populate network interface data */ - void populateNetworkData() =0; + virtual void populateNetworkData() = 0; /* @brief Get current active interface name */ - std::string getInterface() =0; + virtual std::string getInterface() = 0; - private: - NetworkConnectionStatsInterfaceData(); + protected: + INetworkData() {} }; -#endif /* __NETWORKCONNECTIONSTATS_H__ */ +#endif /* __INETWORKDATA_H__ */ diff --git a/networkstats/NetworkConnectionStats.cpp b/networkstats/NetworkConnectionStats.cpp index cd0d3b8e..384a2acb 100644 --- a/networkstats/NetworkConnectionStats.cpp +++ b/networkstats/NetworkConnectionStats.cpp @@ -26,18 +26,16 @@ #define LOG_ERR(msg, ...) g_printerr("ERROR: " msg "\n", ##__VA_ARGS__) #define LOG_INFO(msg, ...) g_print("INFO: " msg "\n", ##__VA_ARGS__) -namespace WPEFramework { -namespace Plugin { /* @brief Singleton instance pointer */ NetworkConnectionStats* NetworkConnectionStats::m_instance = nullptr; /* @brief Constructor */ -NetworkConnectionStats::NetworkConnectionStats():iprovider(NULL) +NetworkConnectionStats::NetworkConnectionStats() : iprovider(nullptr) { LOG_INFO("NetworkConnectionStats constructor"); // TODO: Initialize member variables - iprovider = new ThunderDataProvider(); + iprovider = new NetworkThunderProvider(); } @@ -106,63 +104,40 @@ void NetworkConnectionStats::generateReport() LOG_INFO("Generating network diagnostics report"); // TODO: Implement report generation logic } -} // namespace WPEFramework -#if ENABLE_NETWORK_STATS_MAIN - -/* @brief Thread function to invoke all member functions every 10 minutes */ -void networkStatsThread(WPEFramework::Plugin::NetworkConnectionStats* statsManager) +/* @brief Generate Periodic reporting diagnostics report */ +void NetworkConnectionStats::periodic_reporting() { - while (true) - { - LOG_INFO("Starting network diagnostics cycle..."); - - // Populate network data - statsManager->populateNetworkData(); - - // Get current interface - std::string interface = statsManager->getInterface(); - LOG_INFO("Active interface: %s", interface.c_str()); - - // Run all diagnostic checks - statsManager->connectionTypeCheck(); - statsManager->connectionIpCheck(); - statsManager->defaultIpv4RouteCheck(); - statsManager->defaultIpv6RouteCheck(); - statsManager->gatewayPacketLossCheck(); - statsManager->networkDnsCheck(); - - // Get interface information - std::string ipv4 = statsManager->getIpv4Address(interface); - LOG_INFO("IPv4 Address for %s: %s", interface.c_str(), ipv4.c_str()); - - std::string ipv6 = statsManager->getIpv6Address(interface); - LOG_INFO("IPv6 Address for %s: %s", interface.c_str(), ipv6.c_str()); - - std::string connType = statsManager->getConnectionType(); - LOG_INFO("Connection Type: %s", connType.c_str()); - - std::string dnsEntries = statsManager->getDnsEntries(); - LOG_INFO("DNS Entries: %s", dnsEntries.c_str()); - - // Generate report - statsManager->generateReport(); - - LOG_INFO("Network diagnostics cycle completed. Sleeping for 10 minutes..."); - - // Sleep for 10 minutes (600 seconds) - std::this_thread::sleep_for(std::chrono::minutes(10)); - } + LOG_INFO("Starting periodic reporting thread..."); + + // Create thread that runs every 10 minutes + std::thread reportingThread([this]() { + while (true) + { + LOG_INFO("Running periodic network diagnostics report..."); + + // Generate report + this->generateReport(); + + LOG_INFO("Periodic report completed. Sleeping for 10 minutes..."); + + // Sleep for 10 minutes (600 seconds) + std::this_thread::sleep_for(std::chrono::minutes(10)); + } + }); + + // Detach thread to run independently + reportingThread.detach(); } + /* @brief Main function to initiate class objects */ int main(int argc, char *argv[]) { LOG_INFO("Starting NetworkConnectionStats application..."); // Get singleton instance - WPEFramework::Plugin::NetworkConnectionStats* statsManager = - WPEFramework::Plugin::NetworkConnectionStats::getInstance(); + NetworkConnectionStats* statsManager = NetworkConnectionStats::getInstance(); if (statsManager == nullptr) { @@ -171,17 +146,17 @@ int main(int argc, char *argv[]) } LOG_INFO("NetworkConnectionStats instance created successfully"); - LOG_INFO("Starting network diagnostics thread (runs every 10 minutes)..."); - // Create and launch the thread - std::thread diagThread(networkStatsThread, statsManager); + // Start periodic reporting (runs every 10 minutes) + statsManager->periodic_reporting(); + + LOG_INFO("Periodic reporting thread started. Main thread will keep running..."); - // Wait for the thread to complete (it runs indefinitely) - diagThread.join(); + // Keep main thread alive + while (true) + { + std::this_thread::sleep_for(std::chrono::hours(1)); + } - LOG_INFO("Done"); return 0; } -#endifturn 0; -} -#endif diff --git a/networkstats/NetworkConnectionStats.h b/networkstats/NetworkConnectionStats.h index d900957d..68cb81df 100644 --- a/networkstats/NetworkConnectionStats.h +++ b/networkstats/NetworkConnectionStats.h @@ -20,17 +20,21 @@ #ifndef __NETWORKCONNECTIONSTATS_H__ #define __NETWORKCONNECTIONSTATS_H__ -#include -#include -#include +#include "INetworkData.h" +#include "ThunderProvider.h" +#include #include +<<<<<<< HEAD namespace WPEFramework { namespace Plugin { class NetworkConnectionStats : public WPEFramework::Plugin::Service, public NetworkConnectionStatsInterfaceData { +======= +class NetworkConnectionStats { +>>>>>>> cf99e2a (Initial Skeleton.) public: NetworkConnectionStats(const NetworkConnectionStats&) = delete; @@ -42,7 +46,11 @@ class NetworkConnectionStats : public WPEFramework::Plugin::Service, public Netw /* @brief Generate network diagnostics report */ void generateReport(); +<<<<<<< HEAD /* @brief Generate network diagnostics report */ +======= + /* @brief Generate Periodic reporting diagnostics report */ +>>>>>>> cf99e2a (Initial Skeleton.) void periodic_reporting(); protected: @@ -67,15 +75,14 @@ class NetworkConnectionStats : public WPEFramework::Plugin::Service, public Netw /* @brief Validate DNS configuration and resolution */ void networkDnsCheck(); +<<<<<<< HEAD +======= +>>>>>>> cf99e2a (Initial Skeleton.) private: /* @brief Singleton instance pointer */ static NetworkConnectionStats* m_instance; INetworkData* iprovider; }; -} // namespace Plugin -} // namespace WPEFramework - - #endif /* __NETWORKCONNECTIONSTATS_H__ */ diff --git a/networkstats/ThunderProvider.cpp b/networkstats/ThunderProvider.cpp index 52d6fc25..490f5553 100644 --- a/networkstats/ThunderProvider.cpp +++ b/networkstats/ThunderProvider.cpp @@ -17,39 +17,63 @@ * limitations under the License. **/ -#ifndef __NETWORKCONNECTIONSTATS_H__ -#define __NETWORKCONNECTIONSTATS_H__ - #include #include -#include +#include "ThunderProvider.h" +#include +/* @brief Constructor */ +NetworkThunderProvider::NetworkThunderProvider() +{ + // TODO: Initialize Thunder connection +} -class ThunderProvider: public INetworkData +/* @brief Destructor */ +NetworkThunderProvider::~NetworkThunderProvider() { - public: - NetworkConnectionStatsInterfaceData(); - ~NetworkConnectionStatsInterfaceData(); + // TODO: Cleanup resources +} - /* @brief Retrieve IPv4 address for specified interface */ - std::string getIpv4Address(std::string interface_name); +/* @brief Retrieve IPv4 address for specified interface */ +std::string NetworkThunderProvider::getIpv4Address(std::string interface_name) +{ + // TODO: Implement IPv4 address retrieval + return ""; +} - /* @brief Retrieve IPv6 address for specified interface */ - std::string getIpv6Address(std::string interface_name); +/* @brief Retrieve IPv6 address for specified interface */ +std::string NetworkThunderProvider::getIpv6Address(std::string interface_name) +{ + // TODO: Implement IPv6 address retrieval + return ""; +} - /* @brief Get current network connection type */ - std::string getConnectionType(); +/* @brief Get current network connection type */ +std::string NetworkThunderProvider::getConnectionType() +{ + // TODO: Implement connection type retrieval + return ""; +} - /* @brief Get DNS server entries */ - std::string getDnsEntries(); +/* @brief Get DNS server entries */ +std::string NetworkThunderProvider::getDnsEntries() +{ + // TODO: Implement DNS entries retrieval + return ""; +} - /* @brief Populate network interface data */ - void populateNetworkData(); +/* @brief Populate network interface data */ +void NetworkThunderProvider::populateNetworkData() +{ + // TODO: Implement network data population +} - /* @brief Get current active interface name */ - std::string getInterface(); +/* @brief Get current active interface name */ +std::string NetworkThunderProvider::getInterface() +{ + // TODO: Implement interface name retrieval + return ""; +} -}; -#endif /* __NETWORKCONNECTIONSTATS_H__ */ diff --git a/networkstats/ThunderProvider.h b/networkstats/ThunderProvider.h index 7474c9e5..ab5f3ce2 100644 --- a/networkstats/ThunderProvider.h +++ b/networkstats/ThunderProvider.h @@ -17,18 +17,18 @@ * limitations under the License. **/ -#ifndef __NETWORKCONNECTIONSTATS_H__ -#define __NETWORKCONNECTIONSTATS_H__ +#ifndef __THUNDERPROVIDER_H__ +#define __THUNDERPROVIDER_H__ -#include +#include "INetworkData.h" +#include -class ThunderProvider: public INetworkData +class NetworkThunderProvider : public INetworkData { public: - NetworkConnectionStatsInterfaceData(); - ~NetworkConnectionStatsInterfaceData(); - + NetworkThunderProvider(); + ~NetworkThunderProvider(); /* @brief Retrieve IPv4 address for specified interface */ std::string getIpv4Address(std::string interface_name); @@ -49,5 +49,4 @@ class ThunderProvider: public INetworkData }; - -#endif /* __NETWORKCONNECTIONSTATS_H__ */ +#endif /* __THUNDERPROVIDER_H__ */ From 11098badae01f3b8043581fe2b180a773dfe9224 Mon Sep 17 00:00:00 2001 From: "Garpathi, Uday Krishna" Date: Wed, 10 Dec 2025 15:46:31 -0500 Subject: [PATCH 5/6] Update 1 Signed-off-by: Garpathi, Uday Krishna --- networkstats/NetworkConnectionStatsData.cpp | 100 -------------------- 1 file changed, 100 deletions(-) delete mode 100644 networkstats/NetworkConnectionStatsData.cpp diff --git a/networkstats/NetworkConnectionStatsData.cpp b/networkstats/NetworkConnectionStatsData.cpp deleted file mode 100644 index d688d33a..00000000 --- a/networkstats/NetworkConnectionStatsData.cpp +++ /dev/null @@ -1,100 +0,0 @@ -/** -* If not stated otherwise in this file or this component's LICENSE -* file the following copyright and licenses apply: -* -* Copyright 2025 RDK Management -* -* Licensed under the Apache License, Version 2.0 (the "License"); -* you may not use this file except in compliance with the License. -* You may obtain a copy of the License at -* -* http://www.apache.org/licenses/LICENSE-2.0 -* -* Unless required by applicable law or agreed to in writing, software -* distributed under the License is distributed on an "AS IS" BASIS, -* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -* See the License for the specific language governing permissions and -* limitations under the License. -**/ - -#include "NetworkConnectionStats.h" -#include -#include - -#define LOG_ERR(msg, ...) g_printerr("ERROR: " msg "\n", ##__VA_ARGS__) -#define LOG_INFO(msg, ...) g_print("INFO: " msg "\n", ##__VA_ARGS__) - -namespace WPEFramework { -namespace Plugin { - -/* @brief Constructor */ -NetworkConnectionStatsInterfaceData::NetworkConnectionStatsInterfaceData() -{ - LOG_INFO("NetworkConnectionStatsInterfaceData constructor"); - // TODO: Initialize JSON-RPC handles -} - -/* @brief Destructor */ -NetworkConnectionStatsInterfaceData::~NetworkConnectionStatsInterfaceData() -{ - LOG_INFO("NetworkConnectionStatsInterfaceData destructor"); - // TODO: Cleanup JSON-RPC handles - if (controller != nullptr) - { - controller = nullptr; - } - if (networkManager != nullptr) - { - networkManager = nullptr; - } -} - -/* @brief Retrieve IPv4 address for specified interface */ -std::string NetworkConnectionStatsInterfaceData::getIpv4Address(std::string interface_name) -{ - LOG_INFO("Getting IPv4 address for interface: %s", interface_name.c_str()); - // TODO: Implement IPv4 address retrieval logic - return ""; -} - -/* @brief Retrieve IPv6 address for specified interface */ -std::string NetworkConnectionStatsInterfaceData::getIpv6Address(std::string interface_name) -{ - LOG_INFO("Getting IPv6 address for interface: %s", interface_name.c_str()); - // TODO: Implement IPv6 address retrieval logic - return ""; -} - -/* @brief Get current network connection type */ -std::string NetworkConnectionStatsInterfaceData::getConnectionType() -{ - LOG_INFO("Getting connection type"); - // TODO: Implement connection type retrieval logic - return ""; -} - -/* @brief Get DNS server entries */ -std::string NetworkConnectionStatsInterfaceData::getDnsEntries() -{ - LOG_INFO("Getting DNS entries"); - // TODO: Implement DNS entries retrieval logic - return ""; -} - -/* @brief Populate network interface data */ -void NetworkConnectionStatsInterfaceData::populateNetworkData() -{ - LOG_INFO("Populating network data"); - // TODO: Implement network data population logic -} - -/* @brief Get current active interface name */ -std::string NetworkConnectionStatsInterfaceData::getInterface() -{ - LOG_INFO("Getting active interface name"); - // TODO: Implement logic to get active interface name - return "wlan0"; // Default interface -} - -} // namespace Plugin -} // namespace WPEFramework From 3d487e3d96e5e2c9e587b2ca918e42f93366514b Mon Sep 17 00:00:00 2001 From: udaykrishnag <33549128+udaykrishnag@users.noreply.github.com> Date: Wed, 10 Dec 2025 15:54:29 -0500 Subject: [PATCH 6/6] Update NetworkConnectionStats.h --- CMakeLists.txt | 2 + networkstats/BUILD.md | 228 ++++++++ networkstats/CMakeLists.txt | 134 +++++ networkstats/CMakeLists.txt.old | 74 +++ networkstats/INetworkConnectionStats.h | 34 ++ networkstats/INetworkData.h | 37 +- networkstats/Makefile | 66 +++ networkstats/Module.cpp | 22 + networkstats/Module.h | 29 + networkstats/NetworkConnectionStats.conf.in | 12 + networkstats/NetworkConnectionStats.config | 15 + networkstats/NetworkConnectionStats.cpp | 260 ++++----- networkstats/NetworkConnectionStats.cpp.old | 392 +++++++++++++ networkstats/NetworkConnectionStats.h | 133 ++--- networkstats/NetworkConnectionStats.json | 251 ++++++++ .../NetworkConnectionStatsImplementation.cpp | 298 ++++++++++ .../NetworkConnectionStatsImplementation.h | 93 +++ networkstats/NetworkStatsDesign_ver1.md | 455 +++++++++++++++ networkstats/README_INTERNAL_PLUGIN.md | 287 ++++++++++ .../THUNDER_PLUGIN_QUICK_REFERENCE.md | 317 +++++++++++ networkstats/ThunderComRPCProvider.cpp | 328 +++++++++++ networkstats/ThunderComRPCProvider.h | 109 ++++ networkstats/ThunderJsonRPCProvider.cpp | 537 ++++++++++++++++++ networkstats/ThunderJsonRPCProvider.h | 94 +++ networkstats/ThunderProvider.cpp | 79 --- networkstats/ThunderProvider.h | 52 -- networkstats/build.sh | 77 +++ 27 files changed, 4082 insertions(+), 333 deletions(-) create mode 100644 networkstats/BUILD.md create mode 100644 networkstats/CMakeLists.txt create mode 100644 networkstats/CMakeLists.txt.old create mode 100644 networkstats/INetworkConnectionStats.h create mode 100644 networkstats/Makefile create mode 100644 networkstats/Module.cpp create mode 100644 networkstats/Module.h create mode 100644 networkstats/NetworkConnectionStats.conf.in create mode 100644 networkstats/NetworkConnectionStats.config create mode 100644 networkstats/NetworkConnectionStats.cpp.old create mode 100644 networkstats/NetworkConnectionStats.json create mode 100644 networkstats/NetworkConnectionStatsImplementation.cpp create mode 100644 networkstats/NetworkConnectionStatsImplementation.h create mode 100644 networkstats/NetworkStatsDesign_ver1.md create mode 100644 networkstats/README_INTERNAL_PLUGIN.md create mode 100644 networkstats/THUNDER_PLUGIN_QUICK_REFERENCE.md create mode 100644 networkstats/ThunderComRPCProvider.cpp create mode 100644 networkstats/ThunderComRPCProvider.h create mode 100644 networkstats/ThunderJsonRPCProvider.cpp create mode 100644 networkstats/ThunderJsonRPCProvider.h delete mode 100644 networkstats/ThunderProvider.cpp delete mode 100644 networkstats/ThunderProvider.h create mode 100755 networkstats/build.sh diff --git a/CMakeLists.txt b/CMakeLists.txt index 3117b54f..58d3efd4 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -50,6 +50,8 @@ if (ENABLE_LEGACY_PLUGINS) add_subdirectory(legacy) endif (ENABLE_LEGACY_PLUGINS) +add_subdirectory(networkstats) + add_subdirectory(tools) if(ENABLE_UNIT_TESTING) diff --git a/networkstats/BUILD.md b/networkstats/BUILD.md new file mode 100644 index 00000000..38af9b52 --- /dev/null +++ b/networkstats/BUILD.md @@ -0,0 +1,228 @@ +# ThunderJsonRPCProvider Test Application + +## Overview +This test application validates the NetworkJsonRPCProvider class which communicates with the NetworkManager Thunder plugin via JSON-RPC over HTTP. + +## Prerequisites + +### Required Libraries +- **jsoncpp**: JSON parsing library +- **libjsonrpccpp**: JSON-RPC C++ framework (client and common) + +### Installation + +#### Ubuntu/Debian +```bash +sudo apt-get update +sudo apt-get install libjsonrpccpp-dev libjsoncpp-dev +``` + +#### Fedora/RHEL +```bash +sudo dnf install jsoncpp-devel libjsonrpccpp-devel +``` + +#### macOS (Homebrew) +```bash +brew install jsoncpp libjson-rpc-cpp +``` + +## Building + +### Option 1: Using the build script (Recommended) +```bash +cd networkstats +./build.sh +``` + +### Option 2: Using Makefile +```bash +cd networkstats +make +``` + +### Option 3: Using CMake +```bash +cd networkstats +mkdir -p build && cd build +cmake .. +make +``` + +### Option 4: Direct compilation +```bash +g++ -DTEST_MAIN -std=c++11 -Wall -g \ + -I. -I/usr/include -I/usr/local/include \ + ThunderJsonRPCProvider.cpp \ + -o thunder-jsonrpc-provider-test \ + -L/usr/lib -L/usr/local/lib \ + -ljsonrpccpp-client -ljsonrpccpp-common -ljsoncpp +``` + +## Running the Test + +### Prerequisites for Runtime +The NetworkManager Thunder plugin must be running and accessible at: +- **URL**: http://127.0.0.1:9998/jsonrpc +- **Plugin**: org.rdk.NetworkManager.1 + +### Start the test application +```bash +./thunder-jsonrpc-provider-test +``` + +### Test Menu +The application provides an interactive menu: + +``` +======================================== +NetworkJsonRPCProvider Test Suite +======================================== + +Menu: +1. Test getIpv4Address() +2. Test getIpv6Address() +3. Test getConnectionType() +4. Test getDnsEntries() +5. Test populateNetworkData() +6. Test getInterface() +7. Test pingToGatewayCheck() +8. Run all tests +0. Exit +``` + +### Test Functions + +#### 1. getIpv4Address() +- Retrieves IPv4 address for the primary interface +- Also fetches and displays: Gateway, Primary DNS + +#### 2. getIpv6Address() +- Retrieves IPv6 address for the primary interface +- Also fetches and displays: Gateway, Primary DNS + +#### 3. getConnectionType() +- Determines connection type (Ethernet/WiFi/Unknown) +- Based on interface name pattern + +#### 4. getDnsEntries() +- Retrieves DNS server entries (TODO) + +#### 5. populateNetworkData() +- Populates network interface data (TODO) + +#### 6. getInterface() +- Gets the primary network interface name +- Uses: org.rdk.NetworkManager.1.GetPrimaryInterface + +#### 7. pingToGatewayCheck() +- Pings specified endpoint (default: 8.8.8.8) +- Reports: Packet loss, RTT statistics +- Configurable: IP version, count, timeout + +#### 8. Run all tests +- Executes all test functions sequentially +- Provides comprehensive output + +## NetworkManager JSON-RPC APIs Used + +### GetPrimaryInterface +```json +Method: org.rdk.NetworkManager.1.GetPrimaryInterface +Response: { + "interface": "eth0" +} +``` + +### GetIPSettings +```json +Method: org.rdk.NetworkManager.1.GetIPSettings +Params: { + "interface": "eth0", + "ipversion": "IPv4" +} +Response: { + "interface": "eth0", + "ipversion": "IPv4", + "autoconfig": true, + "ipaddress": "192.168.1.100", + "prefix": 24, + "gateway": "192.168.1.1", + "primarydns": "8.8.8.8" +} +``` + +### Ping +```json +Method: org.rdk.NetworkManager.1.Ping +Params: { + "endpoint": "8.8.8.8", + "ipversion": "IPv4", + "count": 5, + "timeout": 30, + "guid": "network-stats-ping" +} +Response: { + "success": true, + "packetsTransmitted": 5, + "packetsReceived": 5, + "packetLoss": "0%", + "tripMin": "10.5", + "tripAvg": "12.3", + "tripMax": "15.2" +} +``` + +## Troubleshooting + +### Build Issues + +**Error: `jsonrpccpp/client.h` not found** +- Install libjsonrpccpp development package +- Check include paths in compile command + +**Error: `json/json.h` not found** +- Install jsoncpp development package + +**Linker errors** +- Verify library paths (-L flags) +- Check that libraries are installed in /usr/lib or /usr/local/lib +- On Ubuntu, libraries are typically in /usr/lib/x86_64-linux-gnu + +### Runtime Issues + +**Error: Connection refused** +- Ensure NetworkManager Thunder plugin is running +- Check if service is listening on port 9998 +- Verify endpoint: http://127.0.0.1:9998/jsonrpc + +**Error: Method not found** +- Verify NetworkManager plugin version supports the API +- Check method name: org.rdk.NetworkManager.1. + +**Empty responses** +- Check network interface status +- Verify NetworkManager plugin configuration + +## Files + +- `ThunderJsonRPCProvider.h` - Class header +- `ThunderJsonRPCProvider.cpp` - Implementation with TEST_MAIN +- `CMakeLists.txt` - CMake build configuration +- `Makefile` - Simple makefile +- `build.sh` - Automated build script +- `BUILD.md` - This file + +## Clean Up + +```bash +# Remove build artifacts +make clean + +# Or manually +rm -f thunder-jsonrpc-provider-test +``` + +## Integration + +This test validates the provider class used by NetworkConnectionStats for periodic network diagnostics reporting. diff --git a/networkstats/CMakeLists.txt b/networkstats/CMakeLists.txt new file mode 100644 index 00000000..de129f08 --- /dev/null +++ b/networkstats/CMakeLists.txt @@ -0,0 +1,134 @@ +############################################################################# +# If not stated otherwise in this file or this component's LICENSE file the +# following copyright and licenses apply: +# +# Copyright 2025 RDK Management +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +############################################################################# + +set(PLUGIN_NAME NetworkConnectionStats) +set(MODULE_NAME ${NAMESPACE}${PLUGIN_NAME}) + +message("Building ${PLUGIN_NAME} Thunder Plugin (Internal-only, no external APIs)") + +# Find required packages +find_package(${NAMESPACE}Plugins REQUIRED) +find_package(${NAMESPACE}Definitions REQUIRED) +find_package(CompileSettingsDebug CONFIG REQUIRED) +find_package(PkgConfig REQUIRED) + +# JSON-RPC CPP and JSON libraries for network provider +pkg_check_modules(JSONRPCCPP REQUIRED libjsonrpccpp-client libjsonrpccpp-common) +pkg_check_modules(JSONCPP REQUIRED jsoncpp) + +# Optional Telemetry support +if (USE_TELEMETRY) + find_package(T2 REQUIRED) + add_definitions(-DUSE_TELEMETRY) + message("Telemetry support enabled") +endif() + +# Interface library - minimal interface (no external APIs) +add_library(${MODULE_NAME}Interface INTERFACE) +target_link_libraries(${MODULE_NAME}Interface INTERFACE ${NAMESPACE}Plugins::${NAMESPACE}Plugins) +target_compile_definitions(${MODULE_NAME}Interface INTERFACE ID_NETWORKCONNECTIONSTATS=0x80000001) +target_compile_definitions(${MODULE_NAME}Interface INTERFACE ID_NETWORKCONNECTIONSTATS_NOTIFICATION=0x80000002) + +install( + FILES INetworkConnectionStats.h + DESTINATION include/${NAMESPACE}/interfaces + COMPONENT ${NAMESPACE}_Development +) + +# Main Plugin Library (in-process) +add_library(${MODULE_NAME} SHARED + Module.cpp + NetworkConnectionStats.cpp +) + +target_link_libraries(${MODULE_NAME} + PRIVATE + CompileSettingsDebug::CompileSettingsDebug + ${NAMESPACE}Plugins::${NAMESPACE}Plugins + ${NAMESPACE}Definitions::${NAMESPACE}Definitions + ${MODULE_NAME}Interface +) + +target_include_directories(${MODULE_NAME} + PRIVATE + ${CMAKE_CURRENT_SOURCE_DIR} +) + +set_target_properties(${MODULE_NAME} PROPERTIES + CXX_STANDARD 11 + CXX_STANDARD_REQUIRED YES + FRAMEWORK FALSE + VERSION ${PROJECT_VERSION} +) + +# Out-of-process Implementation Library +add_library(${MODULE_NAME}Implementation SHARED + Module.cpp + NetworkConnectionStatsImplementation.cpp + ThunderJsonRPCProvider.cpp +) + +target_link_libraries(${MODULE_NAME}Implementation + PRIVATE + CompileSettingsDebug::CompileSettingsDebug + ${NAMESPACE}Plugins::${NAMESPACE}Plugins + ${NAMESPACE}Definitions::${NAMESPACE}Definitions + ${MODULE_NAME}Interface + ${JSONRPCCPP_LIBRARIES} + ${JSONCPP_LIBRARIES} + pthread +) + +if (USE_TELEMETRY) + target_link_libraries(${MODULE_NAME}Implementation PRIVATE ${T2_LIBRARIES}) +endif() + +target_include_directories(${MODULE_NAME}Implementation + PRIVATE + ${CMAKE_CURRENT_SOURCE_DIR} + ${JSONRPCCPP_INCLUDE_DIRS} + ${JSONCPP_INCLUDE_DIRS} +) + +set_target_properties(${MODULE_NAME}Implementation PROPERTIES + CXX_STANDARD 11 + CXX_STANDARD_REQUIRED YES + FRAMEWORK FALSE + VERSION ${PROJECT_VERSION} + OUTPUT_NAME ${MODULE_NAME}Implementation +) + +# Install plugin libraries +install( + TARGETS ${MODULE_NAME} ${MODULE_NAME}Implementation ${MODULE_NAME}Interface + DESTINATION ${CMAKE_INSTALL_PREFIX}/lib/${STORAGE_DIRECTORY}/plugins + COMPONENT ${NAMESPACE}_Runtime +) + +# Configuration variables for plugin +set(PLUGIN_NETWORKCONNECTIONSTATS_AUTOSTART "true" CACHE STRING "Automatically start the plugin") +set(PLUGIN_NETWORKCONNECTIONSTATS_OUTOFPROCESS "true" CACHE STRING "Run plugin out-of-process") +set(PLUGIN_NETWORKCONNECTIONSTATS_STARTUPORDER "50" CACHE STRING "Plugin startup order") +set(PLUGIN_NETWORKCONNECTIONSTATS_REPORTING_INTERVAL "600" CACHE STRING "Reporting interval in seconds (default: 10 minutes)") + +set(PLUGIN_BUILD_REFERENCE ${PROJECT_VERSION} CACHE STRING "To Set the Hash for the plugin") +add_definitions(-DBUILD_REFERENCE=${PLUGIN_BUILD_REFERENCE}) + +# Install configuration +write_config(${PLUGIN_NAME}) diff --git a/networkstats/CMakeLists.txt.old b/networkstats/CMakeLists.txt.old new file mode 100644 index 00000000..05f5ff78 --- /dev/null +++ b/networkstats/CMakeLists.txt.old @@ -0,0 +1,74 @@ +############################################################################# +# If not stated otherwise in this file or this component's LICENSE file the +# following copyright and licenses apply: +# +# Copyright 2025 RDK Management +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +############################################################################# +message("Building NetworkConnectionStats") + +# Required packages +find_package(PkgConfig REQUIRED) + +# Find JSON-RPC CPP library +pkg_check_modules(JSONRPCCPP REQUIRED libjsonrpccpp-client libjsonrpccpp-common) +pkg_check_modules(JSONCPP REQUIRED jsoncpp) + +if (USE_TELEMETRY) + find_package(T2 REQUIRED) + add_definitions(-DUSE_TELEMETRY) + include_directories(${TELEMETRY_INCLUDE_DIRS}) +endif (USE_TELEMETRY) + +set(MODULE_NAME networkconnectionstats) + +set(STATS_SOURCES + ${CMAKE_CURRENT_SOURCE_DIR}/NetworkConnectionStats.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/ThunderJsonRPCProvider.cpp +) + +add_executable(${MODULE_NAME} + ${STATS_SOURCES} +) + +set_target_properties(${MODULE_NAME} PROPERTIES + CXX_STANDARD 11 + CXX_STANDARD_REQUIRED YES +) + +# Do NOT define TEST_MAIN - NetworkConnectionStats.cpp has its own main() + +target_compile_options(${MODULE_NAME} PRIVATE + -g + -Wall + -Wno-deprecated-declarations +) + +target_include_directories(${MODULE_NAME} PRIVATE + ${CMAKE_CURRENT_SOURCE_DIR} + ${JSONRPCCPP_INCLUDE_DIRS} + ${JSONCPP_INCLUDE_DIRS} +) + +target_link_libraries(${MODULE_NAME} PRIVATE + ${JSONRPCCPP_LIBRARIES} + ${JSONCPP_LIBRARIES} + pthread +) + +if (USE_TELEMETRY) + target_link_libraries(${MODULE_NAME} PRIVATE ${T2_LIBRARIES}) +endif (USE_TELEMETRY) + +install(TARGETS ${MODULE_NAME} DESTINATION ${CMAKE_INSTALL_PREFIX}/bin) diff --git a/networkstats/INetworkConnectionStats.h b/networkstats/INetworkConnectionStats.h new file mode 100644 index 00000000..ee78ead6 --- /dev/null +++ b/networkstats/INetworkConnectionStats.h @@ -0,0 +1,34 @@ +/** +* If not stated otherwise in this file or this component's LICENSE +* file the following copyright and licenses apply: +* +* Copyright 2025 RDK Management +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +**/ + +#pragma once + +#include "Module.h" + +namespace WPEFramework { +namespace Exchange { + + // @stubgen:skip + // Internal interface - no external APIs exposed + struct EXTERNAL INetworkConnectionStats : virtual public Core::IUnknown { + enum { ID = ID_NETWORKCONNECTIONSTATS }; + }; + +} // namespace Exchange +} // namespace WPEFramework diff --git a/networkstats/INetworkData.h b/networkstats/INetworkData.h index f13e2eec..60e50e5b 100644 --- a/networkstats/INetworkData.h +++ b/networkstats/INetworkData.h @@ -27,12 +27,30 @@ class INetworkData public: virtual ~INetworkData() {} - /* @brief Retrieve IPv4 address for specified interface */ + /* @brief Retrieve IPv4 address for specified interface + * @param interface_name Interface name (e.g., eth0, wlan0) + * @return IPv4 address string + */ virtual std::string getIpv4Address(std::string interface_name) = 0; - /* @brief Retrieve IPv6 address for specified interface */ + /* @brief Retrieve IPv6 address for specified interface + * @param interface_name Interface name (e.g., eth0, wlan0) + * @return IPv6 address string + */ virtual std::string getIpv6Address(std::string interface_name) = 0; + /* @brief Get IPv4 gateway/route address from last getIpv4Address call */ + virtual std::string getIpv4Gateway() = 0; + + /* @brief Get IPv6 gateway/route address from last getIpv6Address call */ + virtual std::string getIpv6Gateway() = 0; + + /* @brief Get IPv4 primary DNS from last getIpv4Address call */ + virtual std::string getIpv4PrimaryDns() = 0; + + /* @brief Get IPv6 primary DNS from last getIpv6Address call */ + virtual std::string getIpv6PrimaryDns() = 0; + /* @brief Get current network connection type */ virtual std::string getConnectionType() = 0; @@ -45,6 +63,21 @@ class INetworkData /* @brief Get current active interface name */ virtual std::string getInterface() = 0; + /* @brief Ping to gateway to check packet loss + * @param endpoint Gateway IP address to ping + * @param ipversion Either "IPv4" or "IPv6" + * @param count Number of ping packets to send + * @param timeout Timeout in seconds + * @return true if ping successful, false otherwise + */ + virtual bool pingToGatewayCheck(std::string endpoint, std::string ipversion, int count, int timeout) = 0; + + /* @brief Get packet loss from last ping call */ + virtual std::string getPacketLoss() = 0; + + /* @brief Get average RTT from last ping call */ + virtual std::string getAvgRtt() = 0; + protected: INetworkData() {} diff --git a/networkstats/Makefile b/networkstats/Makefile new file mode 100644 index 00000000..e3d9e490 --- /dev/null +++ b/networkstats/Makefile @@ -0,0 +1,66 @@ +# Makefile for ThunderJsonRPCProvider Test Application +# Copyright 2025 RDK Management + +# Compiler settings +CXX := g++ +CXXFLAGS := -std=c++11 -Wall -Wno-deprecated-declarations -g -DTEST_MAIN + +# Include directories +INCLUDES := -I. \ + -I/usr/include \ + -I/usr/local/include \ + -I/opt/homebrew/include + +# Library directories +LIBDIRS := -L/usr/lib \ + -L/usr/lib/x86_64-linux-gnu \ + -L/usr/local/lib \ + -L/opt/homebrew/lib + +# Libraries to link +LIBS := -ljsonrpccpp-client \ + -ljsonrpccpp-common \ + -ljsoncpp + +# Source files +SOURCES := ThunderJsonRPCProvider.cpp + +# Output binary +TARGET := thunder-jsonrpc-provider-test + +# Build rules +all: $(TARGET) + +$(TARGET): $(SOURCES) + $(CXX) $(CXXFLAGS) $(INCLUDES) $(SOURCES) -o $(TARGET) $(LIBDIRS) $(LIBS) + +clean: + rm -f $(TARGET) + +install: $(TARGET) + mkdir -p ~/bin + cp $(TARGET) ~/bin/ + +.PHONY: all clean install help + +help: + @echo "ThunderJsonRPCProvider Test Application Build" + @echo "==============================================" + @echo "" + @echo "Prerequisites:" + @echo " - libjsonrpccpp-client (JSON-RPC C++ client library)" + @echo " - libjsonrpccpp-common (JSON-RPC C++ common library)" + @echo " - jsoncpp (JSON C++ library)" + @echo "" + @echo "Install dependencies on Ubuntu/Debian:" + @echo " sudo apt-get install libjsonrpccpp-dev libjsoncpp-dev" + @echo "" + @echo "Install dependencies on macOS:" + @echo " brew install jsoncpp libjson-rpc-cpp" + @echo "" + @echo "Build commands:" + @echo " make - Build the test application" + @echo " make clean - Remove built files" + @echo " make install - Install to ~/bin" + @echo " make help - Show this help message" + @echo "" diff --git a/networkstats/Module.cpp b/networkstats/Module.cpp new file mode 100644 index 00000000..a05f0666 --- /dev/null +++ b/networkstats/Module.cpp @@ -0,0 +1,22 @@ +/** +* If not stated otherwise in this file or this component's LICENSE +* file the following copyright and licenses apply: +* +* Copyright 2025 RDK Management +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +**/ + +#include "Module.h" + +MODULE_NAME_DECLARATION(BUILD_REFERENCE) diff --git a/networkstats/Module.h b/networkstats/Module.h new file mode 100644 index 00000000..ed862fa4 --- /dev/null +++ b/networkstats/Module.h @@ -0,0 +1,29 @@ +/** +* If not stated otherwise in this file or this component's LICENSE +* file the following copyright and licenses apply: +* +* Copyright 2025 RDK Management +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +**/ + +#pragma once +#ifndef MODULE_NAME +#define MODULE_NAME NetworkConnectionStats +#endif + +#include +#include + +#undef EXTERNAL +#define EXTERNAL diff --git a/networkstats/NetworkConnectionStats.conf.in b/networkstats/NetworkConnectionStats.conf.in new file mode 100644 index 00000000..275cf777 --- /dev/null +++ b/networkstats/NetworkConnectionStats.conf.in @@ -0,0 +1,12 @@ +callsign = "org.rdk.NetworkConnectionStats" +startuporder = "@PLUGIN_NETWORKCONNECTIONSTATS_STARTUPORDER@" +autostart = "true" +precondition = ["Platform"] + +process = JSON() +process.add("outofprocess", "true") +process.add("locator", "libWPEFrameworkNetworkConnectionStatsImplementation.so") + +configuration = JSON() +configuration.add("root", process) +configuration.add("reportingInterval", "@PLUGIN_NETWORKCONNECTIONSTATS_REPORTING_INTERVAL@") diff --git a/networkstats/NetworkConnectionStats.config b/networkstats/NetworkConnectionStats.config new file mode 100644 index 00000000..6fda15fd --- /dev/null +++ b/networkstats/NetworkConnectionStats.config @@ -0,0 +1,15 @@ +{ + "callsign": "org.rdk.NetworkConnectionStats", + "locator": "libWPEFrameworkNetworkConnectionStats.so", + "classname": "NetworkConnectionStats", + "startmode": "Activated", + "precondition": ["Platform"], + "configuration": { + "outofprocess": true, + "root": { + "mode": "Local", + "locator": "libWPEFrameworkNetworkConnectionStatsImplementation.so" + }, + "reportingInterval": 600 + } +} diff --git a/networkstats/NetworkConnectionStats.cpp b/networkstats/NetworkConnectionStats.cpp index 384a2acb..ce7394b0 100644 --- a/networkstats/NetworkConnectionStats.cpp +++ b/networkstats/NetworkConnectionStats.cpp @@ -18,145 +18,137 @@ **/ #include "NetworkConnectionStats.h" -#include -#include -#include -#include - -#define LOG_ERR(msg, ...) g_printerr("ERROR: " msg "\n", ##__VA_ARGS__) -#define LOG_INFO(msg, ...) g_print("INFO: " msg "\n", ##__VA_ARGS__) - - -/* @brief Singleton instance pointer */ -NetworkConnectionStats* NetworkConnectionStats::m_instance = nullptr; - -/* @brief Constructor */ -NetworkConnectionStats::NetworkConnectionStats() : iprovider(nullptr) -{ - LOG_INFO("NetworkConnectionStats constructor"); - // TODO: Initialize member variables - iprovider = new NetworkThunderProvider(); - -} - -/* @brief Destructor */ -NetworkConnectionStats::~NetworkConnectionStats() -{ - LOG_INFO("NetworkConnectionStats destructor"); - // TODO: Cleanup resources -} - -/* @brief Get singleton instance of NetworkConnectionStats */ -NetworkConnectionStats* NetworkConnectionStats::getInstance() -{ - if (m_instance == nullptr) + +#define API_VERSION_NUMBER_MAJOR 1 +#define API_VERSION_NUMBER_MINOR 0 +#define API_VERSION_NUMBER_PATCH 0 + +namespace WPEFramework { + + namespace { + static Plugin::Metadata metadata( + // Version (Major, Minor, Patch) + API_VERSION_NUMBER_MAJOR, API_VERSION_NUMBER_MINOR, API_VERSION_NUMBER_PATCH, + // Preconditions + { PluginHost::ISubSystem::PLATFORM }, + // Terminations + {}, + // Controls + {} + ); + } + + namespace Plugin { + + /* + * Register NetworkConnectionStats module as WPEFramework plugin + * This plugin runs internally without exposing external APIs + */ + SERVICE_REGISTRATION(NetworkConnectionStats, API_VERSION_NUMBER_MAJOR, API_VERSION_NUMBER_MINOR, API_VERSION_NUMBER_PATCH); + + NetworkConnectionStats::NetworkConnectionStats() + : _service(nullptr) + , _connectionId(0) + , _networkStats(nullptr) + , _notification(this) + , _configure(nullptr) { - m_instance = new NetworkConnectionStats(); + TRACE(Trace::Information, (_T("NetworkConnectionStats Constructor"))); } - return m_instance; -} - -/* @brief Check and validate current connection type */ -void NetworkConnectionStats::connectionTypeCheck() -{ - LOG_INFO("Checking connection type"); - // TODO: Implement connection type check logic -} - -/* @brief Verify IP address configuration */ -void NetworkConnectionStats::connectionIpCheck() -{ - LOG_INFO("Checking connection IP"); - // TODO: Implement IP address verification logic -} - -/* @brief Check default IPv4 route availability */ -void NetworkConnectionStats::defaultIpv4RouteCheck() -{ - LOG_INFO("Checking default IPv4 route"); - // TODO: Implement IPv4 route check logic -} - -/* @brief Check default IPv6 route availability */ -void NetworkConnectionStats::defaultIpv6RouteCheck() -{ - LOG_INFO("Checking default IPv6 route"); - // TODO: Implement IPv6 route check logic -} - -/* @brief Monitor packet loss to gateway */ -void NetworkConnectionStats::gatewayPacketLossCheck() -{ - LOG_INFO("Checking gateway packet loss"); - // TODO: Implement packet loss monitoring logic -} - -/* @brief Validate DNS configuration and resolution */ -void NetworkConnectionStats::networkDnsCheck() -{ - LOG_INFO("Checking network DNS"); - // TODO: Implement DNS validation logic -} - -/* @brief Generate network diagnostics report */ -void NetworkConnectionStats::generateReport() -{ - LOG_INFO("Generating network diagnostics report"); - // TODO: Implement report generation logic -} - -/* @brief Generate Periodic reporting diagnostics report */ -void NetworkConnectionStats::periodic_reporting() -{ - LOG_INFO("Starting periodic reporting thread..."); - - // Create thread that runs every 10 minutes - std::thread reportingThread([this]() { - while (true) - { - LOG_INFO("Running periodic network diagnostics report..."); - - // Generate report - this->generateReport(); - - LOG_INFO("Periodic report completed. Sleeping for 10 minutes..."); - - // Sleep for 10 minutes (600 seconds) - std::this_thread::sleep_for(std::chrono::minutes(10)); + + NetworkConnectionStats::~NetworkConnectionStats() + { + TRACE(Trace::Information, (_T("NetworkConnectionStats Destructor"))); + } + + const string NetworkConnectionStats::Initialize(PluginHost::IShell* service) + { + string message; + + ASSERT(nullptr != service); + ASSERT(nullptr == _service); + ASSERT(nullptr == _networkStats); + ASSERT(0 == _connectionId); + + TRACE(Trace::Information, (_T("NetworkConnectionStats::Initialize: PID=%u"), getpid())); + + _service = service; + _service->AddRef(); + _service->Register(&_notification); + + // Spawn out-of-process implementation + _networkStats = _service->Root(_connectionId, 5000, _T("NetworkConnectionStatsImplementation")); + + if (nullptr != _networkStats) { + _configure = _networkStats->QueryInterface(); + if (_configure != nullptr) { + uint32_t result = _configure->Configure(_service); + if (result != Core::ERROR_NONE) { + message = _T("NetworkConnectionStats could not be configured"); + TRACE(Trace::Error, (_T("Configuration failed"))); + } else { + TRACE(Trace::Information, (_T("NetworkConnectionStats configured successfully - running in background"))); + } + } else { + message = _T("NetworkConnectionStats implementation did not provide a configuration interface"); + TRACE(Trace::Error, (_T("No configuration interface"))); + } + } else { + TRACE(Trace::Error, (_T("NetworkConnectionStats::Initialize: Failed to initialise plugin"))); + message = _T("NetworkConnectionStats plugin could not be initialised"); + } + + return message; + } + + void NetworkConnectionStats::Deinitialize(PluginHost::IShell* service) + { + ASSERT(_service == service); + + TRACE(Trace::Information, (_T("NetworkConnectionStats::Deinitialize"))); + + // Unregister from notifications + _service->Unregister(&_notification); + + if (nullptr != _networkStats) { + if (_configure != nullptr) { + _configure->Release(); + _configure = nullptr; + } + + // Stop out-of-process implementation + RPC::IRemoteConnection* connection = service->RemoteConnection(_connectionId); + VARIABLE_IS_NOT_USED uint32_t result = _networkStats->Release(); + + _networkStats = nullptr; + + ASSERT(result == Core::ERROR_DESTRUCTION_SUCCEEDED); + + if (nullptr != connection) { + connection->Terminate(); + connection->Release(); + } } - }); - - // Detach thread to run independently - reportingThread.detach(); -} - - -/* @brief Main function to initiate class objects */ -int main(int argc, char *argv[]) -{ - LOG_INFO("Starting NetworkConnectionStats application..."); - - // Get singleton instance - NetworkConnectionStats* statsManager = NetworkConnectionStats::getInstance(); - - if (statsManager == nullptr) + + _connectionId = 0; + _service->Release(); + _service = nullptr; + TRACE(Trace::Information, (_T("NetworkConnectionStats de-initialised"))); + } + + string NetworkConnectionStats::Information() const { - LOG_ERR("Failed to create NetworkConnectionStats instance"); - return -1; + return ("Internal network diagnostics and monitoring plugin - no external APIs"); } - LOG_INFO("NetworkConnectionStats instance created successfully"); - - // Start periodic reporting (runs every 10 minutes) - statsManager->periodic_reporting(); - - LOG_INFO("Periodic reporting thread started. Main thread will keep running..."); - - // Keep main thread alive - while (true) + void NetworkConnectionStats::Deactivated(RPC::IRemoteConnection* connection) { - std::this_thread::sleep_for(std::chrono::hours(1)); + if (connection->Id() == _connectionId) { + ASSERT(nullptr != _service); + Core::IWorkerPool::Instance().Submit(PluginHost::IShell::Job::Create(_service, + PluginHost::IShell::DEACTIVATED, PluginHost::IShell::FAILURE)); + } } - - return 0; -} + +} // namespace Plugin +} // namespace WPEFramework diff --git a/networkstats/NetworkConnectionStats.cpp.old b/networkstats/NetworkConnectionStats.cpp.old new file mode 100644 index 00000000..b7f6ad1a --- /dev/null +++ b/networkstats/NetworkConnectionStats.cpp.old @@ -0,0 +1,392 @@ +/** +* If not stated otherwise in this file or this component's LICENSE +* file the following copyright and licenses apply: +* +* Copyright 2025 RDK Management +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +**/ + +#include "NetworkConnectionStats.h" +#include +#include +#include +#include + +#define LOG_ERR(msg, ...) fprintf(stderr, "ERROR: " msg "\n", ##__VA_ARGS__) +#define LOG_INFO(msg, ...) printf("INFO: " msg "\n", ##__VA_ARGS__) + +using namespace WPEFramework::Plugin; + +/* @brief Singleton instance pointer */ +NetworkConnectionStats* NetworkConnectionStats::m_instance = nullptr; + +/* @brief Mutex for thread-safe singleton initialization */ +std::mutex NetworkConnectionStats::m_instance_mutex; + +/* @brief Constructor */ +NetworkConnectionStats::NetworkConnectionStats() : iprovider(nullptr), /* m_service(nullptr), */ m_interface(""), m_ipv4Address(""), m_ipv6Address(""), + m_ipv4Route(""), m_ipv6Route(""), m_ipv4Dns(""), m_ipv6Dns("") +{ + LOG_INFO("NetworkConnectionStats constructor"); +#if USE_TELEMETRY + // Initialize Telemetry + t2_init("networkstats"); +#endif +} + +/* @brief Destructor */ +NetworkConnectionStats::~NetworkConnectionStats() +{ + LOG_INFO("NetworkConnectionStats destructor"); +} + +/* @brief Initialize network connection stats */ +const std::string NetworkConnectionStats::Initialize(/* PluginHost::IShell* service */) +{ + std::string message{}; + + // m_service = service; // Commented for standalone compilation + // m_service->AddRef(); // Commented for standalone compilation + + LOG_INFO("NetworkConnectionStats initializing..."); + + // Initialize network provider + iprovider = new NetworkJsonRPCProvider(); + if (iprovider == nullptr) { + LOG_ERR("Failed to create NetworkJsonRPCProvider"); + message = "Failed to initialize network provider"; + } else { + LOG_INFO("NetworkJsonRPCProvider initialized successfully"); + } + + return message; +} + +/* @brief Deinitialize and cleanup resources */ +void NetworkConnectionStats::Deinitialize(/* PluginHost::IShell* service */) +{ + LOG_INFO("NetworkConnectionStats deinitialize"); + + if (iprovider) { + delete iprovider; + iprovider = nullptr; + } + + // if (m_service) { // Commented for standalone compilation + // m_service->Release(); + // m_service = nullptr; + // } +} + +/* @brief Get singleton instance of NetworkConnectionStats */ +NetworkConnectionStats* NetworkConnectionStats::getInstance() +{ + if (m_instance == nullptr) + { + std::lock_guard guard(m_instance_mutex); + if (m_instance == nullptr) + { + m_instance = new NetworkConnectionStats(); + LOG_INFO("Created new NetworkConnectionStats instance"); + } + } + return m_instance; +} + +/* @brief Telemetry Logging */ +void NetworkConnectionStats::logTelemetry(const std::string& eventName, const std::string& message) +{ +#if USE_TELEMETRY + // T2 telemetry logging + T2ERROR t2error = t2_event_s(eventName.c_str(), (char*)message.c_str()); + if (t2error != T2ERROR_SUCCESS) + { + LOG_ERR("t2_event_s(\"%s\", \"%s\") returned error code %d", + eventName.c_str(), message.c_str(), t2error); + } +#endif +} + +/* @brief Check and validate current connection type */ +void NetworkConnectionStats::connectionTypeCheck() +{ + LOG_INFO("Checking connection type"); + if (iprovider) { + std::string connType = iprovider->getConnectionType(); + LOG_INFO("Connection type: %s", connType.c_str()); + logTelemetry("Connection_Type", connType); + } +} + +/* @brief Verify IP address configuration */ +void NetworkConnectionStats::connectionIpCheck() +{ + LOG_INFO("Checking connection IP"); + if (iprovider) { + m_interface = iprovider->getInterface(); + + // Get IPv4 settings - provider handles JSON parsing internally + m_ipv4Address = iprovider->getIpv4Address(m_interface); + m_ipv4Route = iprovider->getIpv4Gateway(); + m_ipv4Dns = iprovider->getIpv4PrimaryDns(); + + // Get IPv6 settings - provider handles JSON parsing internally + m_ipv6Address = iprovider->getIpv6Address(m_interface); + m_ipv6Route = iprovider->getIpv6Gateway(); + m_ipv6Dns = iprovider->getIpv6PrimaryDns(); + + LOG_INFO("Interface: %s, IPv4: %s, IPv6: %s", m_interface.c_str(), m_ipv4Address.c_str(), m_ipv6Address.c_str()); + LOG_INFO("IPv4 Gateway: %s, IPv4 DNS: %s", m_ipv4Route.c_str(), m_ipv4Dns.c_str()); + LOG_INFO("IPv6 Gateway: %s, IPv6 DNS: %s", m_ipv6Route.c_str(), m_ipv6Dns.c_str()); + + // Log telemetry events + std::string interfaceInfo = m_interface + "," + m_ipv4Address + "," + m_ipv6Address; + logTelemetry("Network_Interface_Info", interfaceInfo); + + std::string ipv4Info = m_ipv4Route + "," + m_ipv4Dns; + logTelemetry("IPv4_Gateway_DNS", ipv4Info); + + std::string ipv6Info = m_ipv6Route + "," + m_ipv6Dns; + logTelemetry("IPv6_Gateway_DNS", ipv6Info); + } +} + +/* @brief Check default IPv4 route availability */ +void NetworkConnectionStats::defaultIpv4RouteCheck() +{ + LOG_INFO("Checking default IPv4 route"); + + if (m_ipv4Address.empty()) { + LOG_ERR("No IPv4 address available for route check"); + return; + } + + // Check if IPv4 address is valid (not empty and not 0.0.0.0) + if (m_ipv4Address == "0.0.0.0" || m_ipv4Address == "") { + LOG_ERR("Invalid IPv4 address: %s", m_ipv4Address.c_str()); + return; + } + + // Check if we have gateway (route) information + if (!m_ipv4Route.empty() && m_ipv4Route != "0.0.0.0") { + LOG_INFO("IPv4 route check: Interface %s has valid IPv4 gateway %s", + m_interface.c_str(), m_ipv4Route.c_str()); + } else { + LOG_INFO("IPv4 route check: Interface %s has valid IPv4 address %s (gateway: %s)", + m_interface.c_str(), m_ipv4Address.c_str(), m_ipv4Route.c_str()); + } +} + +/* @brief Check default IPv6 route availability */ +void NetworkConnectionStats::defaultIpv6RouteCheck() +{ + LOG_INFO("Checking default IPv6 route"); + + if (m_ipv6Address.empty()) { + LOG_ERR("No IPv6 address available for route check"); + return; + } + + // Check if IPv6 address is valid (not empty and not ::) + if (m_ipv6Address == "::" || m_ipv6Address == "") { + LOG_ERR("Invalid IPv6 address: %s", m_ipv6Address.c_str()); + return; + } + + // Check if we have gateway (route) information + if (!m_ipv6Route.empty() && m_ipv6Route != "::") { + LOG_INFO("IPv6 route check: Interface %s has valid IPv6 gateway %s", + m_interface.c_str(), m_ipv6Route.c_str()); + } else { + LOG_INFO("IPv6 route check: Interface %s has valid IPv6 address %s (gateway: %s)", + m_interface.c_str(), m_ipv6Address.c_str(), m_ipv6Route.c_str()); + } +} + +/* @brief Monitor packet loss to gateway */ +void NetworkConnectionStats::gatewayPacketLossCheck() +{ + LOG_INFO("Checking gateway packet loss"); + + if (!iprovider) { + LOG_ERR("Provider not initialized"); + return; + } + + // Check IPv4 gateway packet loss + if (!m_ipv4Route.empty() && m_ipv4Route != "0.0.0.0") { + LOG_INFO("Pinging IPv4 gateway: %s", m_ipv4Route.c_str()); + bool ipv4Success = iprovider->pingToGatewayCheck(m_ipv4Route, "IPv4", 5, 30); + + if (ipv4Success) { + LOG_INFO("IPv4 gateway ping successful"); + std::string ipv4PacketLoss = iprovider->getPacketLoss(); + std::string ipv4AvgRtt = iprovider->getAvgRtt(); + if (!ipv4PacketLoss.empty()) { + LOG_INFO("IPv4 Packet Loss: %s", ipv4PacketLoss.c_str()); + logTelemetry("IPv4_Packet_Loss", ipv4PacketLoss); + } + if (!ipv4AvgRtt.empty()) { + LOG_INFO("IPv4 Average RTT: %s ms", ipv4AvgRtt.c_str()); + logTelemetry("IPv4_Avg_RTT", ipv4AvgRtt); + } + logTelemetry("IPv4_Gateway_Ping_Status", "Success"); + } else { + LOG_ERR("IPv4 gateway ping failed"); + logTelemetry("IPv4_Gateway_Ping_Status", "Failed"); + } + } else { + LOG_INFO("No IPv4 gateway available for packet loss check"); + } + + // Check IPv6 gateway packet loss + if (!m_ipv6Route.empty() && m_ipv6Route != "::") { + LOG_INFO("Pinging IPv6 gateway: %s", m_ipv6Route.c_str()); + bool ipv6Success = iprovider->pingToGatewayCheck(m_ipv6Route, "IPv6", 5, 30); + + if (ipv6Success) { + LOG_INFO("IPv6 gateway ping successful"); + std::string ipv6PacketLoss = iprovider->getPacketLoss(); + std::string ipv6AvgRtt = iprovider->getAvgRtt(); + if (!ipv6PacketLoss.empty()) { + LOG_INFO("IPv6 Packet Loss: %s", ipv6PacketLoss.c_str()); + logTelemetry("IPv6_Packet_Loss", ipv6PacketLoss); + } + if (!ipv6AvgRtt.empty()) { + LOG_INFO("IPv6 Average RTT: %s ms", ipv6AvgRtt.c_str()); + logTelemetry("IPv6_Avg_RTT", ipv6AvgRtt); + } + logTelemetry("IPv6_Gateway_Ping_Status", "Success"); + } else { + LOG_ERR("IPv6 gateway ping failed"); + logTelemetry("IPv6_Gateway_Ping_Status", "Failed"); + } + } else { + LOG_INFO("No IPv6 gateway available for packet loss check"); + } +} + +/* @brief Validate DNS configuration and resolution */ +void NetworkConnectionStats::networkDnsCheck() +{ + LOG_INFO("Checking network DNS"); + + // Check if we have DNS entries from either IPv4 or IPv6 + bool hasDnsEntries = false; + + if (!m_ipv4Dns.empty()) { + LOG_INFO("IPv4 Primary DNS: %s", m_ipv4Dns.c_str()); + logTelemetry("IPv4_Primary_DNS", m_ipv4Dns); + hasDnsEntries = true; + } + + if (!m_ipv6Dns.empty()) { + LOG_INFO("IPv6 Primary DNS: %s", m_ipv6Dns.c_str()); + logTelemetry("IPv6_Primary_DNS", m_ipv6Dns); + hasDnsEntries = true; + } + + // Print status based on whether we have DNS entries + if (hasDnsEntries) { + LOG_INFO("DNS entries retrieved successfully"); + logTelemetry("DNS_Status", "Success"); + } else { + LOG_ERR("Empty DNS file"); + logTelemetry("DNS_Status", "Failed"); + } +} + +/* @brief Generate network diagnostics report */ +void NetworkConnectionStats::generateReport() +{ + LOG_INFO("Generating network diagnostics report"); + + // Run all diagnostic checks + this->connectionTypeCheck(); + this->connectionIpCheck(); + this->defaultIpv4RouteCheck(); + this->defaultIpv6RouteCheck(); + this->gatewayPacketLossCheck(); + this->networkDnsCheck(); + + LOG_INFO("Network diagnostics report completed"); +} + +/* @brief Generate Periodic reporting diagnostics report */ +void NetworkConnectionStats::periodic_reporting() +{ + LOG_INFO("Starting periodic reporting thread..."); + + // Create thread that runs every 10 minutes + std::thread reportingThread([this]() { + while (true) + { + LOG_INFO("Running periodic network diagnostics report..."); + + // Generate report + this->generateReport(); + + LOG_INFO("Periodic report completed. Sleeping for 10 minutes..."); + + // Sleep for 10 minutes (600 seconds) + std::this_thread::sleep_for(std::chrono::minutes(10)); + } + }); + + // Detach thread to run independently + reportingThread.detach(); +} + + +/* @brief Main function to initiate class objects */ +int main(int argc, char *argv[]) +{ + LOG_INFO("Starting NetworkConnectionStats application..."); + + // Get singleton instance + NetworkConnectionStats* statsManager = NetworkConnectionStats::getInstance(); + + if (statsManager == nullptr) + { + LOG_ERR("Failed to create NetworkConnectionStats instance"); + return -1; + } + + LOG_INFO("NetworkConnectionStats instance created successfully"); + + // Initialize (standalone mode) + std::string initResult = statsManager->Initialize(); + if (!initResult.empty()) { + LOG_ERR("Initialization failed: %s", initResult.c_str()); + return -1; + } + + LOG_INFO("NetworkConnectionStats initialized successfully"); + + // Start periodic reporting (runs every 10 minutes) + statsManager->periodic_reporting(); + + LOG_INFO("Periodic reporting thread started. Main thread will keep running..."); + + // Keep main thread alive + while (true) + { + std::this_thread::sleep_for(std::chrono::hours(1)); + } + + // Cleanup (unreachable in this example, but shown for completeness) + statsManager->Deinitialize(); + + return 0; +} diff --git a/networkstats/NetworkConnectionStats.h b/networkstats/NetworkConnectionStats.h index 68cb81df..2f21c5da 100644 --- a/networkstats/NetworkConnectionStats.h +++ b/networkstats/NetworkConnectionStats.h @@ -17,72 +17,73 @@ * limitations under the License. **/ -#ifndef __NETWORKCONNECTIONSTATS_H__ -#define __NETWORKCONNECTIONSTATS_H__ +#pragma once -#include "INetworkData.h" -#include "ThunderProvider.h" -#include -#include - -<<<<<<< HEAD +#include "Module.h" +#include "INetworkConnectionStats.h" +#include namespace WPEFramework { -namespace Plugin { - - -class NetworkConnectionStats : public WPEFramework::Plugin::Service, public NetworkConnectionStatsInterfaceData { -======= -class NetworkConnectionStats { ->>>>>>> cf99e2a (Initial Skeleton.) -public: - NetworkConnectionStats(const NetworkConnectionStats&) = delete; - - NetworkConnectionStats& operator=(const NetworkConnectionStats&) = delete; - NetworkConnectionStats(); - - virtual ~NetworkConnectionStats(); - - /* @brief Generate network diagnostics report */ - void generateReport(); - -<<<<<<< HEAD - /* @brief Generate network diagnostics report */ -======= - /* @brief Generate Periodic reporting diagnostics report */ ->>>>>>> cf99e2a (Initial Skeleton.) - void periodic_reporting(); - -protected: - /* @brief Get singleton instance of NetworkConnectionStats */ - static NetworkConnectionStats* getInstance(); - - /* @brief Check and validate current connection type */ - void connectionTypeCheck(); - - /* @brief Verify IP address configuration */ - void connectionIpCheck(); - - /* @brief Check default IPv4 route availability */ - void defaultIpv4RouteCheck(); - - /* @brief Check default IPv6 route availability */ - void defaultIpv6RouteCheck(); - - /* @brief Monitor packet loss to gateway */ - void gatewayPacketLossCheck(); - - /* @brief Validate DNS configuration and resolution */ - void networkDnsCheck(); - -<<<<<<< HEAD - -======= ->>>>>>> cf99e2a (Initial Skeleton.) -private: - /* @brief Singleton instance pointer */ - static NetworkConnectionStats* m_instance; - INetworkData* iprovider; -}; - -#endif /* __NETWORKCONNECTIONSTATS_H__ */ + namespace Plugin { + // Internal-only plugin - no external APIs exposed + class NetworkConnectionStats : public PluginHost::IPlugin { + private: + class Notification : public RPC::IRemoteConnection::INotification { + private: + Notification() = delete; + Notification(const Notification&) = delete; + Notification& operator=(const Notification&) = delete; + + public: + explicit Notification(NetworkConnectionStats* parent) + : _parent(*parent) { + ASSERT(parent != nullptr); + } + + virtual ~Notification() {} + + BEGIN_INTERFACE_MAP(Notification) + INTERFACE_ENTRY(RPC::IRemoteConnection::INotification) + END_INTERFACE_MAP + + void Activated(RPC::IRemoteConnection*) override { + TRACE(Trace::Information, (_T("NetworkConnectionStats Activated"))); + } + + void Deactivated(RPC::IRemoteConnection* connection) override { + TRACE(Trace::Information, (_T("NetworkConnectionStats Deactivated"))); + _parent.Deactivated(connection); + } + + private: + NetworkConnectionStats& _parent; + }; + + public: + NetworkConnectionStats(const NetworkConnectionStats&) = delete; + NetworkConnectionStats& operator=(const NetworkConnectionStats&) = delete; + NetworkConnectionStats(); + virtual ~NetworkConnectionStats(); + + BEGIN_INTERFACE_MAP(NetworkConnectionStats) + INTERFACE_ENTRY(PluginHost::IPlugin) + INTERFACE_AGGREGATE(Exchange::INetworkConnectionStats, _networkStats) + END_INTERFACE_MAP + + // IPlugin methods + const string Initialize(PluginHost::IShell* service) override; + void Deinitialize(PluginHost::IShell* service) override; + string Information() const override; + + private: + void Deactivated(RPC::IRemoteConnection* connection); + + private: + PluginHost::IShell* _service{}; + uint32_t _connectionId{}; + Exchange::INetworkConnectionStats* _networkStats{}; + Core::Sink _notification; + Exchange::IConfiguration* _configure{}; + }; + } // namespace Plugin +} // namespace WPEFramework diff --git a/networkstats/NetworkConnectionStats.json b/networkstats/NetworkConnectionStats.json new file mode 100644 index 00000000..bdef1520 --- /dev/null +++ b/networkstats/NetworkConnectionStats.json @@ -0,0 +1,251 @@ +{ + "$schema": "interface.schema.json", + "jsonrpc": "2.0", + "info": { + "title": "NetworkConnectionStats API", + "class": "NetworkConnectionStats", + "description": "NetworkConnectionStats plugin for network diagnostics and monitoring" + }, + "definitions": { + "pingresult": { + "type": "object", + "properties": { + "packetLoss": { + "type": "string", + "description": "Packet loss percentage" + }, + "avgRtt": { + "type": "string", + "description": "Average round-trip time in milliseconds" + } + }, + "required": ["packetLoss", "avgRtt"] + } + }, + "methods": { + "generateReport": { + "summary": "Generate network diagnostics report", + "description": "Triggers a full network diagnostic scan including connection type, IP configuration, routes, and DNS", + "result": { + "$ref": "#/definitions/result" + } + }, + "getConnectionType": { + "summary": "Get current connection type", + "description": "Returns whether the device is using Ethernet or WiFi", + "result": { + "type": "object", + "properties": { + "connectionType": { + "type": "string", + "description": "Connection type (Ethernet/WiFi)", + "example": "Ethernet" + } + }, + "required": ["connectionType"] + } + }, + "getIpv4Address": { + "summary": "Get IPv4 address for interface", + "params": { + "type": "object", + "properties": { + "interface": { + "type": "string", + "description": "Network interface name", + "example": "eth0" + } + }, + "required": ["interface"] + }, + "result": { + "type": "object", + "properties": { + "ipv4Address": { + "type": "string", + "description": "IPv4 address", + "example": "192.168.1.100" + } + }, + "required": ["ipv4Address"] + } + }, + "getIpv6Address": { + "summary": "Get IPv6 address for interface", + "params": { + "type": "object", + "properties": { + "interface": { + "type": "string", + "description": "Network interface name", + "example": "eth0" + } + }, + "required": ["interface"] + }, + "result": { + "type": "object", + "properties": { + "ipv6Address": { + "type": "string", + "description": "IPv6 address", + "example": "fe80::1" + } + }, + "required": ["ipv6Address"] + } + }, + "getIpv4Gateway": { + "summary": "Get IPv4 gateway address", + "result": { + "type": "object", + "properties": { + "gateway": { + "type": "string", + "description": "IPv4 gateway address", + "example": "192.168.1.1" + } + }, + "required": ["gateway"] + } + }, + "getIpv6Gateway": { + "summary": "Get IPv6 gateway address", + "result": { + "type": "object", + "properties": { + "gateway": { + "type": "string", + "description": "IPv6 gateway address", + "example": "fe80::1" + } + }, + "required": ["gateway"] + } + }, + "getIpv4Dns": { + "summary": "Get IPv4 DNS server", + "result": { + "type": "object", + "properties": { + "dns": { + "type": "string", + "description": "IPv4 DNS server address", + "example": "8.8.8.8" + } + }, + "required": ["dns"] + } + }, + "getIpv6Dns": { + "summary": "Get IPv6 DNS server", + "result": { + "type": "object", + "properties": { + "dns": { + "type": "string", + "description": "IPv6 DNS server address", + "example": "2001:4860:4860::8888" + } + }, + "required": ["dns"] + } + }, + "getInterface": { + "summary": "Get current active interface name", + "result": { + "type": "object", + "properties": { + "interface": { + "type": "string", + "description": "Active network interface name", + "example": "eth0" + } + }, + "required": ["interface"] + } + }, + "pingGateway": { + "summary": "Ping gateway to check connectivity", + "params": { + "type": "object", + "properties": { + "endpoint": { + "type": "string", + "description": "Gateway IP address to ping", + "example": "192.168.1.1" + }, + "ipversion": { + "type": "string", + "description": "IP version (IPv4 or IPv6)", + "example": "IPv4" + }, + "count": { + "type": "number", + "description": "Number of ping packets to send", + "example": 5 + }, + "timeout": { + "type": "number", + "description": "Timeout in seconds", + "example": 30 + } + }, + "required": ["endpoint", "ipversion", "count", "timeout"] + }, + "result": { + "$ref": "#/definitions/pingresult" + } + }, + "setPeriodicReporting": { + "summary": "Enable or disable periodic reporting", + "params": { + "type": "object", + "properties": { + "enable": { + "type": "boolean", + "description": "Enable (true) or disable (false) periodic reporting", + "example": true + } + }, + "required": ["enable"] + }, + "result": { + "$ref": "#/definitions/result" + } + }, + "setReportingInterval": { + "summary": "Set periodic reporting interval", + "params": { + "type": "object", + "properties": { + "intervalMinutes": { + "type": "number", + "description": "Reporting interval in minutes", + "example": 10 + } + }, + "required": ["intervalMinutes"] + }, + "result": { + "$ref": "#/definitions/result" + } + } + }, + "events": { + "onReportGenerated": { + "summary": "Notification when a network report is generated", + "params": { + "type": "object", + "properties": { + "status": { + "type": "string", + "description": "Report generation status message", + "example": "Report generated successfully" + } + }, + "required": ["status"] + } + } + } +} diff --git a/networkstats/NetworkConnectionStatsImplementation.cpp b/networkstats/NetworkConnectionStatsImplementation.cpp new file mode 100644 index 00000000..61c1ef4c --- /dev/null +++ b/networkstats/NetworkConnectionStatsImplementation.cpp @@ -0,0 +1,298 @@ +/** +* If not stated otherwise in this file or this component's LICENSE +* file the following copyright and licenses apply: +* +* Copyright 2025 RDK Management +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +**/ + +#include "NetworkConnectionStatsImplementation.h" +#include + +#define API_VERSION_NUMBER_MAJOR 1 +#define API_VERSION_NUMBER_MINOR 0 +#define API_VERSION_NUMBER_PATCH 0 + +namespace WPEFramework { +namespace Plugin { + + SERVICE_REGISTRATION(NetworkConnectionStatsImplementation, 1, 0); + + NetworkConnectionStatsImplementation::NetworkConnectionStatsImplementation() + : _adminLock() + , _service(nullptr) + , m_provider(nullptr) + , m_interface("") + , m_ipv4Address("") + , m_ipv6Address("") + , m_ipv4Route("") + , m_ipv6Route("") + , m_ipv4Dns("") + , m_ipv6Dns("") + , _periodicReportingEnabled(true) // Auto-enabled + , _reportingIntervalMinutes(10) // Default 10 minutes + , _stopReporting(false) + { + TRACE(Trace::Information, (_T("NetworkConnectionStatsImplementation Constructor"))); +#if USE_TELEMETRY + t2_init("networkstats"); +#endif + } + + NetworkConnectionStatsImplementation::~NetworkConnectionStatsImplementation() + { + TRACE(Trace::Information, (_T("NetworkConnectionStatsImplementation Destructor"))); + + // Stop periodic reporting thread + _stopReporting = true; + if (_reportingThread.joinable()) { + _reportingThread.join(); + } + + if (m_provider) { + delete m_provider; + m_provider = nullptr; + } + + _service = nullptr; + } + + uint32_t NetworkConnectionStatsImplementation::Configure(PluginHost::IShell* service) + { + TRACE(Trace::Information, (_T("NetworkConnectionStatsImplementation::Configure"))); + uint32_t result = Core::ERROR_NONE; + + ASSERT(service != nullptr); + _service = service; + _service->AddRef(); + + // Parse configuration + JsonObject config; + config.FromString(_service->ConfigLine()); + + if (config.HasLabel("reportingInterval")) { + _reportingIntervalMinutes = config["reportingInterval"].Number(); + TRACE(Trace::Information, (_T("Reporting interval set to %u minutes"), _reportingIntervalMinutes.load())); + } + + if (config.HasLabel("autoStart")) { + _periodicReportingEnabled = config["autoStart"].Boolean(); + } + + // Initialize network provider + m_provider = new NetworkJsonRPCProvider(); + if (m_provider == nullptr) { + TRACE(Trace::Error, (_T("Failed to create NetworkJsonRPCProvider"))); + result = Core::ERROR_GENERAL; + } else { + TRACE(Trace::Information, (_T("NetworkJsonRPCProvider initialized successfully"))); + + // Generate initial report + generateReport(); + + // Start periodic reporting if enabled + if (_periodicReportingEnabled) { + _stopReporting = false; + _reportingThread = std::thread(&NetworkConnectionStatsImplementation::periodicReportingThread, this); + TRACE(Trace::Information, (_T("Periodic reporting started with %u minute interval"), _reportingIntervalMinutes.load())); + } + } + + return result; + } + + void NetworkConnectionStatsImplementation::periodicReportingThread() + { + TRACE(Trace::Information, (_T("Periodic reporting thread started"))); + + while (!_stopReporting && _periodicReportingEnabled) { + // Sleep for configured interval + std::this_thread::sleep_for(std::chrono::minutes(_reportingIntervalMinutes.load())); + + if (_stopReporting || !_periodicReportingEnabled) { + break; + } + + // Generate report + generateReport(); + TRACE(Trace::Information, (_T("Periodic report generated"))); + } + + TRACE(Trace::Information, (_T("Periodic reporting thread stopped"))); + } + + void NetworkConnectionStatsImplementation::generateReport() + { + TRACE(Trace::Information, (_T("Generating network diagnostics report"))); + + _adminLock.Lock(); + + // Run all diagnostic checks + connectionTypeCheck(); + connectionIpCheck(); + defaultIpv4RouteCheck(); + defaultIpv6RouteCheck(); + gatewayPacketLossCheck(); + networkDnsCheck(); + + _adminLock.Unlock(); + + TRACE(Trace::Information, (_T("Network diagnostics report completed"))); + } + + void NetworkConnectionStatsImplementation::logTelemetry(const std::string& eventName, const std::string& message) + { +#if USE_TELEMETRY + T2ERROR t2error = t2_event_s(eventName.c_str(), (char*)message.c_str()); + if (t2error != T2ERROR_SUCCESS) { + TRACE(Trace::Error, (_T("t2_event_s(\"%s\", \"%s\") failed with error %d"), + eventName.c_str(), message.c_str(), t2error)); + } +#endif + } + + void NetworkConnectionStatsImplementation::connectionTypeCheck() + { + if (m_provider) { + std::string connType = m_provider->getConnectionType(); + TRACE(Trace::Information, (_T("Connection type: %s"), connType.c_str())); + logTelemetry("Connection_Type", connType); + } + } + + void NetworkConnectionStatsImplementation::connectionIpCheck() + { + if (m_provider) { + m_interface = m_provider->getInterface(); + + // Get IPv4 settings + m_ipv4Address = m_provider->getIpv4Address(m_interface); + m_ipv4Route = m_provider->getIpv4Gateway(); + m_ipv4Dns = m_provider->getIpv4PrimaryDns(); + + // Get IPv6 settings + m_ipv6Address = m_provider->getIpv6Address(m_interface); + m_ipv6Route = m_provider->getIpv6Gateway(); + m_ipv6Dns = m_provider->getIpv6PrimaryDns(); + + TRACE(Trace::Information, (_T("Interface: %s, IPv4: %s, IPv6: %s"), + m_interface.c_str(), m_ipv4Address.c_str(), m_ipv6Address.c_str())); + TRACE(Trace::Information, (_T("IPv4 Gateway: %s, DNS: %s"), + m_ipv4Route.c_str(), m_ipv4Dns.c_str())); + TRACE(Trace::Information, (_T("IPv6 Gateway: %s, DNS: %s"), + m_ipv6Route.c_str(), m_ipv6Dns.c_str())); + + // Log telemetry events + std::string interfaceInfo = m_interface + "," + m_ipv4Address + "," + m_ipv6Address; + logTelemetry("Network_Interface_Info", interfaceInfo); + + std::string ipv4Info = m_ipv4Route + "," + m_ipv4Dns; + logTelemetry("IPv4_Gateway_DNS", ipv4Info); + + std::string ipv6Info = m_ipv6Route + "," + m_ipv6Dns; + logTelemetry("IPv6_Gateway_DNS", ipv6Info); + } + } + + void NetworkConnectionStatsImplementation::defaultIpv4RouteCheck() + { + if (!m_ipv4Address.empty() && m_ipv4Address != "0.0.0.0") { + if (!m_ipv4Route.empty() && m_ipv4Route != "0.0.0.0") { + TRACE(Trace::Information, (_T("IPv4: Interface %s has gateway %s"), + m_interface.c_str(), m_ipv4Route.c_str())); + } else { + TRACE(Trace::Warning, (_T("IPv4: No valid gateway for interface %s"), m_interface.c_str())); + } + } + } + + void NetworkConnectionStatsImplementation::defaultIpv6RouteCheck() + { + if (!m_ipv6Address.empty() && m_ipv6Address != "::") { + if (!m_ipv6Route.empty() && m_ipv6Route != "::") { + TRACE(Trace::Information, (_T("IPv6: Interface %s has gateway %s"), + m_interface.c_str(), m_ipv6Route.c_str())); + } else { + TRACE(Trace::Warning, (_T("IPv6: No valid gateway for interface %s"), m_interface.c_str())); + } + } + } + + void NetworkConnectionStatsImplementation::gatewayPacketLossCheck() + { + if (!m_provider) { + return; + } + + // Check IPv4 gateway packet loss + if (!m_ipv4Route.empty() && m_ipv4Route != "0.0.0.0") { + TRACE(Trace::Information, (_T("Pinging IPv4 gateway: %s"), m_ipv4Route.c_str())); + bool success = m_provider->pingToGatewayCheck(m_ipv4Route, "IPv4", 5, 30); + if (success) { + std::string packetLoss = m_provider->getPacketLoss(); + std::string avgRtt = m_provider->getAvgRtt(); + TRACE(Trace::Information, (_T("IPv4 gateway ping - Loss: %s%%, RTT: %sms"), + packetLoss.c_str(), avgRtt.c_str())); + + std::string ipv4PingInfo = "IPv4," + m_ipv4Route + "," + packetLoss + "," + avgRtt; + logTelemetry("Gateway_Ping_Stats", ipv4PingInfo); + } else { + TRACE(Trace::Error, (_T("IPv4 gateway ping failed"))); + logTelemetry("Gateway_Ping_Stats", "IPv4," + m_ipv4Route + ",failed,0"); + } + } + + // Check IPv6 gateway packet loss + if (!m_ipv6Route.empty() && m_ipv6Route != "::") { + TRACE(Trace::Information, (_T("Pinging IPv6 gateway: %s"), m_ipv6Route.c_str())); + bool success = m_provider->pingToGatewayCheck(m_ipv6Route, "IPv6", 5, 30); + if (success) { + std::string packetLoss = m_provider->getPacketLoss(); + std::string avgRtt = m_provider->getAvgRtt(); + TRACE(Trace::Information, (_T("IPv6 gateway ping - Loss: %s%%, RTT: %sms"), + packetLoss.c_str(), avgRtt.c_str())); + + std::string ipv6PingInfo = "IPv6," + m_ipv6Route + "," + packetLoss + "," + avgRtt; + logTelemetry("Gateway_Ping_Stats", ipv6PingInfo); + } else { + TRACE(Trace::Error, (_T("IPv6 gateway ping failed"))); + logTelemetry("Gateway_Ping_Stats", "IPv6," + m_ipv6Route + ",failed,0"); + } + } + } + + void NetworkConnectionStatsImplementation::networkDnsCheck() + { + bool hasDns = false; + + if (!m_ipv4Dns.empty()) { + TRACE(Trace::Information, (_T("IPv4 DNS: %s"), m_ipv4Dns.c_str())); + hasDns = true; + } + + if (!m_ipv6Dns.empty()) { + TRACE(Trace::Information, (_T("IPv6 DNS: %s"), m_ipv6Dns.c_str())); + hasDns = true; + } + + if (hasDns) { + TRACE(Trace::Information, (_T("DNS configuration present"))); + } else { + TRACE(Trace::Warning, (_T("No DNS configuration found"))); + logTelemetry("DNS_Status", "No DNS configured"); + } + } + +} // namespace Plugin +} // namespace WPEFramework diff --git a/networkstats/NetworkConnectionStatsImplementation.h b/networkstats/NetworkConnectionStatsImplementation.h new file mode 100644 index 00000000..8968191e --- /dev/null +++ b/networkstats/NetworkConnectionStatsImplementation.h @@ -0,0 +1,93 @@ +/** +* If not stated otherwise in this file or this component's LICENSE +* file the following copyright and licenses apply: +* +* Copyright 2025 RDK Management +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +**/ + +#pragma once + +#include "Module.h" +#include "INetworkConnectionStats.h" +#include +#include "INetworkData.h" +#include "ThunderJsonRPCProvider.h" +#include +#include + +#if USE_TELEMETRY +#include +#endif + +namespace WPEFramework { +namespace Plugin { + + // Internal implementation - runs network diagnostics automatically + // No external APIs exposed + class NetworkConnectionStatsImplementation + : public Exchange::INetworkConnectionStats + , public Exchange::IConfiguration { + + public: + NetworkConnectionStatsImplementation(const NetworkConnectionStatsImplementation&) = delete; + NetworkConnectionStatsImplementation& operator=(const NetworkConnectionStatsImplementation&) = delete; + NetworkConnectionStatsImplementation(); + virtual ~NetworkConnectionStatsImplementation(); + + BEGIN_INTERFACE_MAP(NetworkConnectionStatsImplementation) + INTERFACE_ENTRY(Exchange::INetworkConnectionStats) + INTERFACE_ENTRY(Exchange::IConfiguration) + END_INTERFACE_MAP + + // IConfiguration interface + uint32_t Configure(PluginHost::IShell* service) override; + + private: + // Internal diagnostic methods + void connectionTypeCheck(); + void connectionIpCheck(); + void defaultIpv4RouteCheck(); + void defaultIpv6RouteCheck(); + void gatewayPacketLossCheck(); + void networkDnsCheck(); + void generateReport(); + void logTelemetry(const std::string& eventName, const std::string& message); + void periodicReportingThread(); + + private: + mutable Core::CriticalSection _adminLock; + PluginHost::IShell* _service; + + // Network data provider + INetworkData* m_provider; + + // Cached network data + std::string m_interface; + std::string m_ipv4Address; + std::string m_ipv6Address; + std::string m_ipv4Route; + std::string m_ipv6Route; + std::string m_ipv4Dns; + std::string m_ipv6Dns; + + // Periodic reporting + std::atomic _periodicReportingEnabled; + std::atomic _reportingIntervalMinutes; + std::thread _reportingThread; + std::atomic _stopReporting; + }; + +} // namespace Plugin +} // namespace WPEFramework diff --git a/networkstats/NetworkStatsDesign_ver1.md b/networkstats/NetworkStatsDesign_ver1.md new file mode 100644 index 00000000..564bab73 --- /dev/null +++ b/networkstats/NetworkStatsDesign_ver1.md @@ -0,0 +1,455 @@ +# NetworkStats Module Design Document + +## Overview +NetworkStats is a Thunder plugin that runs out-of-process and monitors network connectivity by subscribing to NetworkManager plugin events. It periodically collects network statistics and sends T2 telemetry events. + +## Architecture + +### Key Components +- **NetworkStats Plugin**: Main Thunder plugin (out-of-process) +- **NetworkManager Interface**: COM-RPC connection to NetworkManager plugin +- **Timer Thread**: Periodic statistics collection (10-minute interval) +- **T2 Telemetry Client**: Sends telemetry events +- **Event Subscription Manager**: Manages NetworkManager event subscriptions + +--- + +## Sequence Diagram + +```mermaid +sequenceDiagram + participant Thunder as Thunder Framework + participant NS as NetworkStats Plugin + participant NM as NetworkManager Plugin + participant Timer as Timer Thread + participant T2 as T2 Telemetry + + Note over Thunder,T2: Plugin Initialization + Thunder->>NS: Initialize(IShell* service) + activate NS + NS->>NS: Create NetworkStatsImplementation + NS->>Thunder: Register notification callbacks + NS->>NM: Establish COM-RPC connection + activate NM + NM-->>NS: Connection established + NS->>NM: Register(¬ification) + NS->>NM: Subscribe to events
(onInterfaceStateChange,
onIPAddressStatusChanged,
onInternetStatusChange) + NM-->>NS: Subscription confirmed + + Note over NS,Timer: Timer Thread Creation + NS->>Timer: Create timer thread
(10 min interval) + activate Timer + Timer->>Timer: Initialize in stopped state + NS-->>Thunder: Initialization complete + deactivate NS + + Note over NM,Timer: Network Connection Event + NM->>NS: onInterfaceStateChange(INTERFACE_CONNECTED) + activate NS + NS->>NS: Process connection event + NS->>Timer: Start timer thread + Timer->>Timer: Begin 10-min countdown + deactivate NS + + Note over Timer,T2: Periodic Statistics Collection + loop Every 10 minutes + Timer->>Timer: Timer expired + Timer->>NS: Collect network statistics + activate NS + NS->>NM: GetIPSettings() + NM-->>NS: IP configuration + NS->>NM: GetActiveInterface() + NM-->>NS: Interface details + NS->>NS: Aggregate statistics + NS->>T2: Send telemetry event
(connection type, IP, DNS, etc.) + activate T2 + T2-->>NS: Event sent + deactivate T2 + Timer->>Timer: Restart 10-min timer + deactivate NS + end + + Note over NM,Timer: Network Disconnection Event + NM->>NS: onInterfaceStateChange(INTERFACE_DISCONNECTED) + activate NS + NS->>NS: Process disconnection event + NS->>Timer: Stop timer thread + Timer->>Timer: Halt countdown + deactivate NS + + Note over NM,Timer: Network Reconnection Event + NM->>NS: onInternetStatusChange(INTERNET_CONNECTED) + activate NS + NS->>NS: Process reconnection + NS->>Timer: Resume timer thread + Timer->>Timer: Resume 10-min countdown + deactivate NS + + Note over Thunder,T2: Plugin Deinitialization + Thunder->>NS: Deinitialize() + activate NS + NS->>Timer: Stop timer thread + deactivate Timer + Timer-->>NS: Thread stopped + NS->>NM: Unregister notifications + NS->>NM: Unsubscribe from events + NM-->>NS: Unsubscribed + NS->>NM: Release COM-RPC connection + deactivate NM + NS->>NS: Cleanup resources + NS-->>Thunder: Deinitialization complete + deactivate NS +``` + +--- + +## Flowchart + +```mermaid +flowchart TD + Start([Thunder Starts NetworkStats Plugin]) --> Init[Initialize Plugin] + Init --> CreateImpl[Create NetworkStatsImplementation
Out-of-Process] + CreateImpl --> RegNotif[Register Notification Callbacks] + RegNotif --> EstablishComRPC[Establish COM-RPC Connection
to NetworkManager] + + EstablishComRPC --> CheckConn{Connection
Successful?} + CheckConn -->|No| LogError[Log Error & Return Failure] + CheckConn -->|Yes| Subscribe[Subscribe to NetworkManager Events:
- onInterfaceStateChange
- onIPAddressStatusChanged
- onInternetStatusChange] + + Subscribe --> CreateTimer[Create Timer Thread
10-min interval, stopped] + CreateTimer --> WaitEvent[Wait for Network Events] + + WaitEvent --> EventReceived{Event
Received?} + + EventReceived -->|Interface Connected| StartTimer[Start Timer Thread] + EventReceived -->|Interface Disconnected| StopTimer[Stop Timer Thread] + EventReceived -->|Internet Connected| ResumeTimer[Resume Timer Thread] + EventReceived -->|Internet Disconnected| PauseTimer[Pause Timer Thread] + + StartTimer --> TimerActive[Timer Thread Active] + ResumeTimer --> TimerActive + + TimerActive --> TimerLoop{Timer
Expired?} + TimerLoop -->|No| TimerActive + TimerLoop -->|Yes| CollectStats[Collect Network Statistics] + + CollectStats --> GetIPSettings[Call NetworkManager:
GetIPSettings] + GetIPSettings --> GetInterface[Call NetworkManager:
GetActiveInterface] + GetInterface --> GetDNS[Call NetworkManager:
GetNameServers] + GetDNS --> AggregateData[Aggregate Statistics Data] + + AggregateData --> SendT2[Send T2 Telemetry Event:
- Connection Type
- IP Addresses
- DNS Entries
- Interface Status] + SendT2 --> LogTelemetry[Log Telemetry Event] + LogTelemetry --> RestartTimer[Restart 10-min Timer] + RestartTimer --> TimerActive + + StopTimer --> TimerStopped[Timer Thread Stopped] + PauseTimer --> TimerStopped + TimerStopped --> WaitEvent + + EventReceived -->|Deinitialize| Shutdown[Thunder Shutdown Signal] + Shutdown --> StopTimerThread[Stop Timer Thread] + StopTimerThread --> UnsubEvents[Unsubscribe from All Events] + UnsubEvents --> UnregNotif[Unregister Notifications] + UnregNotif --> ReleaseComRPC[Release COM-RPC Connection] + ReleaseComRPC --> Cleanup[Cleanup Resources] + Cleanup --> End([Plugin Deinitialized]) + + LogError --> End + + style Start fill:#90EE90 + style End fill:#FFB6C1 + style EstablishComRPC fill:#FFD700 + style Subscribe fill:#FFD700 + style CollectStats fill:#87CEEB + style SendT2 fill:#DDA0DD + style Shutdown fill:#FFA07A +``` + +--- + +## Component Details + +### 1. NetworkStats Plugin Structure + +```cpp +namespace WPEFramework { + namespace Plugin { + class NetworkStats : public PluginHost::IPlugin, public PluginHost::JSONRPC { + class Notification : public RPC::IRemoteConnection::INotification, + public Exchange::INetworkManager::INotification { + // Event handlers for NetworkManager events + void onInterfaceStateChange(const Exchange::INetworkManager::InterfaceState state, + const string interface) override; + void onIPAddressStatusChanged(const string& interface, const string& ipAddress, + const Exchange::INetworkManager::IPStatus status) override; + void onInternetStatusChange(const Exchange::INetworkManager::InternetStatus prevState, + const Exchange::INetworkManager::InternetStatus currState) override; + // ... other event handlers + }; + + private: + uint32_t _connectionId; + PluginHost::IShell* _service; + Exchange::INetworkManager* _networkManager; + Core::Sink _notification; + + // Timer management + std::thread _statsCollectionThread; + std::atomic _timerRunning; + std::mutex _timerMutex; + std::condition_variable _timerCondVar; + }; + } +} +``` + +### 2. Timer Thread State Machine + +``` +States: +┌─────────┐ Start Event ┌─────────┐ +│ STOPPED │ ───────────────► │ RUNNING │ +└─────────┘ └─────────┘ + ▲ │ + │ │ + │ Stop/Disconnect │ Pause Event + │ Event │ + │ ▼ + │ ┌────────┐ + └────────────────────────│ PAUSED │ + Resume Event └────────┘ +``` + +### 3. Statistics Collection Flow + +**Data Collected Every 10 Minutes:** +- Active network interface (eth0/wlan0) +- Connection type (Ethernet/WiFi) +- IPv4 address and subnet +- IPv6 address (if available) +- DNS server entries (primary/secondary) +- Default gateway +- Interface state (UP/DOWN) +- Internet connectivity status + +**T2 Telemetry Event Structure:** +```json +{ + "timestamp": "2025-01-23T10:30:00Z", + "interface": "eth0", + "connectionType": "ETHERNET", + "ipv4Address": "192.168.1.100", + "ipv6Address": "fe80::1234:5678:90ab:cdef", + "dnsServers": ["8.8.8.8", "8.8.4.4"], + "gateway": "192.168.1.1", + "internetStatus": "CONNECTED", + "signalQuality": "EXCELLENT" +} +``` + +--- + +## Implementation Classes + +### NetworkStatsImplementation.cpp +- **Purpose**: Out-of-process implementation +- **Key Methods**: + - `Configure()`: Parses config, initializes COM-RPC connection + - `StartStatisticsCollection()`: Starts timer thread + - `StopStatisticsCollection()`: Stops timer thread + - `CollectAndSendStatistics()`: Gathers data and sends T2 events + - `OnNetworkEvent()`: Handles NetworkManager notifications + +### NetworkStatsTimer.cpp +- **Purpose**: Manages periodic statistics collection +- **Key Methods**: + - `Start()`: Begin 10-minute countdown + - `Stop()`: Halt timer thread + - `Pause()`: Suspend timer without destroying thread + - `Resume()`: Continue from paused state + - `OnTimerExpired()`: Callback for statistics collection + +### T2TelemetryClient.cpp +- **Purpose**: Interface with T2 telemetry system +- **Key Methods**: + - `SendEvent()`: Sends JSON telemetry data + - `FormatStatistics()`: Converts statistics to T2 format + +--- + +## Configuration + +### NetworkStats.config +```json +{ + "locator": "libWPEFrameworkNetworkStats.so", + "classname": "NetworkStats", + "callsign": "org.rdk.NetworkStats", + "autostart": true, + "configuration": { + "root": { + "outofprocess": true, + "locator": "libWPEFrameworkNetworkStatsImpl.so" + }, + "collectionInterval": 600, + "networkManagerCallsign": "org.rdk.NetworkManager", + "enableT2Telemetry": true, + "telemetryMarker": "NETWORK_STATS" + } +} +``` + +--- + +## Event Subscription Logic + +### Subscribed Events +| Event | Action | +|-------|--------| +| `onInterfaceStateChange(CONNECTED)` | Start timer thread | +| `onInterfaceStateChange(DISCONNECTED)` | Stop timer thread | +| `onInternetStatusChange(CONNECTED)` | Resume timer thread | +| `onInternetStatusChange(DISCONNECTED)` | Pause timer thread | +| `onIPAddressStatusChanged` | Log IP change, continue timer | + +--- + +## Error Handling + +1. **COM-RPC Connection Failure**: + - Retry connection up to 5 times with exponential backoff + - Log fatal error if all retries fail + - Return initialization failure to Thunder + +2. **Timer Thread Crash**: + - Log exception details + - Attempt to restart timer thread once + - Notify Thunder of degraded operation + +3. **T2 Telemetry Failure**: + - Log warning (non-fatal) + - Cache statistics for next successful transmission + - Continue timer operation + +--- + +## Threading Model + +``` +Thunder Main Process +│ +└─► NetworkStats Plugin (Main Thread) + │ + ├─► COM-RPC Communication Thread (Thunder managed) + │ + └─► Out-of-Process Instance + │ + ├─► Event Notification Thread (Thunder managed) + │ + └─► Statistics Collection Timer Thread (Custom) + │ + └─► T2 Telemetry Thread (System) +``` + +--- + +## Shutdown Sequence + +1. Thunder sends `Deinitialize()` signal +2. NetworkStats sets `_timerRunning = false` +3. Notify timer condition variable to wake thread +4. Join timer thread (wait for completion) +5. Unregister from NetworkManager notifications +6. Call `_networkManager->Unregister(&_notification)` +7. Release COM-RPC interface: `_networkManager->Release()` +8. Cleanup Thunder connection: `_service->Release()` +9. Return control to Thunder + +--- + +## Testing Strategy + +### L1 Unit Tests (`tests/l1Test/networkstats_l1_test.cpp`) +- Timer thread start/stop/pause/resume logic +- Statistics data aggregation +- T2 telemetry formatting + +### L2 Integration Tests (`tests/l2Test/networkstats_l2_test.cpp`) +- Mock NetworkManager COM-RPC interface +- Simulate network events (connect/disconnect) +- Verify timer behavior with event triggers +- Validate T2 event payload structure + +--- + +## Dependencies + +- **Thunder Framework**: COM-RPC infrastructure +- **NetworkManager Plugin**: Network state provider +- **T2 Library**: Telemetry event transmission +- **libnm/GDBus** (indirect): Via NetworkManager backend + +--- + +## Build Integration + +### CMakeLists.txt +```cmake +add_library(NetworkStats SHARED + plugin/networkstats/NetworkStats.cpp + plugin/networkstats/NetworkStatsImplementation.cpp + plugin/networkstats/NetworkStatsTimer.cpp + plugin/networkstats/T2TelemetryClient.cpp +) + +target_link_libraries(NetworkStats + ${NAMESPACE}Plugins::${NAMESPACE}Plugins + ${NAMESPACE}Definitions::${NAMESPACE}Definitions + NetworkManagerInterfaces # For INetworkManager + t2log # T2 Telemetry +) + +install(TARGETS NetworkStats DESTINATION lib/${NAMESPACE}/plugins) +install(FILES NetworkStats.config DESTINATION ${CMAKE_INSTALL_PREFIX}/etc/${NAMESPACE}/plugins) +``` + +--- + +## Logging + +Use `NetworkManagerLogger.h` macros: + +```cpp +NMLOG_INFO("NetworkStats timer started (interval: %d sec)", interval); +NMLOG_WARNING("Failed to collect statistics, retry scheduled"); +NMLOG_ERROR("COM-RPC connection to NetworkManager lost"); +NMLOG_DEBUG("Telemetry event sent: %s", jsonData.c_str()); +``` + +--- + +## Future Enhancements + +1. **Configurable Collection Interval**: Allow runtime adjustment via JSON-RPC +2. **Historical Data Storage**: Cache last N statistics for trend analysis +3. **Bandwidth Monitoring**: Track data usage per interface +4. **Alert Thresholds**: Trigger events on connection quality degradation +5. **Multi-Interface Support**: Collect statistics from all interfaces simultaneously + +--- + +## References + +- NetworkManager Plugin Documentation +- [Thunder Plugin Architecture](https://github.com/WebPlatformForEmbedded/Thunder) +- COM-RPC Interface Guide +- [T2 Telemetry Specification](https://github.com/rdkcentral/rdk-telemetry) + +--- + +## Revision History + +| Version | Date | Author | Changes | +|---------|------|--------|---------| +| 1.0 | 2025-12-17 | Initial Design | Created sequence diagram and flowchart | diff --git a/networkstats/README_INTERNAL_PLUGIN.md b/networkstats/README_INTERNAL_PLUGIN.md new file mode 100644 index 00000000..be6cce23 --- /dev/null +++ b/networkstats/README_INTERNAL_PLUGIN.md @@ -0,0 +1,287 @@ +# NetworkConnectionStats - Internal Thunder Plugin (No External APIs) + +## Design Philosophy + +This plugin is designed to run **internally within Thunder framework** without exposing any external APIs. It automatically performs network diagnostics and reports telemetry data in the background. + +## Architecture + +``` +┌─────────────────────────────────────────┐ +│ Thunder/WPEFramework Process │ +│ ┌───────────────────────────────────┐ │ +│ │ NetworkConnectionStats │ │ +│ │ (Plugin - IPlugin only) │ │ +│ │ - Auto-activates │ │ +│ │ - No JSONRPC APIs │ │ +│ │ - Spawns out-of-process │ │ +│ └──────────┬────────────────────────┘ │ +└─────────────┼───────────────────────────┘ + │ COM-RPC (internal) + ▼ +┌─────────────────────────────────────────┐ +│ Out-of-Process (Background Service) │ +│ ┌───────────────────────────────────┐ │ +│ │ NetworkConnectionStatsImpl │ │ +│ │ - Auto-starts periodic reporting │ │ +│ │ - Runs every N minutes (default10)│ │ +│ │ - Generates diagnostic reports │ │ +│ │ - Sends T2 telemetry events │ │ +│ │ - No external APIs exposed │ │ +│ └───────────────────────────────────┘ │ +└─────────────────────────────────────────┘ +``` + +## What This Plugin Does + +### On Activation +1. Thunder activates the plugin automatically (`startmode: "Activated"`) +2. Plugin spawns out-of-process implementation +3. Implementation initializes network provider +4. **Generates initial diagnostic report** +5. **Starts periodic reporting thread** (runs every N minutes) + +### Periodic Diagnostics (Every N Minutes) +The plugin automatically runs these checks: + +1. **Connection Type Check** + - Detects Ethernet vs WiFi + - Logs to telemetry: `Connection_Type` + +2. **IP Configuration Check** + - Gets interface name, IPv4/IPv6 addresses + - Gets IPv4/IPv6 gateways and DNS servers + - Logs to telemetry: `Network_Interface_Info`, `IPv4_Gateway_DNS`, `IPv6_Gateway_DNS` + +3. **IPv4 Route Check** + - Validates IPv4 gateway availability + - Traces warnings if no gateway found + +4. **IPv6 Route Check** + - Validates IPv6 gateway availability + - Traces warnings if no gateway found + +5. **Gateway Packet Loss Check** + - Pings IPv4 gateway (5 packets, 30s timeout) + - Pings IPv6 gateway (5 packets, 30s timeout) + - Measures packet loss percentage and average RTT + - Logs to telemetry: `Gateway_Ping_Stats` + +6. **DNS Configuration Check** + - Verifies DNS servers are configured + - Logs warnings if no DNS found + +### Telemetry Events Sent + +All events use the T2 telemetry framework: + +| Event Name | Data Format | Example | +|------------|-------------|---------| +| `Connection_Type` | String | `"Ethernet"` or `"WiFi"` | +| `Network_Interface_Info` | CSV | `"eth0,192.168.1.100,fe80::1"` | +| `IPv4_Gateway_DNS` | CSV | `"192.168.1.1,8.8.8.8"` | +| `IPv6_Gateway_DNS` | CSV | `"fe80::1,2001:4860:4860::8888"` | +| `Gateway_Ping_Stats` | CSV | `"IPv4,192.168.1.1,0.0,2.5"` (version,gateway,loss%,avgRTT) | +| `DNS_Status` | String | `"No DNS configured"` (only on error) | + +## Configuration + +### NetworkConnectionStats.config +```json +{ + "locator": "libWPEFrameworkNetworkConnectionStats.so", + "classname": "NetworkConnectionStats", + "startmode": "Activated", + "configuration": { + "outofprocess": true, + "root": { "mode": "Local" }, + "reportingInterval": 10, + "autoStart": true + } +} +``` + +### Configuration Parameters + +| Parameter | Type | Default | Description | +|-----------|------|---------|-------------| +| `reportingInterval` | number | 10 | Diagnostic interval in minutes | +| `autoStart` | boolean | true | Auto-start periodic reporting on plugin activation | +| `outofprocess` | boolean | true | Run implementation out-of-process (recommended) | + +## Files Structure + +### Core Plugin Files +- **Module.h/cpp** - Thunder module definition +- **INetworkConnectionStats.h** - Minimal COM-RPC interface (no methods) +- **NetworkConnectionStats.h** - Plugin class (IPlugin only) +- **NetworkConnectionStats.cpp** - Plugin implementation +- **NetworkConnectionStatsImplementation.h** - Out-of-process implementation header +- **NetworkConnectionStatsImplementation.cpp** - All diagnostic logic + +### Supporting Files +- **INetworkData.h** - Network data provider interface +- **ThunderJsonRPCProvider.h/cpp** - NetworkManager plugin client +- **NetworkConnectionStats.config** - Plugin configuration +- **NetworkConnectionStats.conf.in** - CMake config template +- **CMakeLists.txt** - Build system + +## No External APIs + +### What Was Removed +- ❌ JSON-RPC API methods +- ❌ External event notifications +- ❌ Public getter/setter methods +- ❌ NetworkConnectionStats.json API specification +- ❌ INotification interface + +### What Remains +- ✅ IPlugin interface (Initialize, Deinitialize, Information) +- ✅ IConfiguration interface (for plugin config) +- ✅ Internal diagnostic methods +- ✅ Telemetry logging +- ✅ Periodic reporting thread + +## How It Works + +### Startup Sequence +``` +1. Thunder starts +2. Thunder loads NetworkConnectionStats plugin +3. Plugin Initialize() called +4. Plugin spawns out-of-process NetworkConnectionStatsImplementation +5. Implementation Configure() called +6. NetworkJsonRPCProvider initialized +7. Initial diagnostic report generated immediately +8. Periodic reporting thread started (if autoStart=true) +9. Thread sleeps for 'reportingInterval' minutes +10. Thread wakes up, generates report +11. Repeat steps 9-10 until plugin deactivated +``` + +### Logging + +All diagnostic output goes to Thunder trace logs: +```bash +# View logs +journalctl -u wpeframework -f | grep NetworkConnectionStats + +# Example output: +INFO: NetworkConnectionStats Constructor +INFO: NetworkConnectionStatsImplementation::Configure +INFO: Periodic reporting started with 10 minute interval +INFO: Connection type: Ethernet +INFO: Interface: eth0, IPv4: 192.168.1.100, IPv6: fe80::1 +INFO: IPv4 Gateway: 192.168.1.1, DNS: 8.8.8.8 +INFO: Pinging IPv4 gateway: 192.168.1.1 +INFO: IPv4 gateway ping - Loss: 0.0%, RTT: 2.5ms +INFO: Network diagnostics report completed +INFO: Periodic report generated +``` + +## Building + +```bash +cmake -B build \ + -DPLUGIN_NETWORKCONNECTIONSTATS=ON \ + -DPLUGIN_NETWORKCONNECTIONSTATS_OUTOFPROCESS=ON \ + -DUSE_TELEMETRY=ON \ + -DPLUGIN_NETWORKCONNECTIONSTATS_REPORTING_INTERVAL=10 + +cmake --build build +sudo cmake --install build +``` + +## Deployment + +### Install Plugin +```bash +# Libraries installed to: +/usr/lib/wpeframework/plugins/libWPEFrameworkNetworkConnectionStats.so +/usr/lib/wpeframework/plugins/libWPEFrameworkNetworkConnectionStatsImplementation.so + +# Config installed to: +/etc/wpeframework/plugins/NetworkConnectionStats.json +``` + +### Thunder Auto-Activation +Plugin automatically activates on Thunder startup (no manual activation needed). + +## Monitoring + +### Check Plugin Status +```bash +curl http://localhost:9998/jsonrpc -d '{ + "jsonrpc": "2.0", + "id": 1, + "method": "Controller.1.status@NetworkConnectionStats" +}' +``` + +### Expected Response +```json +{ + "jsonrpc": "2.0", + "id": 1, + "result": [ + { + "callsign": "NetworkConnectionStats", + "locator": "libWPEFrameworkNetworkConnectionStats.so", + "classname": "NetworkConnectionStats", + "autostart": true, + "state": "activated" + } + ] +} +``` + +### View Telemetry Events +Telemetry events are sent via T2 framework and uploaded to backend systems. Check T2 logs: +```bash +journalctl -u t2 -f +``` + +## Advantages of Internal-Only Design + +1. **Simplicity** - No API surface to maintain or secure +2. **Background Operation** - Runs autonomously without external triggers +3. **Zero Configuration** - Works out-of-the-box with defaults +4. **Resource Efficient** - Periodic checks minimize CPU/network usage +5. **Telemetry Integration** - Data flows automatically to analytics backend +6. **Process Isolation** - Out-of-process prevents Thunder crashes +7. **Automatic Recovery** - Thunder restarts implementation if it crashes + +## Use Cases + +This plugin is ideal for: +- **Continuous network monitoring** - Track connection health 24/7 +- **Proactive diagnostics** - Detect issues before users report them +- **Analytics/BigData** - Feed network metrics to data pipelines +- **Fleet management** - Monitor thousands of devices remotely +- **SLA tracking** - Measure uptime and connectivity quality + +## Comparison: Standalone vs Thunder Plugin + +| Aspect | Standalone (Old) | Thunder Plugin (New) | +|--------|------------------|----------------------| +| **Execution** | Separate process with main() | Thunder-managed lifecycle | +| **Startup** | Manual launch | Auto-activated by Thunder | +| **APIs** | None | None (internal-only) | +| **Logging** | printf/fprintf | Thunder TRACE framework | +| **Telemetry** | T2 direct calls | T2 direct calls (same) | +| **Configuration** | Hardcoded | Thunder config system | +| **Monitoring** | None | Thunder Controller API | +| **Integration** | Isolated | Part of Thunder ecosystem | + +## Summary + +NetworkConnectionStats is now a **self-contained Thunder plugin** that: +- ✅ Runs automatically in the background +- ✅ Performs periodic network diagnostics (every N minutes) +- ✅ Sends telemetry data to T2 framework +- ✅ Requires zero external interaction +- ✅ Has no exposed APIs (internal-only) +- ✅ Integrates seamlessly with Thunder framework +- ✅ Provides continuous network health monitoring + +**Perfect for production deployments that need passive network monitoring without exposing attack surfaces!** diff --git a/networkstats/THUNDER_PLUGIN_QUICK_REFERENCE.md b/networkstats/THUNDER_PLUGIN_QUICK_REFERENCE.md new file mode 100644 index 00000000..6a8a7abc --- /dev/null +++ b/networkstats/THUNDER_PLUGIN_QUICK_REFERENCE.md @@ -0,0 +1,317 @@ +# NetworkConnectionStats Thunder Plugin - Quick Reference + +## Architecture Pattern (Following Telemetry) + +``` +┌─────────────────────────────────────────────────────────────────┐ +│ Thunder/WPEFramework Process │ +│ ┌────────────────────────────────────────────────────────────┐ │ +│ │ NetworkConnectionStats (Plugin) │ │ +│ │ - Inherits IPlugin + JSONRPC │ │ +│ │ - Manages RPC connection │ │ +│ │ - Forwards JSON-RPC calls │ │ +│ │ - Handles notifications │ │ +│ └────────────────┬───────────────────────────────────────────┘ │ +│ │ COM-RPC │ +└───────────────────┼──────────────────────────────────────────────┘ + │ + ▼ +┌─────────────────────────────────────────────────────────────────┐ +│ Out-of-Process (Separate Process) │ +│ ┌────────────────────────────────────────────────────────────┐ │ +│ │ NetworkConnectionStatsImplementation │ │ +│ │ - Implements INetworkConnectionStats │ │ +│ │ - Implements IConfiguration │ │ +│ │ - Contains business logic │ │ +│ │ - Manages network diagnostics │ │ +│ │ - Sends notifications via INotification │ │ +│ │ ┌──────────────────────────────────────────────────────┐ │ │ +│ │ │ NetworkJsonRPCProvider (INetworkData) │ │ │ +│ │ │ - Calls NetworkManager plugin APIs │ │ │ +│ │ │ - Gets network interface info │ │ │ +│ │ │ - Performs ping tests │ │ │ +│ │ └──────────────────────────────────────────────────────┘ │ │ +│ └────────────────────────────────────────────────────────────┘ │ +└─────────────────────────────────────────────────────────────────┘ +``` + +## File Mapping: Telemetry → NetworkConnectionStats + +| Telemetry | NetworkConnectionStats | Purpose | +|-----------|------------------------|---------| +| Module.h/cpp | Module.h/cpp | Thunder module definition | +| ITelemetry.h | INetworkConnectionStats.h | COM-RPC interface | +| Telemetry.h | NetworkConnectionStats.h | Plugin class header | +| Telemetry.cpp | NetworkConnectionStats.cpp | Plugin implementation | +| TelemetryImplementation.h | NetworkConnectionStatsImplementation.h | Out-of-process header | +| TelemetryImplementation.cpp | NetworkConnectionStatsImplementation.cpp | Out-of-process impl | +| Telemetry.json | NetworkConnectionStats.json | JSON-RPC API spec | +| Telemetry.config | NetworkConnectionStats.config | Config file | + +## Key Classes & Interfaces + +### 1. NetworkConnectionStats (Plugin) +**File**: NetworkConnectionStats.h/cpp +**Inherits**: PluginHost::IPlugin, PluginHost::JSONRPC +**Purpose**: Main plugin class running in Thunder process + +**Key Methods**: +- `Initialize(PluginHost::IShell*)` - Plugin activation +- `Deinitialize(PluginHost::IShell*)` - Plugin cleanup +- `Information()` - Plugin description +- `Deactivated(RPC::IRemoteConnection*)` - Handle out-of-process crash + +**Inner Class**: `Notification` +- Implements `RPC::IRemoteConnection::INotification` +- Implements `INetworkConnectionStats::INotification` +- Forwards events to JSON-RPC clients + +### 2. INetworkConnectionStats (Interface) +**File**: INetworkConnectionStats.h +**Purpose**: COM-RPC interface definition + +**Sub-Interface**: `INotification` +- `OnReportGenerated(const string&)` - Event notification + +**Main Interface Methods**: +- `Register/Unregister` - Notification subscription +- `GenerateReport()` - Trigger diagnostics +- `GetConnectionType()` - Network type +- `GetIpv4Address/GetIpv6Address` - IP addresses +- `GetIpv4Gateway/GetIpv6Gateway` - Gateways +- `GetIpv4Dns/GetIpv6Dns` - DNS servers +- `GetInterface()` - Active interface +- `PingGateway()` - Gateway connectivity test +- `SetPeriodicReporting()` - Enable/disable periodic reports +- `SetReportingInterval()` - Configure interval + +### 3. NetworkConnectionStatsImplementation +**File**: NetworkConnectionStatsImplementation.h/cpp +**Inherits**: INetworkConnectionStats, IConfiguration +**Purpose**: Out-of-process business logic + +**Key Methods**: +- `Configure(PluginHost::IShell*)` - Initialize from config +- All INetworkConnectionStats interface methods +- `connectionTypeCheck()` - Internal diagnostic +- `connectionIpCheck()` - Internal diagnostic +- `defaultIpv4RouteCheck()` - Internal diagnostic +- `defaultIpv6RouteCheck()` - Internal diagnostic +- `gatewayPacketLossCheck()` - Internal diagnostic +- `networkDnsCheck()` - Internal diagnostic +- `logTelemetry()` - T2 event logging +- `periodicReportingThread()` - Background thread + +## Interface Macros Explained + +### BEGIN_INTERFACE_MAP / END_INTERFACE_MAP +Thunder's interface query mechanism (like COM's QueryInterface): + +```cpp +BEGIN_INTERFACE_MAP(NetworkConnectionStats) + INTERFACE_ENTRY(PluginHost::IPlugin) // Standard plugin interface + INTERFACE_ENTRY(PluginHost::IDispatcher) // JSON-RPC dispatcher + INTERFACE_AGGREGATE(Exchange::INetworkConnectionStats, _networkStats) // Delegate to out-of-process +END_INTERFACE_MAP +``` + +**INTERFACE_AGGREGATE**: Forwards interface queries to another object (_networkStats = out-of-process impl) + +## RPC Connection Flow + +1. **Plugin Activation** (In-process): + ```cpp + Initialize(service) { + _networkStats = service->Root(_connectionId, 5000, "NetworkConnectionStatsImplementation"); + // Spawns out-of-process, creates proxy + } + ``` + +2. **Out-of-process Creation**: + - Thunder spawns new process + - Loads NetworkConnectionStatsImplementation library + - Creates instance via SERVICE_REGISTRATION macro + - Establishes RPC channel + +3. **API Call Flow**: + ``` + JSON-RPC Client → Thunder → NetworkConnectionStats (plugin) + → COM-RPC Proxy → Out-of-process → NetworkConnectionStatsImplementation + → NetworkJsonRPCProvider → NetworkManager Plugin + ``` + +4. **Event Flow**: + ``` + NetworkConnectionStatsImplementation::notifyReportGenerated() + → INotification::OnReportGenerated() + → COM-RPC channel + → NetworkConnectionStats::Notification::OnReportGenerated() + → JNetworkConnectionStats::Event::OnReportGenerated() + → JSON-RPC Event to subscribed clients + ``` + +## Critical Thunder Patterns + +### 1. SERVICE_REGISTRATION +Registers class with Thunder's factory: +```cpp +SERVICE_REGISTRATION(NetworkConnectionStats, 1, 0, 0); +``` + +### 2. Metadata Declaration +Plugin version and lifecycle info: +```cpp +static Plugin::Metadata metadata( + 1, 0, 0, // Major, Minor, Patch + {}, // Preconditions + {}, // Terminations + {} // Controls +); +``` + +### 3. Notification Pattern +```cpp +// Plugin side +Core::Sink _notification; + +// Implementation side +std::list _notifications; + +Register() { + _notifications.push_back(notification); + notification->AddRef(); // Reference counting! +} + +Unregister() { + notification->Release(); + _notifications.erase(notification); +} +``` + +### 4. Thread Safety +```cpp +Core::CriticalSection _adminLock; + +void SomeMethod() { + _adminLock.Lock(); + // Critical section + _adminLock.Unlock(); +} +``` + +### 5. Reference Counting +```cpp +_service->AddRef(); // Increment ref count +_service->Release(); // Decrement ref count +// Object destroyed when ref count reaches 0 +``` + +## Configuration File Format + +### NetworkConnectionStats.config +```json +{ + "locator": "libWPEFrameworkNetworkConnectionStats.so", // Plugin library + "classname": "NetworkConnectionStats", // Class name + "startmode": "Activated", // Auto-start + "configuration": { + "outofprocess": true, // Run out-of-process + "root": { + "mode": "Local" // Process mode + }, + "reportingInterval": 10 // Custom config + } +} +``` + +## Build Integration + +### Add to Parent CMakeLists.txt +```cmake +add_subdirectory(networkstats) +``` + +### Build Commands +```bash +cd build +cmake .. -DPLUGIN_NETWORKCONNECTIONSTATS=ON -DUSE_TELEMETRY=ON +make NetworkConnectionStats +sudo make install +``` + +## Debugging Tips + +### 1. Enable Traces +Set log level in Thunder config or via Controller plugin: +```bash +curl http://localhost:9998/jsonrpc -d '{ + "jsonrpc": "2.0", + "id": 1, + "method": "Controller.1.configuration@NetworkConnectionStats", + "params": {"trace": ["Information", "Error"]} +}' +``` + +### 2. Check Process +```bash +ps aux | grep NetworkConnectionStatsImplementation +``` + +### 3. Monitor Logs +```bash +journalctl -u wpeframework -f | grep NetworkConnectionStats +``` + +### 4. Verify Plugin Load +```bash +curl http://localhost:9998/jsonrpc -d '{ + "jsonrpc": "2.0", + "id": 1, + "method": "Controller.1.status@NetworkConnectionStats" +}' +``` + +## Common Issues & Solutions + +### Issue: Plugin doesn't load +**Check**: +- Library in correct path (`/usr/lib/wpeframework/plugins/`) +- Dependencies present (`ldd libWPEFrameworkNetworkConnectionStats.so`) +- Config file syntax valid + +### Issue: Out-of-process crashes +**Check**: +- NetworkJsonRPCProvider can reach NetworkManager plugin +- Null pointer checks in implementation +- Thread synchronization + +### Issue: Events not received +**Check**: +- Client subscribed to event +- Notification registered in implementation +- RPC connection active + +### Issue: "Interface not found" +**Check**: +- INTERFACE_AGGREGATE correctly set +- Out-of-process implementation exports interface +- SERVICE_REGISTRATION present + +## Testing Checklist + +- [ ] Plugin activates successfully +- [ ] All JSON-RPC methods callable +- [ ] Events received by subscribers +- [ ] Periodic reporting works +- [ ] Out-of-process survives restarts +- [ ] Telemetry events logged (if enabled) +- [ ] No memory leaks (valgrind) +- [ ] Thread-safe under concurrent calls +- [ ] Graceful deactivation/cleanup + +## Resources + +- **Thunder Documentation**: https://rdkcentral.github.io/Thunder/ +- **Plugin Template**: Telemetry plugin (reference implementation) +- **API Generator**: JsonGenerator.py in Thunder tools +- **Code Examples**: Other Thunder plugins in RDK repositories diff --git a/networkstats/ThunderComRPCProvider.cpp b/networkstats/ThunderComRPCProvider.cpp new file mode 100644 index 00000000..1c0c69af --- /dev/null +++ b/networkstats/ThunderComRPCProvider.cpp @@ -0,0 +1,328 @@ +/** +* If not stated otherwise in this file or this component's LICENSE +* file the following copyright and licenses apply: +* +* Copyright 2025 RDK Management +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +**/ + +#include "ThunderComRPCProvider.h" +#include + +using namespace std; + +/* @brief Constructor */ +NetworkComRPCProvider::NetworkComRPCProvider() + : m_networkManager(nullptr) + , m_isConnected(false) +{ + // TODO: Initialize COM-RPC connection +} + +/* @brief Destructor */ +NetworkComRPCProvider::~NetworkComRPCProvider() +{ + // TODO: Cleanup COM-RPC resources +} + +/* @brief Initialize COM-RPC connection to NetworkManager */ +bool NetworkComRPCProvider::initializeConnection() +{ + // TODO: Implement COM-RPC connection to NetworkManager + return false; +} + +/* @brief Release COM-RPC connection */ +void NetworkComRPCProvider::releaseConnection() +{ + // TODO: Implement COM-RPC connection cleanup +} + +/* @brief Convert COM-RPC response string to Json::Value */ +void NetworkComRPCProvider::convertToJsonResponse(const std::string& jsonString, Json::Value& response) +{ + // TODO: Implement JSON string parsing +} + +/* @brief Retrieve IPv4 address for specified interface */ +std::string NetworkComRPCProvider::getIpv4Address(std::string interface_name) +{ + // TODO: Implement using COM-RPC INetworkManager::GetIPSettings + // Initialize member variables + m_ipv4Gateway = ""; + m_ipv4PrimaryDns = ""; + return ""; +} + +/* @brief Retrieve IPv6 address for specified interface */ +std::string NetworkComRPCProvider::getIpv6Address(std::string interface_name) +{ + // TODO: Implement using COM-RPC INetworkManager::GetIPSettings + // Initialize member variables + m_ipv6Gateway = ""; + m_ipv6PrimaryDns = ""; + return ""; +} + +/* @brief Get current network connection type */ +std::string NetworkComRPCProvider::getConnectionType() +{ + // TODO: Implement using COM-RPC + return ""; +} + +/* @brief Get DNS server entries */ +std::string NetworkComRPCProvider::getDnsEntries() +{ + // TODO: Implement using COM-RPC INetworkManager::GetIPSettings + return ""; +} + +/* @brief Populate network interface data */ +void NetworkComRPCProvider::populateNetworkData() +{ + // TODO: Implement network data population using COM-RPC +} + +/* @brief Get current active interface name */ +std::string NetworkComRPCProvider::getInterface() +{ + // TODO: Implement using COM-RPC INetworkManager::GetPrimaryInterface + return ""; +} + +/* @brief Ping to gateway to check packet loss */ +bool NetworkComRPCProvider::pingToGatewayCheck(std::string endpoint, std::string ipversion, int count, int timeout) +{ + // TODO: Implement using COM-RPC INetworkManager::Ping + // Initialize member variables + m_packetLoss = ""; + m_avgRtt = ""; + return false; +} + +/* @brief Get IPv4 gateway/route address from last getIpv4Address call */ +std::string NetworkComRPCProvider::getIpv4Gateway() +{ + return m_ipv4Gateway; +} + +/* @brief Get IPv6 gateway/route address from last getIpv6Address call */ +std::string NetworkComRPCProvider::getIpv6Gateway() +{ + return m_ipv6Gateway; +} + +/* @brief Get IPv4 primary DNS from last getIpv4Address call */ +std::string NetworkComRPCProvider::getIpv4PrimaryDns() +{ + return m_ipv4PrimaryDns; +} + +/* @brief Get IPv6 primary DNS from last getIpv6Address call */ +std::string NetworkComRPCProvider::getIpv6PrimaryDns() +{ + return m_ipv6PrimaryDns; +} + +/* @brief Get packet loss from last ping call */ +std::string NetworkComRPCProvider::getPacketLoss() +{ + return m_packetLoss; +} + +/* @brief Get average RTT from last ping call */ +std::string NetworkComRPCProvider::getAvgRtt() +{ + return m_avgRtt; +} + +#ifdef TEST_MAIN +/* Test main function to validate NetworkComRPCProvider member functions */ +int main() +{ + NetworkComRPCProvider provider; + int choice; + std::string interface_name; + bool running = true; + + std::cout << "\n========================================\n"; + std::cout << "NetworkComRPCProvider Test Suite\n"; + std::cout << "========================================\n"; + + while (running) + { + std::cout << "\nMenu:\n"; + std::cout << "1. Test getIpv4Address()\n"; + std::cout << "2. Test getIpv6Address()\n"; + std::cout << "3. Test getConnectionType()\n"; + std::cout << "4. Test getDnsEntries()\n"; + std::cout << "5. Test populateNetworkData()\n"; + std::cout << "6. Test getInterface()\n"; + std::cout << "7. Test pingToGatewayCheck()\n"; + std::cout << "8. Run all tests\n"; + std::cout << "0. Exit\n"; + std::cout << "\nEnter your choice: "; + std::cin >> choice; + + switch (choice) + { + case 1: + std::cout << "\nTesting getIpv4Address()...\n"; + { + interface_name = provider.getInterface(); + std::cout << "Using interface: " << (interface_name.empty() ? "(empty)" : interface_name) << "\n"; + std::string ipv4 = provider.getIpv4Address(interface_name); + std::cout << "Result: " << (ipv4.empty() ? "(empty)" : ipv4) << "\n"; + std::string gateway = provider.getIpv4Gateway(); + std::string primaryDns = provider.getIpv4PrimaryDns(); + std::cout << "Gateway: " << (gateway.empty() ? "(empty)" : gateway) << "\n"; + std::cout << "Primary DNS: " << (primaryDns.empty() ? "(empty)" : primaryDns) << "\n"; + } + break; + + case 2: + std::cout << "\nTesting getIpv6Address()...\n"; + { + interface_name = provider.getInterface(); + std::cout << "Using interface: " << (interface_name.empty() ? "(empty)" : interface_name) << "\n"; + std::string ipv6 = provider.getIpv6Address(interface_name); + std::cout << "Result: " << (ipv6.empty() ? "(empty)" : ipv6) << "\n"; + std::string gateway = provider.getIpv6Gateway(); + std::string primaryDns = provider.getIpv6PrimaryDns(); + std::cout << "Gateway: " << (gateway.empty() ? "(empty)" : gateway) << "\n"; + std::cout << "Primary DNS: " << (primaryDns.empty() ? "(empty)" : primaryDns) << "\n"; + } + break; + + case 3: + std::cout << "\nTesting getConnectionType()...\n"; + { + std::string connType = provider.getConnectionType(); + std::cout << "Result: " << (connType.empty() ? "(empty)" : connType) << "\n"; + } + break; + + case 4: + std::cout << "\nTesting getDnsEntries()...\n"; + { + std::string dns = provider.getDnsEntries(); + std::cout << "Result: " << (dns.empty() ? "(empty)" : dns) << "\n"; + } + break; + + case 5: + std::cout << "\nTesting populateNetworkData()...\n"; + provider.populateNetworkData(); + std::cout << "Network data population completed\n"; + break; + + case 6: + std::cout << "\nTesting getInterface()...\n"; + { + std::string iface = provider.getInterface(); + std::cout << "Result: " << (iface.empty() ? "(empty)" : iface) << "\n"; + } + break; + + case 7: + std::cout << "\nTesting pingToGatewayCheck()...\n"; + { + std::string gateway; + std::string ipversion; + std::cout << "Enter gateway IP (default: 8.8.8.8): "; + std::cin.ignore(); + std::getline(std::cin, gateway); + if (gateway.empty()) gateway = "8.8.8.8"; + + std::cout << "Enter IP version (IPv4/IPv6, default: IPv4): "; + std::getline(std::cin, ipversion); + if (ipversion.empty()) ipversion = "IPv4"; + + bool success = provider.pingToGatewayCheck(gateway, ipversion, 5, 30); + std::cout << "Ping " << (success ? "successful" : "failed") << "\n"; + std::string packetLoss = provider.getPacketLoss(); + std::string avgRtt = provider.getAvgRtt(); + std::cout << "Packet Loss: " << (packetLoss.empty() ? "(empty)" : packetLoss) << "\n"; + std::cout << "Average RTT: " << (avgRtt.empty() ? "(empty)" : avgRtt) << " ms\n"; + } + break; + + case 8: + std::cout << "\n========================================\n"; + std::cout << "Running All Tests\n"; + std::cout << "========================================\n"; + + std::cout << "\n--- Test 1/7: getInterface() ---\n"; + { + std::string iface = provider.getInterface(); + std::cout << "Result: " << (iface.empty() ? "(empty)" : iface) << "\n"; + } + + std::cout << "\n--- Test 2/7: getIpv4Address() ---\n"; + { + interface_name = provider.getInterface(); + std::string ipv4 = provider.getIpv4Address(interface_name); + std::cout << "Result: " << (ipv4.empty() ? "(empty)" : ipv4) << "\n"; + } + + std::cout << "\n--- Test 3/7: getIpv6Address() ---\n"; + { + interface_name = provider.getInterface(); + std::string ipv6 = provider.getIpv6Address(interface_name); + std::cout << "Result: " << (ipv6.empty() ? "(empty)" : ipv6) << "\n"; + } + + std::cout << "\n--- Test 4/7: getConnectionType() ---\n"; + { + std::string connType = provider.getConnectionType(); + std::cout << "Result: " << (connType.empty() ? "(empty)" : connType) << "\n"; + } + + std::cout << "\n--- Test 5/7: getDnsEntries() ---\n"; + { + std::string dns = provider.getDnsEntries(); + std::cout << "Result: " << (dns.empty() ? "(empty)" : dns) << "\n"; + } + + std::cout << "\n--- Test 6/7: populateNetworkData() ---\n"; + provider.populateNetworkData(); + std::cout << "Network data population completed\n"; + + std::cout << "\n--- Test 7/7: pingToGatewayCheck() ---\n"; + { + bool success = provider.pingToGatewayCheck("8.8.8.8", "IPv4", 5, 30); + std::cout << "Ping " << (success ? "successful" : "failed") << "\n"; + } + + std::cout << "\n========================================\n"; + std::cout << "All Tests Completed\n"; + std::cout << "========================================\n"; + break; + + case 0: + std::cout << "\nExiting test suite...\n"; + running = false; + break; + + default: + std::cout << "\nInvalid choice. Please enter a number between 0-8.\n"; + break; + } + } + + return 0; +} + +#endif // TEST_MAIN diff --git a/networkstats/ThunderComRPCProvider.h b/networkstats/ThunderComRPCProvider.h new file mode 100644 index 00000000..f2aa49dc --- /dev/null +++ b/networkstats/ThunderComRPCProvider.h @@ -0,0 +1,109 @@ +/** +* If not stated otherwise in this file or this component's LICENSE +* file the following copyright and licenses apply: +* +* Copyright 2025 RDK Management +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +**/ + +#ifndef __THUNDERCOMRPCPROVIDER_H__ +#define __THUNDERCOMRPCPROVIDER_H__ + +#include "INetworkData.h" +#include +#include + +#define NETWORK_MANAGER_CALLSIGN "org.rdk.NetworkManager.1" + +class NetworkComRPCProvider : public INetworkData +{ +public: + NetworkComRPCProvider(); + virtual ~NetworkComRPCProvider(); + + /* @brief Retrieve IPv4 address for specified interface + * @param interface_name Interface name (e.g., eth0, wlan0) + * @return IPv4 address string + */ + virtual std::string getIpv4Address(std::string interface_name) override; + + /* @brief Retrieve IPv6 address for specified interface + * @param interface_name Interface name (e.g., eth0, wlan0) + * @return IPv6 address string + */ + virtual std::string getIpv6Address(std::string interface_name) override; + + /* @brief Get IPv4 gateway/route address from last getIpv4Address call */ + virtual std::string getIpv4Gateway() override; + + /* @brief Get IPv6 gateway/route address from last getIpv6Address call */ + virtual std::string getIpv6Gateway() override; + + /* @brief Get IPv4 primary DNS from last getIpv4Address call */ + virtual std::string getIpv4PrimaryDns() override; + + /* @brief Get IPv6 primary DNS from last getIpv6Address call */ + virtual std::string getIpv6PrimaryDns() override; + + /* @brief Get current network connection type */ + virtual std::string getConnectionType() override; + + /* @brief Get DNS server entries */ + virtual std::string getDnsEntries() override; + + /* @brief Populate network interface data */ + virtual void populateNetworkData() override; + + /* @brief Get current active interface name */ + virtual std::string getInterface() override; + + /* @brief Ping to gateway to check packet loss + * @param endpoint Gateway IP address to ping + * @param ipversion Either "IPv4" or "IPv6" + * @param count Number of ping packets to send + * @param timeout Timeout in seconds + * @return true if ping successful, false otherwise + */ + virtual bool pingToGatewayCheck(std::string endpoint, std::string ipversion, int count, int timeout) override; + + /* @brief Get packet loss from last ping call */ + virtual std::string getPacketLoss() override; + + /* @brief Get average RTT from last ping call */ + virtual std::string getAvgRtt() override; + +private: + /* @brief Initialize COM-RPC connection to NetworkManager */ + bool initializeConnection(); + + /* @brief Release COM-RPC connection */ + void releaseConnection(); + + /* @brief Convert COM-RPC response to Json::Value */ + void convertToJsonResponse(const std::string& jsonString, Json::Value& response); + +private: + void* m_networkManager; // Will be casted to appropriate interface + bool m_isConnected; + + // Cached data from last API calls + std::string m_ipv4Gateway; + std::string m_ipv6Gateway; + std::string m_ipv4PrimaryDns; + std::string m_ipv6PrimaryDns; + std::string m_packetLoss; + std::string m_avgRtt; +}; + +#endif /* __THUNDERCOMRPCPROVIDER_H__ */ \ No newline at end of file diff --git a/networkstats/ThunderJsonRPCProvider.cpp b/networkstats/ThunderJsonRPCProvider.cpp new file mode 100644 index 00000000..d4f8cfc4 --- /dev/null +++ b/networkstats/ThunderJsonRPCProvider.cpp @@ -0,0 +1,537 @@ +/** +* If not stated otherwise in this file or this component's LICENSE +* file the following copyright and licenses apply: +* +* Copyright 2025 RDK Management +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +**/ + +#include "ThunderJsonRPCProvider.h" +#include +#include +#include +#include +#include + +using namespace std; + +/* @brief Constructor */ +NetworkJsonRPCProvider::NetworkJsonRPCProvider() +{ + // TODO: Initialize Thunder connection +} + +/* @brief Destructor */ +NetworkJsonRPCProvider::~NetworkJsonRPCProvider() +{ + // TODO: Cleanup resources +} + +/* @brief Retrieve IPv4 address for specified interface */ +std::string NetworkJsonRPCProvider::getIpv4Address(std::string interface_name) +{ + jsonrpc::HttpClient client("http://127.0.0.1:9998/jsonrpc"); + client.AddHeader("content-type", "application/json"); + + jsonrpc::Client c(client, jsonrpc::JSONRPC_CLIENT_V2); + Json::Value params; + Json::Value result; + std::string method("org.rdk.NetworkManager.1.GetIPSettings"); + + // Set parameters + params["interface"] = interface_name; + params["ipversion"] = "IPv4"; + + // Initialize member variables + m_ipv4Gateway = ""; + m_ipv4PrimaryDns = ""; + + try { + result = c.CallMethod(method, params); + + std::string ipv4_address = ""; + + // Parse and extract all fields from JSON response + if (result.isMember("ipaddress") && result["ipaddress"].isString()) { + ipv4_address = result["ipaddress"].asString(); + std::cout << "IPv4 address retrieved: " << ipv4_address << std::endl; + } + + // Extract gateway as IPv4 Route + if (result.isMember("gateway") && result["gateway"].isString()) { + m_ipv4Gateway = result["gateway"].asString(); + std::cout << "IPv4 Route: " << m_ipv4Gateway << std::endl; + } + + // Extract primarydns as DNS entry + if (result.isMember("primarydns") && result["primarydns"].isString()) { + m_ipv4PrimaryDns = result["primarydns"].asString(); + std::cout << "DNS entry: " << m_ipv4PrimaryDns << std::endl; + } + + if (ipv4_address.empty()) { + std::cerr << "Error: 'ipaddress' field not found or invalid in response" << std::endl; + } + + return ipv4_address; + } + catch (jsonrpc::JsonRpcException& e) { + std::cerr << "JSON-RPC Exception in getIpv4Address(): " << e.what() << std::endl; + return ""; + } + catch (std::exception& e) { + std::cerr << "Exception in getIpv4Address(): " << e.what() << std::endl; + return ""; + } +} + +/* @brief Retrieve IPv6 address for specified interface */ +std::string NetworkJsonRPCProvider::getIpv6Address(std::string interface_name) +{ + jsonrpc::HttpClient client("http://127.0.0.1:9998/jsonrpc"); + client.AddHeader("content-type", "application/json"); + + jsonrpc::Client c(client, jsonrpc::JSONRPC_CLIENT_V2); + Json::Value params; + Json::Value result; + std::string method("org.rdk.NetworkManager.1.GetIPSettings"); + + // Set parameters + params["interface"] = interface_name; + params["ipversion"] = "IPv6"; + + // Initialize member variables + m_ipv6Gateway = ""; + m_ipv6PrimaryDns = ""; + + try { + result = c.CallMethod(method, params); + + std::string ipv6_address = ""; + + // Parse and extract all fields from JSON response + if (result.isMember("ipaddress") && result["ipaddress"].isString()) { + ipv6_address = result["ipaddress"].asString(); + std::cout << "IPv6 address retrieved: " << ipv6_address << std::endl; + } + + // Extract gateway as IPv6 Route + if (result.isMember("gateway") && result["gateway"].isString()) { + m_ipv6Gateway = result["gateway"].asString(); + std::cout << "IPv6 Route: " << m_ipv6Gateway << std::endl; + } + + // Extract primarydns as DNS entry + if (result.isMember("primarydns") && result["primarydns"].isString()) { + m_ipv6PrimaryDns = result["primarydns"].asString(); + std::cout << "DNS entry: " << m_ipv6PrimaryDns << std::endl; + } + + if (ipv6_address.empty()) { + std::cerr << "Error: 'ipaddress' field not found or invalid in response" << std::endl; + } + + return ipv6_address; + } + catch (jsonrpc::JsonRpcException& e) { + std::cerr << "JSON-RPC Exception in getIpv6Address(): " << e.what() << std::endl; + return ""; + } + catch (std::exception& e) { + std::cerr << "Exception in getIpv6Address(): " << e.what() << std::endl; + return ""; + } +} + +/* @brief Get current network connection type */ +std::string NetworkJsonRPCProvider::getConnectionType() +{ + std::string interface = getInterface(); + + if (interface.empty()) { + std::cerr << "Error: Unable to retrieve interface" << std::endl; + return ""; + } + + std::string connectionType; + + // Check if interface starts with "eth" + if (interface.find("eth") == 0) { + connectionType = "Ethernet"; + std::cout << "Connection type: " << connectionType << " (interface: " << interface << ")" << std::endl; + } + // Check if interface starts with "wlan" + else if (interface.find("wlan") == 0) { + connectionType = "WiFi"; + std::cout << "Connection type: " << connectionType << " (interface: " << interface << ")" << std::endl; + } + else { + connectionType = "Unknown"; + std::cout << "Connection type: " << connectionType << " (interface: " << interface << ")" << std::endl; + } + connectionType = "Ethernet"; //For testing purpose hardcoding to Ethernet + return connectionType; +} + +/* @brief Get DNS server entries */ +std::string NetworkJsonRPCProvider::getDnsEntries() +{ + // TODO: Implement DNS entries retrieval + return ""; +} + +/* @brief Populate network interface data */ +void NetworkJsonRPCProvider::populateNetworkData() +{ + // TODO: Implement network data population +} + +/* @brief Get current active interface name */ +std::string NetworkJsonRPCProvider::getInterface() +{ + jsonrpc::HttpClient client("http://127.0.0.1:9998/jsonrpc"); + client.AddHeader("content-type", "application/json"); + + jsonrpc::Client c(client, jsonrpc::JSONRPC_CLIENT_V2); + Json::Value result; + std::string method("org.rdk.NetworkManager.1.GetPrimaryInterface"); + + try { + result = c.CallMethod(method, Json::Value()); + + if (result.isMember("interface") && result["interface"].isString()) { + std::string interface = result["interface"].asString(); + std::cout << "Primary interface retrieved: " << interface << std::endl; + return "eno1"; // Hardcoded for testing + } + else { + std::cerr << "Error: 'interface' field not found or invalid in response" << std::endl; + return ""; + } + } + catch (jsonrpc::JsonRpcException& e) { + std::cerr << "JSON-RPC Exception in getInterface(): " << e.what() << std::endl; + return ""; + } + catch (std::exception& e) { + std::cerr << "Exception in getInterface(): " << e.what() << std::endl; + return ""; + } +} + +/* @brief Ping to gateway to check packet loss */ +bool NetworkJsonRPCProvider::pingToGatewayCheck(std::string endpoint, std::string ipversion, int count, int timeout) +{ + jsonrpc::HttpClient client("http://127.0.0.1:9998/jsonrpc"); + client.AddHeader("content-type", "application/json"); + + jsonrpc::Client c(client, jsonrpc::JSONRPC_CLIENT_V2); + Json::Value params; + Json::Value result; + std::string method("org.rdk.NetworkManager.1.Ping"); + + // Set parameters according to NetworkManagerPlugin.md schema + params["endpoint"] = endpoint; + params["ipversion"] = ipversion; + params["count"] = count; + params["timeout"] = timeout; + params["guid"] = "network-stats-ping"; + + // Initialize member variables + m_packetLoss = ""; + m_avgRtt = ""; + + try { + result = c.CallMethod(method, params); + + std::cout << "Ping request sent to " << endpoint << " (" << ipversion << ")" << std::endl; + + // Check if ping was successful + if (result.isMember("success") && result["success"].isBool()) { + bool success = result["success"].asBool(); + + // Extract ping statistics from JSON + if (result.isMember("packetsTransmitted") && result["packetsTransmitted"].isInt()) { + std::cout << "Packets transmitted: " << result["packetsTransmitted"].asInt() << std::endl; + } + if (result.isMember("packetsReceived") && result["packetsReceived"].isInt()) { + std::cout << "Packets received: " << result["packetsReceived"].asInt() << std::endl; + } + if (result.isMember("packetLoss") && result["packetLoss"].isString()) { + m_packetLoss = result["packetLoss"].asString(); + std::cout << "Packet loss: " << m_packetLoss << std::endl; + } + if (result.isMember("tripMin") && result["tripMin"].isString()) { + std::cout << "RTT min: " << result["tripMin"].asString() << " ms" << std::endl; + } + if (result.isMember("tripAvg") && result["tripAvg"].isString()) { + m_avgRtt = result["tripAvg"].asString(); + std::cout << "RTT avg: " << m_avgRtt << " ms" << std::endl; + } + if (result.isMember("tripMax") && result["tripMax"].isString()) { + std::cout << "RTT max: " << result["tripMax"].asString() << " ms" << std::endl; + } + + return success; + } + else { + std::cerr << "Error: 'success' field not found or invalid in response" << std::endl; + return false; + } + } + catch (jsonrpc::JsonRpcException& e) { + std::cerr << "JSON-RPC Exception in pingToGatewayCheck(): " << e.what() << std::endl; + return false; + } + catch (std::exception& e) { + std::cerr << "Exception in pingToGatewayCheck(): " << e.what() << std::endl; + return false; + } +} + +/* @brief Get IPv4 gateway/route address from last getIpv4Address call */ +std::string NetworkJsonRPCProvider::getIpv4Gateway() +{ + return m_ipv4Gateway; +} + +/* @brief Get IPv6 gateway/route address from last getIpv6Address call */ +std::string NetworkJsonRPCProvider::getIpv6Gateway() +{ + return m_ipv6Gateway; +} + +/* @brief Get IPv4 primary DNS from last getIpv4Address call */ +std::string NetworkJsonRPCProvider::getIpv4PrimaryDns() +{ + return m_ipv4PrimaryDns; +} + +/* @brief Get IPv6 primary DNS from last getIpv6Address call */ +std::string NetworkJsonRPCProvider::getIpv6PrimaryDns() +{ + return m_ipv6PrimaryDns; +} + +/* @brief Get packet loss from last ping call */ +std::string NetworkJsonRPCProvider::getPacketLoss() +{ + return m_packetLoss; +} + +/* @brief Get average RTT from last ping call */ +std::string NetworkJsonRPCProvider::getAvgRtt() +{ + return m_avgRtt; +} + +#ifdef TEST_MAIN +/* Test main function to validate NetworkJsonRPCProvider member functions */ +int main() +{ + NetworkJsonRPCProvider provider; + int choice; + std::string interface_name; + bool running = true; + + std::cout << "\n========================================\n"; + std::cout << "NetworkJsonRPCProvider Test Suite\n"; + std::cout << "========================================\n"; + + while (running) + { + std::cout << "\nMenu:\n"; + std::cout << "1. Test getIpv4Address()\n"; + std::cout << "2. Test getIpv6Address()\n"; + std::cout << "3. Test getConnectionType()\n"; + std::cout << "4. Test getDnsEntries()\n"; + std::cout << "5. Test populateNetworkData()\n"; + std::cout << "6. Test getInterface()\n"; + std::cout << "7. Test pingToGatewayCheck()\n"; + std::cout << "8. Run all tests\n"; + std::cout << "0. Exit\n"; + std::cout << "\nEnter your choice: "; + std::cin >> choice; + + switch (choice) + { + case 1: + std::cout << "\nTesting getIpv4Address()...\n"; + { + //interface_name = provider.getInterface(); + interface_name = "eno1"; // Hardcoded for testing + std::cout << "Using interface: " << (interface_name.empty() ? "(empty)" : interface_name) << "\n"; + std::string ipv4 = provider.getIpv4Address(interface_name); + std::cout << "Result: " << (ipv4.empty() ? "(empty)" : ipv4) << "\n"; + std::string gateway = provider.getIpv4Gateway(); + std::string primaryDns = provider.getIpv4PrimaryDns(); + std::cout << "Gateway: " << (gateway.empty() ? "(empty)" : gateway) << "\n"; + std::cout << "Primary DNS: " << (primaryDns.empty() ? "(empty)" : primaryDns) << "\n"; + } + break; + + case 2: + std::cout << "\nTesting getIpv6Address()...\n"; + { + //interface_name = provider.getInterface(); + interface_name = "eno1"; // Hardcoded for testing + std::cout << "Using interface: " << (interface_name.empty() ? "(empty)" : interface_name) << "\n"; + std::string ipv6 = provider.getIpv6Address(interface_name); + std::cout << "Result: " << (ipv6.empty() ? "(empty)" : ipv6) << "\n"; + std::string gateway = provider.getIpv6Gateway(); + std::string primaryDns = provider.getIpv6PrimaryDns(); + std::cout << "Gateway: " << (gateway.empty() ? "(empty)" : gateway) << "\n"; + std::cout << "Primary DNS: " << (primaryDns.empty() ? "(empty)" : primaryDns) << "\n"; + } + break; + + case 3: + std::cout << "\nTesting getConnectionType()...\n"; + { + std::string connType = provider.getConnectionType(); + std::cout << "Result: " << (connType.empty() ? "(empty)" : connType) << "\n"; + } + break; + + case 4: + std::cout << "\nTesting getDnsEntries()...\n"; + { + std::string dns = provider.getDnsEntries(); + std::cout << "Result: " << (dns.empty() ? "(empty)" : dns) << "\n"; + } + break; + + case 5: + std::cout << "\nTesting populateNetworkData()...\n"; + provider.populateNetworkData(); + std::cout << "populateNetworkData() executed successfully.\n"; + break; + + case 6: + std::cout << "\nTesting getInterface()...\n"; + { + std::string iface = provider.getInterface(); + std::cout << "Result: " << (iface.empty() ? "(empty)" : iface) << "\n"; + } + break; + + case 7: + std::cout << "\nTesting pingToGatewayCheck()...\n"; + { + std::string gateway; + std::cout << "Enter gateway IP address (or press Enter for default 8.8.8.8): "; + std::cin.ignore(); + std::getline(std::cin, gateway); + if (gateway.empty()) { + gateway = "8.8.8.8"; + } + + std::string ipver; + std::cout << "Enter IP version (IPv4/IPv6, default: IPv4): "; + std::getline(std::cin, ipver); + if (ipver.empty()) { + ipver = "IPv4"; + } + + bool result = provider.pingToGatewayCheck(gateway, ipver, 5, 30); + std::cout << "Ping result: " << (result ? "SUCCESS" : "FAILED") << "\n"; + std::string packetLoss = provider.getPacketLoss(); + std::string avgRtt = provider.getAvgRtt(); + std::cout << "Packet Loss: " << (packetLoss.empty() ? "(empty)" : packetLoss) << "\n"; + std::cout << "Average RTT: " << (avgRtt.empty() ? "(empty)" : avgRtt) << " ms\n"; + } + break; + + case 8: + std::cout << "\n========================================\n"; + std::cout << "Running all tests...\n"; + std::cout << "========================================\n"; + + std::cout << "\n[Test 1/6] getConnectionType()\n"; + { + std::string connType = provider.getConnectionType(); + std::cout << "Result: " << (connType.empty() ? "(empty)" : connType) << "\n"; + } + + std::cout << "\n[Test 2/6] getDnsEntries()\n"; + { + std::string dns = provider.getDnsEntries(); + std::cout << "Result: " << (dns.empty() ? "(empty)" : dns) << "\n"; + } + + std::cout << "\n[Test 3/6] getInterface()\n"; + { + std::string iface = provider.getInterface(); + std::cout << "Result: " << (iface.empty() ? "(empty)" : iface) << "\n"; + } + + std::cout << "\n[Test 4/6] populateNetworkData()\n"; + provider.populateNetworkData(); + std::cout << "populateNetworkData() executed successfully.\n"; + std::cout << "\n[Test 5/6] getIpv4Address()\n"; + { + std::string test_iface = provider.getInterface(); + std::cout << "Using interface: " << (test_iface.empty() ? "(empty)" : test_iface) << "\n"; + std::string ipv4 = provider.getIpv4Address(test_iface); + std::cout << "Result: " << (ipv4.empty() ? "(empty)" : ipv4) << "\n"; + std::string gateway = provider.getIpv4Gateway(); + std::string primaryDns = provider.getIpv4PrimaryDns(); + std::cout << "Gateway: " << (gateway.empty() ? "(empty)" : gateway) << "\n"; + std::cout << "Primary DNS: " << (primaryDns.empty() ? "(empty)" : primaryDns) << "\n"; + } + + std::cout << "\n[Test 6/7] getIpv6Address()\n"; + { + std::string test_iface = provider.getInterface(); + std::cout << "Using interface: " << (test_iface.empty() ? "(empty)" : test_iface) << "\n"; + std::string ipv6 = provider.getIpv6Address(test_iface); + std::cout << "Result: " << (ipv6.empty() ? "(empty)" : ipv6) << "\n"; + std::string gateway = provider.getIpv6Gateway(); + std::string primaryDns = provider.getIpv6PrimaryDns(); + std::cout << "Gateway: " << (gateway.empty() ? "(empty)" : gateway) << "\n"; + std::cout << "Primary DNS: " << (primaryDns.empty() ? "(empty)" : primaryDns) << "\n"; + } + + std::cout << "\n[Test 7/7] pingToGatewayCheck()\n"; + { + bool pingResult = provider.pingToGatewayCheck("8.8.8.8", "IPv4", 5, 30); + std::cout << "Result: " << (pingResult ? "SUCCESS" : "FAILED") << "\n"; + std::string packetLoss = provider.getPacketLoss(); + std::string avgRtt = provider.getAvgRtt(); + std::cout << "Packet Loss: " << (packetLoss.empty() ? "(empty)" : packetLoss) << "\n"; + std::cout << "Average RTT: " << (avgRtt.empty() ? "(empty)" : avgRtt) << " ms\n"; + } + + std::cout << "\n========================================\n"; + std::cout << "All tests completed.\n"; + std::cout << "========================================\n"; + break; + + case 0: + std::cout << "\nExiting test suite...\n"; + running = false; + break; + + default: + std::cout << "\nInvalid choice! Please select 0-8.\n"; + break; + } + } + + return 0; +} + +#endif // TEST_MAIN + + diff --git a/networkstats/ThunderJsonRPCProvider.h b/networkstats/ThunderJsonRPCProvider.h new file mode 100644 index 00000000..334e1800 --- /dev/null +++ b/networkstats/ThunderJsonRPCProvider.h @@ -0,0 +1,94 @@ +/** +* If not stated otherwise in this file or this component's LICENSE +* file the following copyright and licenses apply: +* +* Copyright 2025 RDK Management +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +**/ + +#ifndef __THUNDERPROVIDER_H__ +#define __THUNDERPROVIDER_H__ + +#include "INetworkData.h" +#include +//#include + + +class NetworkJsonRPCProvider : public INetworkData +{ + public: + NetworkJsonRPCProvider(); + ~NetworkJsonRPCProvider(); + /* @brief Retrieve IPv4 address for specified interface + * @param interface_name Interface name (e.g., eth0, wlan0) + * @return IPv4 address string + */ + std::string getIpv4Address(std::string interface_name); + + /* @brief Retrieve IPv6 address for specified interface + * @param interface_name Interface name (e.g., eth0, wlan0) + * @return IPv6 address string + */ + std::string getIpv6Address(std::string interface_name); + + /* @brief Get IPv4 gateway/route address from last getIpv4Address call */ + std::string getIpv4Gateway(); + + /* @brief Get IPv6 gateway/route address from last getIpv6Address call */ + std::string getIpv6Gateway(); + + /* @brief Get IPv4 primary DNS from last getIpv4Address call */ + std::string getIpv4PrimaryDns(); + + /* @brief Get IPv6 primary DNS from last getIpv6Address call */ + std::string getIpv6PrimaryDns(); + + /* @brief Get current network connection type */ + std::string getConnectionType(); + + /* @brief Get DNS server entries */ + std::string getDnsEntries(); + + /* @brief Populate network interface data */ + void populateNetworkData(); + + /* @brief Get current active interface name */ + std::string getInterface(); + + /* @brief Ping to gateway to check packet loss + * @param endpoint Gateway IP address to ping + * @param ipversion Either "IPv4" or "IPv6" + * @param count Number of ping packets to send + * @param timeout Timeout in seconds + * @return true if ping successful, false otherwise + */ + bool pingToGatewayCheck(std::string endpoint, std::string ipversion, int count, int timeout); + + /* @brief Get packet loss from last ping call */ + std::string getPacketLoss(); + + /* @brief Get average RTT from last ping call */ + std::string getAvgRtt(); + +private: + // Cached data from last API calls + std::string m_ipv4Gateway; + std::string m_ipv6Gateway; + std::string m_ipv4PrimaryDns; + std::string m_ipv6PrimaryDns; + std::string m_packetLoss; + std::string m_avgRtt; +}; + +#endif /* __THUNDERPROVIDER_H__ */ diff --git a/networkstats/ThunderProvider.cpp b/networkstats/ThunderProvider.cpp deleted file mode 100644 index 490f5553..00000000 --- a/networkstats/ThunderProvider.cpp +++ /dev/null @@ -1,79 +0,0 @@ -/** -* If not stated otherwise in this file or this component's LICENSE -* file the following copyright and licenses apply: -* -* Copyright 2025 RDK Management -* -* Licensed under the Apache License, Version 2.0 (the "License"); -* you may not use this file except in compliance with the License. -* You may obtain a copy of the License at -* -* http://www.apache.org/licenses/LICENSE-2.0 -* -* Unless required by applicable law or agreed to in writing, software -* distributed under the License is distributed on an "AS IS" BASIS, -* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -* See the License for the specific language governing permissions and -* limitations under the License. -**/ - -#include -#include -#include "ThunderProvider.h" -#include - -/* @brief Constructor */ -NetworkThunderProvider::NetworkThunderProvider() -{ - // TODO: Initialize Thunder connection -} - -/* @brief Destructor */ -NetworkThunderProvider::~NetworkThunderProvider() -{ - // TODO: Cleanup resources -} - -/* @brief Retrieve IPv4 address for specified interface */ -std::string NetworkThunderProvider::getIpv4Address(std::string interface_name) -{ - // TODO: Implement IPv4 address retrieval - return ""; -} - -/* @brief Retrieve IPv6 address for specified interface */ -std::string NetworkThunderProvider::getIpv6Address(std::string interface_name) -{ - // TODO: Implement IPv6 address retrieval - return ""; -} - -/* @brief Get current network connection type */ -std::string NetworkThunderProvider::getConnectionType() -{ - // TODO: Implement connection type retrieval - return ""; -} - -/* @brief Get DNS server entries */ -std::string NetworkThunderProvider::getDnsEntries() -{ - // TODO: Implement DNS entries retrieval - return ""; -} - -/* @brief Populate network interface data */ -void NetworkThunderProvider::populateNetworkData() -{ - // TODO: Implement network data population -} - -/* @brief Get current active interface name */ -std::string NetworkThunderProvider::getInterface() -{ - // TODO: Implement interface name retrieval - return ""; -} - - - diff --git a/networkstats/ThunderProvider.h b/networkstats/ThunderProvider.h deleted file mode 100644 index ab5f3ce2..00000000 --- a/networkstats/ThunderProvider.h +++ /dev/null @@ -1,52 +0,0 @@ -/** -* If not stated otherwise in this file or this component's LICENSE -* file the following copyright and licenses apply: -* -* Copyright 2025 RDK Management -* -* Licensed under the Apache License, Version 2.0 (the "License"); -* you may not use this file except in compliance with the License. -* You may obtain a copy of the License at -* -* http://www.apache.org/licenses/LICENSE-2.0 -* -* Unless required by applicable law or agreed to in writing, software -* distributed under the License is distributed on an "AS IS" BASIS, -* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -* See the License for the specific language governing permissions and -* limitations under the License. -**/ - -#ifndef __THUNDERPROVIDER_H__ -#define __THUNDERPROVIDER_H__ - -#include "INetworkData.h" -#include - - -class NetworkThunderProvider : public INetworkData -{ - public: - NetworkThunderProvider(); - ~NetworkThunderProvider(); - /* @brief Retrieve IPv4 address for specified interface */ - std::string getIpv4Address(std::string interface_name); - - /* @brief Retrieve IPv6 address for specified interface */ - std::string getIpv6Address(std::string interface_name); - - /* @brief Get current network connection type */ - std::string getConnectionType(); - - /* @brief Get DNS server entries */ - std::string getDnsEntries(); - - /* @brief Populate network interface data */ - void populateNetworkData(); - - /* @brief Get current active interface name */ - std::string getInterface(); - -}; - -#endif /* __THUNDERPROVIDER_H__ */ diff --git a/networkstats/build.sh b/networkstats/build.sh new file mode 100755 index 00000000..c360ab79 --- /dev/null +++ b/networkstats/build.sh @@ -0,0 +1,77 @@ +#!/bin/bash +# Build script for ThunderJsonRPCProvider Test Application +# Copyright 2025 RDK Management + +set -e + +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +cd "$SCRIPT_DIR" + +echo "========================================" +echo "ThunderJsonRPCProvider Test Build" +echo "========================================" +echo "" + +# Check for required dependencies +check_dependency() { + local lib=$1 + local header=$2 + + if [ -f "/usr/include/$header" ] || [ -f "/usr/local/include/$header" ] || [ -f "/opt/homebrew/include/$header" ]; then + echo "✓ Found: $lib" + return 0 + else + echo "✗ Missing: $lib" + return 1 + fi +} + +echo "Checking dependencies..." +all_deps_found=true + +if ! check_dependency "jsoncpp" "json/json.h"; then + all_deps_found=false +fi + +if ! check_dependency "libjsonrpccpp" "jsonrpccpp/client.h"; then + all_deps_found=false +fi + +echo "" + +if [ "$all_deps_found" = false ]; then + echo "❌ Missing required dependencies!" + echo "" + echo "To install on Ubuntu/Debian:" + echo " sudo apt-get install libjsonrpccpp-dev libjsoncpp-dev" + echo "" + echo "To install on macOS:" + echo " brew install jsoncpp libjson-rpc-cpp" + echo "" + exit 1 +fi + +echo "✓ All dependencies found" +echo "" +echo "Building test application..." + +# Compile +g++ -DTEST_MAIN -std=c++11 -Wall -Wno-deprecated-declarations -g \ + -I. -I/usr/include -I/usr/local/include -I/opt/homebrew/include \ + ThunderJsonRPCProvider.cpp \ + -o thunder-jsonrpc-provider-test \ + -L/usr/lib -L/usr/lib/x86_64-linux-gnu -L/usr/local/lib -L/opt/homebrew/lib \ + -ljsonrpccpp-client -ljsonrpccpp-common -ljsoncpp + +if [ $? -eq 0 ]; then + echo "✓ Build successful!" + echo "" + echo "Test application: ./thunder-jsonrpc-provider-test" + echo "" + echo "To run the test:" + echo " ./thunder-jsonrpc-provider-test" + echo "" +else + echo "❌ Build failed!" + exit 1 +fi