From 2893ed20d427e90e562994aba6b0ca257bc0aab7 Mon Sep 17 00:00:00 2001 From: Bryan Biedenkapp Date: Tue, 29 Oct 2024 09:58:47 -0400 Subject: [PATCH 01/23] ensure multiple instances of a peer-link FNE don't clobber each other when receiving peer-link ACL; --- src/fne/network/PeerNetwork.cpp | 27 ++++++++++++++++++++++++--- 1 file changed, 24 insertions(+), 3 deletions(-) diff --git a/src/fne/network/PeerNetwork.cpp b/src/fne/network/PeerNetwork.cpp index 8c75f8fb..e8af9f02 100644 --- a/src/fne/network/PeerNetwork.cpp +++ b/src/fne/network/PeerNetwork.cpp @@ -199,7 +199,14 @@ void PeerNetwork::userPacketHandler(uint32_t peerId, FrameQueue::OpcodePair opco ::memcpy(str, decompressed, decompressedLen); str[decompressedLen] = 0; // null termination - std::string filename = "/tmp/talkgroup_rules.yml"; + // randomize filename + std::ostringstream s; + std::random_device rd; + std::mt19937 mt(rd()); + std::uniform_int_distribution dist(0x00U, 0xFFFFFFFFU); + s << "/tmp/talkgroup_rules.yml." << dist(mt); + + std::string filename = s.str(); std::ofstream file(filename, std::ofstream::out); if (file.fail()) { LogError(LOG_NET, "Cannot open the talkgroup ID lookup file - %s", filename.c_str()); @@ -324,7 +331,14 @@ tid_lookup_cleanup: ::memcpy(str, decompressed, decompressedLen); str[decompressedLen] = 0; // null termination - std::string filename = "/tmp/rid_acl.dat"; + // randomize filename + std::ostringstream s; + std::random_device rd; + std::mt19937 mt(rd()); + std::uniform_int_distribution dist(0x00U, 0xFFFFFFFFU); + s << "/tmp/rid_acl.dat." << dist(mt); + + std::string filename = s.str(); std::ofstream file(filename, std::ofstream::out); if (file.fail()) { LogError(LOG_NET, "Cannot open the radio ID lookup file - %s", filename.c_str()); @@ -449,7 +463,14 @@ rid_lookup_cleanup: ::memcpy(str, decompressed, decompressedLen); str[decompressedLen] = 0; // null termination - std::string filename = "/tmp/peer_list.dat"; + // randomize filename + std::ostringstream s; + std::random_device rd; + std::mt19937 mt(rd()); + std::uniform_int_distribution dist(0x00U, 0xFFFFFFFFU); + s << "/tmp/peer_list.dat." << dist(mt); + + std::string filename = s.str(); std::ofstream file(filename, std::ofstream::out); if (file.fail()) { LogError(LOG_NET, "Cannot open the peer ID lookup file - %s", filename.c_str()); From bc05a4d06e9a2e41863ab8dd4f53c8c3ecd88e19 Mon Sep 17 00:00:00 2001 From: Bryan Biedenkapp Date: Wed, 30 Oct 2024 11:58:05 -0400 Subject: [PATCH 02/23] add mutex locking for the peer status, to prevent segfaults; --- src/sysview/HostWS.cpp | 3 +++ src/sysview/NodeStatusWnd.h | 3 +++ src/sysview/network/PeerNetwork.cpp | 7 +++++++ src/sysview/network/PeerNetwork.h | 13 +++++++++++++ 4 files changed, 26 insertions(+) diff --git a/src/sysview/HostWS.cpp b/src/sysview/HostWS.cpp index c89c6097..8295dca3 100644 --- a/src/sysview/HostWS.cpp +++ b/src/sysview/HostWS.cpp @@ -188,7 +188,10 @@ int HostWS::run() if (peerStatusUpdate.isRunning() && peerStatusUpdate.hasExpired()) { peerStatusUpdate.start(); + getNetwork()->lockPeerStatus(); std::map peerStatus(getNetwork()->peerStatus.begin(), getNetwork()->peerStatus.end()); + getNetwork()->unlockPeerStatus(); + for (auto entry : peerStatus) { json::object wsObj = json::object(); std::string type = "peer_status"; diff --git a/src/sysview/NodeStatusWnd.h b/src/sysview/NodeStatusWnd.h index e57138b0..c2e9a16b 100644 --- a/src/sysview/NodeStatusWnd.h +++ b/src/sysview/NodeStatusWnd.h @@ -452,7 +452,10 @@ public: void update() { const auto& rootWidget = getRootWidget(); + getNetwork()->lockPeerStatus(); std::map peerStatus(getNetwork()->peerStatus.begin(), getNetwork()->peerStatus.end()); + getNetwork()->unlockPeerStatus(); + for (auto entry : peerStatus) { uint32_t peerId = entry.first; json::object peerObj = entry.second; diff --git a/src/sysview/network/PeerNetwork.cpp b/src/sysview/network/PeerNetwork.cpp index 94567fb4..ddc7fdf1 100644 --- a/src/sysview/network/PeerNetwork.cpp +++ b/src/sysview/network/PeerNetwork.cpp @@ -19,6 +19,12 @@ using namespace network; #include +// --------------------------------------------------------------------------- +// Static Class Members +// --------------------------------------------------------------------------- + +std::mutex PeerNetwork::m_peerStatusMutex; + // --------------------------------------------------------------------------- // Public Class Members // --------------------------------------------------------------------------- @@ -89,6 +95,7 @@ void PeerNetwork::userPacketHandler(uint32_t peerId, FrameQueue::OpcodePair opco } json::object obj = v.get(); + std::lock_guard lock(m_peerStatusMutex); peerStatus[peerId] = obj; } break; diff --git a/src/sysview/network/PeerNetwork.h b/src/sysview/network/PeerNetwork.h index 8620b19a..1321fcce 100644 --- a/src/sysview/network/PeerNetwork.h +++ b/src/sysview/network/PeerNetwork.h @@ -25,6 +25,7 @@ #include #include +#include namespace network { @@ -56,6 +57,15 @@ namespace network PeerNetwork(const std::string& address, uint16_t port, uint16_t localPort, uint32_t peerId, const std::string& password, bool duplex, bool debug, bool dmr, bool p25, bool nxdn, bool slot1, bool slot2, bool allowActivityTransfer, bool allowDiagnosticTransfer, bool updateLookup, bool saveLookup); + /** + * @brief Helper to lock the peer status mutex. + */ + void lockPeerStatus() { m_peerStatusMutex.lock(); } + /** + * @brief Helper to unlock the peer status mutex. + */ + void unlockPeerStatus() { m_peerStatusMutex.unlock(); } + /** * @brief Map of peer status. */ @@ -78,6 +88,9 @@ namespace network * @returns bool True, if configuration was sent, otherwise false. */ bool writeConfig() override; + + private: + static std::mutex m_peerStatusMutex; }; } // namespace network From 25c3babb4be39241bf3c463cd283f1996beb41b2 Mon Sep 17 00:00:00 2001 From: Bryan Biedenkapp Date: Wed, 30 Oct 2024 14:57:20 -0400 Subject: [PATCH 03/23] when a NAK is directly written to a end-point address log the address; --- src/fne/network/FNENetwork.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/fne/network/FNENetwork.cpp b/src/fne/network/FNENetwork.cpp index 8c2ff3ca..d357ad3b 100644 --- a/src/fne/network/FNENetwork.cpp +++ b/src/fne/network/FNENetwork.cpp @@ -10,6 +10,7 @@ #include "fne/Defines.h" #include "common/edac/SHA256.h" #include "common/network/json/json.h" +#include "common/network/tcp/Socket.h" #include "common/zlib/zlib.h" #include "common/Log.h" #include "common/Utils.h" @@ -2396,6 +2397,7 @@ bool FNENetwork::writePeerNAK(uint32_t peerId, const char* tag, NET_CONN_NAK_REA __SET_UINT16B((uint16_t)reason, buffer, 10U); // Reason logPeerNAKReason(peerId, tag, reason); + LogWarning(LOG_NET, "PEER %u NAK %s, %s:%u", peerId, tag, (uint16_t)reason, tcp::Socket::address(addr).c_str(), tcp::Socket::port(addr)); return m_frameQueue->write(buffer, 12U, createStreamId(), peerId, m_peerId, { NET_FUNC::NAK, NET_SUBFUNC::NOP }, 0U, addr, addrLen); } From e1ecdffe219f5d09476c58dd687b567e71c6917b Mon Sep 17 00:00:00 2001 From: Bryan Biedenkapp Date: Wed, 30 Oct 2024 15:12:10 -0400 Subject: [PATCH 04/23] revert previous change; --- src/fne/network/FNENetwork.cpp | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/fne/network/FNENetwork.cpp b/src/fne/network/FNENetwork.cpp index d357ad3b..8c2ff3ca 100644 --- a/src/fne/network/FNENetwork.cpp +++ b/src/fne/network/FNENetwork.cpp @@ -10,7 +10,6 @@ #include "fne/Defines.h" #include "common/edac/SHA256.h" #include "common/network/json/json.h" -#include "common/network/tcp/Socket.h" #include "common/zlib/zlib.h" #include "common/Log.h" #include "common/Utils.h" @@ -2397,7 +2396,6 @@ bool FNENetwork::writePeerNAK(uint32_t peerId, const char* tag, NET_CONN_NAK_REA __SET_UINT16B((uint16_t)reason, buffer, 10U); // Reason logPeerNAKReason(peerId, tag, reason); - LogWarning(LOG_NET, "PEER %u NAK %s, %s:%u", peerId, tag, (uint16_t)reason, tcp::Socket::address(addr).c_str(), tcp::Socket::port(addr)); return m_frameQueue->write(buffer, 12U, createStreamId(), peerId, m_peerId, { NET_FUNC::NAK, NET_SUBFUNC::NOP }, 0U, addr, addrLen); } From df2b91ec6098266eb791d9542e26da8e65ab12dc Mon Sep 17 00:00:00 2001 From: Bryan Biedenkapp Date: Wed, 30 Oct 2024 15:19:43 -0400 Subject: [PATCH 05/23] recommit previous log message change (working this time); --- src/fne/network/FNENetwork.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/src/fne/network/FNENetwork.cpp b/src/fne/network/FNENetwork.cpp index 8c2ff3ca..623e32bb 100644 --- a/src/fne/network/FNENetwork.cpp +++ b/src/fne/network/FNENetwork.cpp @@ -2396,6 +2396,7 @@ bool FNENetwork::writePeerNAK(uint32_t peerId, const char* tag, NET_CONN_NAK_REA __SET_UINT16B((uint16_t)reason, buffer, 10U); // Reason logPeerNAKReason(peerId, tag, reason); + LogWarning(LOG_NET, "PEER %u NAK %s, %s:%u", peerId, tag, udp::Socket::address(addr).c_str(), udp::Socket::port(addr)); return m_frameQueue->write(buffer, 12U, createStreamId(), peerId, m_peerId, { NET_FUNC::NAK, NET_SUBFUNC::NOP }, 0U, addr, addrLen); } From 91d4d689171f7540e9d92d53a769273f43938da0 Mon Sep 17 00:00:00 2001 From: Bryan Biedenkapp Date: Wed, 30 Oct 2024 15:33:05 -0400 Subject: [PATCH 06/23] reformat log message; --- src/fne/network/FNENetwork.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/fne/network/FNENetwork.cpp b/src/fne/network/FNENetwork.cpp index 623e32bb..81f0c44a 100644 --- a/src/fne/network/FNENetwork.cpp +++ b/src/fne/network/FNENetwork.cpp @@ -2396,7 +2396,7 @@ bool FNENetwork::writePeerNAK(uint32_t peerId, const char* tag, NET_CONN_NAK_REA __SET_UINT16B((uint16_t)reason, buffer, 10U); // Reason logPeerNAKReason(peerId, tag, reason); - LogWarning(LOG_NET, "PEER %u NAK %s, %s:%u", peerId, tag, udp::Socket::address(addr).c_str(), udp::Socket::port(addr)); + LogWarning(LOG_NET, "PEER %u NAK %s -> %s:%u", peerId, tag, udp::Socket::address(addr).c_str(), udp::Socket::port(addr)); return m_frameQueue->write(buffer, 12U, createStreamId(), peerId, m_peerId, { NET_FUNC::NAK, NET_SUBFUNC::NOP }, 0U, addr, addrLen); } From e91f61ecb671481c4955dc00df7c39cca3bebdd5 Mon Sep 17 00:00:00 2001 From: Bryan Biedenkapp Date: Wed, 30 Oct 2024 15:44:11 -0400 Subject: [PATCH 07/23] force full network reconnect in most NAK cases; --- src/host/network/Network.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/host/network/Network.cpp b/src/host/network/Network.cpp index 23c3a2f9..0a5699bb 100644 --- a/src/host/network/Network.cpp +++ b/src/host/network/Network.cpp @@ -528,7 +528,7 @@ void Network::clock(uint32_t ms) } } - if (m_status == NET_STAT_RUNNING || (reason == NET_CONN_NAK_FNE_MAX_CONN)) { + if (m_status == NET_STAT_RUNNING && (reason == NET_CONN_NAK_FNE_MAX_CONN)) { LogWarning(LOG_NET, "PEER %u master NAK; attemping to relogin, remotePeerId = %u", m_peerId, rtpHeader.getSSRC()); m_status = NET_STAT_WAITING_LOGIN; m_timeoutTimer.start(); From eedd0674890fcdf84dc17797133cd46a776bc702 Mon Sep 17 00:00:00 2001 From: Bryan Biedenkapp Date: Thu, 31 Oct 2024 12:02:49 -0400 Subject: [PATCH 08/23] correct order of initialization, and ensure m_stop is set to false by default; --- src/common/lookups/TalkgroupRulesLookup.cpp | 1 + src/common/lookups/TalkgroupRulesLookup.h | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/src/common/lookups/TalkgroupRulesLookup.cpp b/src/common/lookups/TalkgroupRulesLookup.cpp index b7ac7f4d..93240fb5 100644 --- a/src/common/lookups/TalkgroupRulesLookup.cpp +++ b/src/common/lookups/TalkgroupRulesLookup.cpp @@ -35,6 +35,7 @@ TalkgroupRulesLookup::TalkgroupRulesLookup(const std::string& filename, uint32_t m_reloadTime(reloadTime), m_rules(), m_acl(acl), + m_stop(false), m_groupHangTime(5U), m_sendTalkgroups(false), m_groupVoice() diff --git a/src/common/lookups/TalkgroupRulesLookup.h b/src/common/lookups/TalkgroupRulesLookup.h index 564baec9..0ed25811 100644 --- a/src/common/lookups/TalkgroupRulesLookup.h +++ b/src/common/lookups/TalkgroupRulesLookup.h @@ -605,9 +605,9 @@ namespace lookups yaml::Node m_rules; bool m_acl; + bool m_stop; static std::mutex m_mutex; - bool m_stop; /** * @brief Loads the table from the passed lookup table file. From f9047ac8315d2ea129c2307765d3c60d635b0fc4 Mon Sep 17 00:00:00 2001 From: Bryan Biedenkapp Date: Thu, 31 Oct 2024 14:49:24 -0400 Subject: [PATCH 09/23] update helper tools; --- tools/colorize-fne.sh | 34 ++++++++++++++++++++++++++++++++++ tools/dvm-watchdog.sh | 2 -- tools/fne-watchdog.sh | 2 -- tools/start-dvm-fne.sh | 2 -- tools/start-dvm.sh | 2 -- tools/stop-dvm.sh | 2 -- tools/stop-watchdog.sh | 2 -- 7 files changed, 34 insertions(+), 12 deletions(-) create mode 100755 tools/colorize-fne.sh diff --git a/tools/colorize-fne.sh b/tools/colorize-fne.sh new file mode 100755 index 00000000..578ea6d0 --- /dev/null +++ b/tools/colorize-fne.sh @@ -0,0 +1,34 @@ +#!/bin/bash +# SPDX-License-Identifier: GPL-2.0-only +#/** +#* Digital Voice Modem - Host Software +#* GPLv2 Open Source. Use is subject to license terms. +#* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. +#* +#*/ +#/* +#* Copyright (C) 2022 by Bryan Biedenkapp N2PLL +#* +#* This program is free software; you can redistribute it and/or modify +#* it under the terms of the GNU General Public License as published by +#* the Free Software Foundation; either version 2 of the License, or +#* (at your option) any later version. +#* +#* This program is distributed in the hope that it will be useful, +#* but WITHOUT ANY WARRANTY; without even the implied warranty of +#* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +#* GNU General Public License for more details. +#* +#* You should have received a copy of the GNU General Public License +#* along with this program; if not, write to the Free Software +#* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +#*/ +LOG_COLOR="s#W:#\x1b[0m\x1b[1m\x1b[33m&#; s#E:#\x1b[0m\x1b[1m\x1b[31m&#; s#M:#\x1b[0m&#; s#I:#\x1b[0m&#; s#D:#\x1b[1m\x1b[34m&#; s#U:#\x1b[44m\x1b[1m\x1b[33m&#;" + +P25_COLOR="s#LDU#\x1b[36m&#; s#TDU#\x1b[0m\x1b[32m&#; s#HDU#\x1b[0m\x1b[32m&#; s#TSDU#\x1b[0m\x1b[35m&#" +AFF_COLOR="s#Affiliations#\x1b[1m\x1b[36m&#;" + +RF_HIGHLIGHT="s#(RF)#\x1b[1m\x1b[34m&\x1b[0m#;" +NET_HIGHLIGHT="s#(NET)#\x1b[1m\x1b[36m&\x1b[0m#;" + +sed "${LOG_COLOR}; ${RF_HIGHLIGHT}; ${NET_HIGHLIGHT}; ${P25_COLOR}; ${AFF_COLOR}" \ No newline at end of file diff --git a/tools/dvm-watchdog.sh b/tools/dvm-watchdog.sh index 231041ea..c46a9104 100755 --- a/tools/dvm-watchdog.sh +++ b/tools/dvm-watchdog.sh @@ -5,8 +5,6 @@ #* GPLv2 Open Source. Use is subject to license terms. #* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. #* -#* @package DVM / Host Software -#* #*/ #/* #* Copyright (C) 2022 by Bryan Biedenkapp N2PLL diff --git a/tools/fne-watchdog.sh b/tools/fne-watchdog.sh index 3e81c0ef..28fe6482 100755 --- a/tools/fne-watchdog.sh +++ b/tools/fne-watchdog.sh @@ -5,8 +5,6 @@ #* GPLv2 Open Source. Use is subject to license terms. #* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. #* -#* @package DVM / Host Software -#* #*/ #/* #* Copyright (C) 2022 by Bryan Biedenkapp N2PLL diff --git a/tools/start-dvm-fne.sh b/tools/start-dvm-fne.sh index b248eec2..8e4f05b0 100755 --- a/tools/start-dvm-fne.sh +++ b/tools/start-dvm-fne.sh @@ -5,8 +5,6 @@ #* GPLv2 Open Source. Use is subject to license terms. #* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. #* -#* @package DVM / Host Software -#* #*/ #/* #* Copyright (C) 2022 by Bryan Biedenkapp N2PLL diff --git a/tools/start-dvm.sh b/tools/start-dvm.sh index 252fbfba..73d72006 100755 --- a/tools/start-dvm.sh +++ b/tools/start-dvm.sh @@ -5,8 +5,6 @@ #* GPLv2 Open Source. Use is subject to license terms. #* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. #* -#* @package DVM / Host Software -#* #*/ #/* #* Copyright (C) 2022 by Bryan Biedenkapp N2PLL diff --git a/tools/stop-dvm.sh b/tools/stop-dvm.sh index 0265e9f3..60a0e32e 100755 --- a/tools/stop-dvm.sh +++ b/tools/stop-dvm.sh @@ -5,8 +5,6 @@ #* GPLv2 Open Source. Use is subject to license terms. #* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. #* -#* @package DVM / Host Software -#* #*/ #/* #* Copyright (C) 2022 by Bryan Biedenkapp N2PLL diff --git a/tools/stop-watchdog.sh b/tools/stop-watchdog.sh index 3852b474..022e2407 100755 --- a/tools/stop-watchdog.sh +++ b/tools/stop-watchdog.sh @@ -5,8 +5,6 @@ #* GPLv2 Open Source. Use is subject to license terms. #* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. #* -#* @package DVM / Host Software -#* #*/ #/* #* Copyright (C) 2022 by Bryan Biedenkapp N2PLL From f7c66bdef0d8f52e70ef863a8788635445e53e13 Mon Sep 17 00:00:00 2001 From: Bryan Biedenkapp Date: Thu, 31 Oct 2024 20:58:49 -0400 Subject: [PATCH 10/23] just for Faulty add tarball_notools; --- CMakeLists.txt | 74 ++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 74 insertions(+) diff --git a/CMakeLists.txt b/CMakeLists.txt index 27c06ad6..33fef823 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -440,6 +440,80 @@ if (NOT TARGET tarball) endif (ENABLE_TUI_SUPPORT AND (NOT DISABLE_TUI_APPS)) endif (NOT TARGET tarball) +# +# Custom make target to perform a tarball packaging. This will ultimately contain the same type of pathing +# the non-standard legacy install to "/opt/dvm" does. +# +if (NOT TARGET tarball_notools) + set(CMAKE_INSTALL_PREFIX_TARBALL "tar_build") + if (ENABLE_TUI_SUPPORT AND (NOT DISABLE_TUI_APPS)) + add_custom_target(tarball_notools + COMMAND rm -rf ${CMAKE_INSTALL_PREFIX_TARBALL} + COMMAND mkdir -p ${CMAKE_INSTALL_PREFIX_TARBALL}/dvm/bin + COMMAND mkdir -p ${CMAKE_INSTALL_PREFIX_TARBALL}/dvm/log + COMMAND touch ${CMAKE_INSTALL_PREFIX_TARBALL}/dvm/log/INCLUDE_DIRECTORY + COMMAND cp -v dvmhost ${CMAKE_INSTALL_PREFIX_TARBALL}/dvm/bin + COMMAND cp -v dvmcmd ${CMAKE_INSTALL_PREFIX_TARBALL}/dvm/bin + COMMAND cp -v dvmmon ${CMAKE_INSTALL_PREFIX_TARBALL}/dvm/bin + COMMAND cp -v sysview ${CMAKE_INSTALL_PREFIX_TARBALL}/dvm/bin + COMMAND cp -v tged ${CMAKE_INSTALL_PREFIX_TARBALL}/dvm/bin + COMMAND cp -v dvmfne ${CMAKE_INSTALL_PREFIX_TARBALL}/dvm/bin + COMMAND cp -v dvmbridge ${CMAKE_INSTALL_PREFIX_TARBALL}/dvm/bin + COMMAND cp -v ../configs/*.yml ${CMAKE_INSTALL_PREFIX_TARBALL}/dvm + COMMAND mkdir -p ${CMAKE_INSTALL_PREFIX_TARBALL}/dvm/schema + COMMAND cp -v ../configs/schema/*.json ${CMAKE_INSTALL_PREFIX_TARBALL}/dvm/schema + COMMAND cp -v ../configs/*.dat ${CMAKE_INSTALL_PREFIX_TARBALL}/dvm + COMMAND mkdir -p ${CMAKE_INSTALL_PREFIX_TARBALL}/dvm/fw + COMMAND if [ -e dvm-firmware_f4.elf ]\; then cp -v dvm-firmware_f4.elf ${CMAKE_INSTALL_PREFIX_TARBALL}/dvm/fw\; fi + COMMAND if [ -e dvm-firmware_f4.bin ]\; then cp -v dvm-firmware_f4.bin ${CMAKE_INSTALL_PREFIX_TARBALL}/dvm/fw\; fi + COMMAND if [ -e dvm-firmware_f4-pog.elf ]\; then cp -v dvm-firmware_f4-pog.elf ${CMAKE_INSTALL_PREFIX_TARBALL}/dvm/fw\; fi + COMMAND if [ -e dvm-firmware_f4-pog.bin ]\; then cp -v dvm-firmware_f4-pog.bin ${CMAKE_INSTALL_PREFIX_TARBALL}/dvm/fw\; fi + COMMAND if [ -e dvm-firmware_eda.elf ]\; then cp -v dvm-firmware_eda.elf ${CMAKE_INSTALL_PREFIX_TARBALL}/dvm/fw\; fi + COMMAND if [ -e dvm-firmware_eda.bin ]\; then cp -v dvm-firmware_eda.bin ${CMAKE_INSTALL_PREFIX_TARBALL}/dvm/fw\; fi + COMMAND if [ -e dvm-firmware_f4-dvmv1.elf ]\; then cp -v dvm-firmware_f4-dvmv1.elf ${CMAKE_INSTALL_PREFIX_TARBALL}/dvm/fw\; fi + COMMAND if [ -e dvm-firmware_f4-dvmv1.bin ]\; then cp -v dvm-firmware_f4-dvmv1.bin ${CMAKE_INSTALL_PREFIX_TARBALL}/dvm/fw\; fi + COMMAND if [ -e dvm-firmware_due.elf ]\; then cp -v dvm-firmware_due.elf ${CMAKE_INSTALL_PREFIX_TARBALL}/dvm/fw\; fi + COMMAND if [ -e dvm-firmware_due.bin ]\; then cp -v dvm-firmware_due.bin ${CMAKE_INSTALL_PREFIX_TARBALL}/dvm/fw\; fi + COMMAND if [ -e dvm-firmware-hs_f1.elf ]\; then cp -v dvm-firmware-hs_f1.elf ${CMAKE_INSTALL_PREFIX_TARBALL}/dvm/fw\; fi + COMMAND if [ -e dvm-firmware-hs_f1.bin ]\; then cp -v dvm-firmware-hs_f1.bin ${CMAKE_INSTALL_PREFIX_TARBALL}/dvm/fw\; fi + COMMAND if [ -e DVM-V24-stm32f103.elf ]\; then cp -v DVM-V24-stm32f103.elf ${CMAKE_INSTALL_PREFIX_TARBALL}/dvm/fw\; fi + COMMAND if [ -e DVM-V24-stm32f103.bin ]\; then cp -v DVM-V24-stm32f103.bin ${CMAKE_INSTALL_PREFIX_TARBALL}/dvm/fw\; fi + COMMAND cd ${CMAKE_INSTALL_PREFIX_TARBALL} && tar czvf ../dvmhost_${CPACK_DEBIAN_PACKAGE_VERSION}_${ARCH}.tar.gz * + COMMAND rm -rf ${CMAKE_INSTALL_PREFIX_TARBALL}) + else() + add_custom_target(tarball_notools + COMMAND rm -rf ${CMAKE_INSTALL_PREFIX_TARBALL} + COMMAND mkdir -p ${CMAKE_INSTALL_PREFIX_TARBALL}/dvm/bin + COMMAND mkdir -p ${CMAKE_INSTALL_PREFIX_TARBALL}/dvm/log + COMMAND touch ${CMAKE_INSTALL_PREFIX_TARBALL}/dvm/log/INCLUDE_DIRECTORY + COMMAND cp -v dvmhost ${CMAKE_INSTALL_PREFIX_TARBALL}/dvm/bin + COMMAND cp -v dvmcmd ${CMAKE_INSTALL_PREFIX_TARBALL}/dvm/bin + COMMAND cp -v dvmfne ${CMAKE_INSTALL_PREFIX_TARBALL}/dvm/bin + COMMAND cp -v dvmbridge ${CMAKE_INSTALL_PREFIX_TARBALL}/dvm/bin + COMMAND cp -v ../configs/*.yml ${CMAKE_INSTALL_PREFIX_TARBALL}/dvm + COMMAND mkdir -p ${CMAKE_INSTALL_PREFIX_TARBALL}/dvm/schema + COMMAND cp -v ../configs/schema/*.json ${CMAKE_INSTALL_PREFIX_TARBALL}/dvm/schema + COMMAND cp -v ../configs/*.dat ${CMAKE_INSTALL_PREFIX_TARBALL}/dvm + COMMAND mkdir -p ${CMAKE_INSTALL_PREFIX_TARBALL}/dvm/fw + COMMAND if [ -e dvm-firmware_f4.elf ]\; then cp -v dvm-firmware_f4.elf ${CMAKE_INSTALL_PREFIX_TARBALL}/dvm/fw\; fi + COMMAND if [ -e dvm-firmware_f4.bin ]\; then cp -v dvm-firmware_f4.bin ${CMAKE_INSTALL_PREFIX_TARBALL}/dvm/fw\; fi + COMMAND if [ -e dvm-firmware_f4-pog.elf ]\; then cp -v dvm-firmware_f4-pog.elf ${CMAKE_INSTALL_PREFIX_TARBALL}/dvm/fw\; fi + COMMAND if [ -e dvm-firmware_f4-pog.bin ]\; then cp -v dvm-firmware_f4-pog.bin ${CMAKE_INSTALL_PREFIX_TARBALL}/dvm/fw\; fi + COMMAND if [ -e dvm-firmware_eda.elf ]\; then cp -v dvm-firmware_eda.elf ${CMAKE_INSTALL_PREFIX_TARBALL}/dvm/fw\; fi + COMMAND if [ -e dvm-firmware_eda.bin ]\; then cp -v dvm-firmware_eda.bin ${CMAKE_INSTALL_PREFIX_TARBALL}/dvm/fw\; fi + COMMAND if [ -e dvm-firmware_f4-dvmv1.elf ]\; then cp -v dvm-firmware_f4-dvmv1.elf ${CMAKE_INSTALL_PREFIX_TARBALL}/dvm/fw\; fi + COMMAND if [ -e dvm-firmware_f4-dvmv1.bin ]\; then cp -v dvm-firmware_f4-dvmv1.bin ${CMAKE_INSTALL_PREFIX_TARBALL}/dvm/fw\; fi + COMMAND if [ -e dvm-firmware_due.elf ]\; then cp -v dvm-firmware_due.elf ${CMAKE_INSTALL_PREFIX_TARBALL}/dvm/fw\; fi + COMMAND if [ -e dvm-firmware_due.bin ]\; then cp -v dvm-firmware_due.bin ${CMAKE_INSTALL_PREFIX_TARBALL}/dvm/fw\; fi + COMMAND if [ -e dvm-firmware-hs_f1.elf ]\; then cp -v dvm-firmware-hs_f1.elf ${CMAKE_INSTALL_PREFIX_TARBALL}/dvm/fw\; fi + COMMAND if [ -e dvm-firmware-hs_f1.bin ]\; then cp -v dvm-firmware-hs_f1.bin ${CMAKE_INSTALL_PREFIX_TARBALL}/dvm/fw\; fi + COMMAND if [ -e DVM-V24-stm32f103.elf ]\; then cp -v DVM-V24-stm32f103.elf ${CMAKE_INSTALL_PREFIX_TARBALL}/dvm/fw\; fi + COMMAND if [ -e DVM-V24-stm32f103.bin ]\; then cp -v DVM-V24-stm32f103.bin ${CMAKE_INSTALL_PREFIX_TARBALL}/dvm/fw\; fi + COMMAND cd ${CMAKE_INSTALL_PREFIX_TARBALL} && tar czvf ../dvmhost_${CPACK_DEBIAN_PACKAGE_VERSION}_${ARCH}.tar.gz * + COMMAND rm -rf ${CMAKE_INSTALL_PREFIX_TARBALL}) + endif (ENABLE_TUI_SUPPORT AND (NOT DISABLE_TUI_APPS)) +endif (NOT TARGET tarball_notools) + # # Custom make target to perform non-standard legacy install to "/opt/dvm". This is meant # to retain backward compatibility with deployment scripts and other tools that work with "/opt/dvm" From f3c6e79999a057325477a7c6dcd1f89cfc036975 Mon Sep 17 00:00:00 2001 From: Bryan Biedenkapp Date: Thu, 31 Oct 2024 21:00:26 -0400 Subject: [PATCH 11/23] fix up some warnings; --- src/monitor/NodeStatusWnd.h | 2 -- src/sysview/DynRegroupSubscriberWnd.h | 2 +- src/sysview/NodeStatusWnd.h | 4 ---- 3 files changed, 1 insertion(+), 7 deletions(-) diff --git a/src/monitor/NodeStatusWnd.h b/src/monitor/NodeStatusWnd.h index 6bbc3dc0..9aeea5f4 100644 --- a/src/monitor/NodeStatusWnd.h +++ b/src/monitor/NodeStatusWnd.h @@ -215,8 +215,6 @@ private: if (FVTerm::getFOutput()->getMaxColor() < 16) setBold(); - const auto& wc = getColorTheme(); - if (!m_tx) { if (m_failed) { setColor(FColor::Black, FColor::LightRed); diff --git a/src/sysview/DynRegroupSubscriberWnd.h b/src/sysview/DynRegroupSubscriberWnd.h index f8072409..b693eaa6 100644 --- a/src/sysview/DynRegroupSubscriberWnd.h +++ b/src/sysview/DynRegroupSubscriberWnd.h @@ -169,7 +169,7 @@ private: redraw(); }); m_subscriber.addCallback("changed", [&]() { - if (m_subscriber.getText().c_str() == "") { + if (m_subscriber.getText().getLength() == 0U) { m_srcId = 1U; return; } diff --git a/src/sysview/NodeStatusWnd.h b/src/sysview/NodeStatusWnd.h index c2e9a16b..37c8fd18 100644 --- a/src/sysview/NodeStatusWnd.h +++ b/src/sysview/NodeStatusWnd.h @@ -283,8 +283,6 @@ private: if (FVTerm::getFOutput()->getMaxColor() < 16) setBold(); - const auto& wc = getColorTheme(); - if (!m_tx) { if (m_failed) { setColor(FColor::Black, FColor::LightRed); @@ -732,8 +730,6 @@ private: { assert(wdgt != nullptr); - const auto& rootWidget = getRootWidget(); - uint8_t channelId = peerObj["channelId"].get(); uint32_t channelNo = peerObj["channelNo"].get(); From e14defe125abdddb711cb212f9e0869559174454 Mon Sep 17 00:00:00 2001 From: Bryan Biedenkapp Date: Fri, 1 Nov 2024 09:36:33 -0400 Subject: [PATCH 12/23] correct issue where the stream ID *may not* be reset at the end of call by explicitly calling the appropriate network reset routine; correct Win32 compilation issues for FNE; --- src/bridge/HostBridge.cpp | 5 +++-- src/fne/network/influxdb/InfluxDB.h | 15 ++++++++++++--- 2 files changed, 15 insertions(+), 5 deletions(-) diff --git a/src/bridge/HostBridge.cpp b/src/bridge/HostBridge.cpp index 088849fe..89faa792 100644 --- a/src/bridge/HostBridge.cpp +++ b/src/bridge/HostBridge.cpp @@ -1041,8 +1041,7 @@ void HostBridge::processUDPAudio() m_txStreamId = 1U; // prevent further false starts -- this isn't the right way to handle this... LogMessage(LOG_HOST, "%s, call start, srcId = %u, dstId = %u", UDP_CALL, m_udpSrcId, m_udpDstId); if (m_grantDemand) { - switch (m_txMode) - { + switch (m_txMode) { case TX_MODE_P25: { p25::lc::LC lc = p25::lc::LC(); @@ -2101,6 +2100,7 @@ void HostBridge::callEnd(uint32_t srcId, uint32_t dstId) data.setSrcId(srcId); m_network->writeDMRTerminator(data, &m_dmrSeqNo, &m_dmrN, m_dmrEmbeddedData); + m_network->resetDMR(data.getSlotNo()); } break; case TX_MODE_P25: @@ -2114,6 +2114,7 @@ void HostBridge::callEnd(uint32_t srcId, uint32_t dstId) uint8_t controlByte = 0x00U; m_network->writeP25TDU(lc, lsd, controlByte); + m_network->resetP25(); } break; } diff --git a/src/fne/network/influxdb/InfluxDB.h b/src/fne/network/influxdb/InfluxDB.h index d9074e8f..d353a6e4 100644 --- a/src/fne/network/influxdb/InfluxDB.h +++ b/src/fne/network/influxdb/InfluxDB.h @@ -191,12 +191,19 @@ namespace network // set SO_REUSEADDR option const int sockOptVal = 1; +#if defined(_WIN32) + if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, (char*)&sockOptVal, sizeof(int)) < 0) { + LogError(LOG_NET, "Failed to connect to InfluxDB server, err: %d", errno); + closesocket(fd); + return 1; + } +#else if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &sockOptVal, sizeof(int)) < 0) { LogError(LOG_NET, "Failed to connect to InfluxDB server, err: %d", errno); closesocket(fd); return 1; } - +#endif // connect to the server ret = connect(fd, addr->ai_addr, addr->ai_addrlen); if (ret < 0) { @@ -297,9 +304,11 @@ namespace network struct linger sl; sl.l_onoff = 1; /* non-zero value enables linger option in kernel */ sl.l_linger = 0; /* timeout interval in seconds */ - +#if defined(_WIN32) + setsockopt(fd, SOL_SOCKET, SO_LINGER, (char*)&sl, sizeof(sl)); +#else setsockopt(fd, SOL_SOCKET, SO_LINGER, &sl, sizeof(sl)); - +#endif // close socket closesocket(fd); return ret / 100 == 2 ? 0 : ret; From 26a8c75f2382578ca4c107a368a5483f4a036d26 Mon Sep 17 00:00:00 2001 From: Bryan Biedenkapp Date: Fri, 1 Nov 2024 14:42:54 -0400 Subject: [PATCH 13/23] enhance display of some dialogs in TUI mode; --- src/monitor/FDblDialog.h | 87 +++++++++++++++++++++++++++++++++++ src/monitor/TransmitWndBase.h | 6 ++- src/sysview/AffListWnd.h | 5 +- src/sysview/FDblDialog.h | 87 +++++++++++++++++++++++++++++++++++ src/sysview/NodeStatusWnd.h | 6 ++- src/sysview/PeerListWnd.h | 5 +- src/sysview/TransmitWndBase.h | 6 ++- src/tged/CloseWndBase.h | 6 ++- src/tged/FDblDialog.h | 87 +++++++++++++++++++++++++++++++++++ src/tged/TGListWnd.h | 49 +++++++++++++++++++- 10 files changed, 330 insertions(+), 14 deletions(-) create mode 100644 src/monitor/FDblDialog.h create mode 100644 src/sysview/FDblDialog.h create mode 100644 src/tged/FDblDialog.h diff --git a/src/monitor/FDblDialog.h b/src/monitor/FDblDialog.h new file mode 100644 index 00000000..3b0efaee --- /dev/null +++ b/src/monitor/FDblDialog.h @@ -0,0 +1,87 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Digital Voice Modem - Host Monitor Software + * GPLv2 Open Source. Use is subject to license terms. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright (C) 2024 Bryan Biedenkapp, N2PLL + * + */ +/** + * @file FDblDialog.h + * @ingroup monitor + */ +#if !defined(__F_DBL_DIALOG_H__) +#define __F_DBL_DIALOG_H__ + +#include "common/Defines.h" + +#include +using namespace finalcut; + +// --------------------------------------------------------------------------- +// Class Declaration +// --------------------------------------------------------------------------- + +/** + * @brief This class implements the double-border dialog. + * @ingroup monitor + */ +class HOST_SW_API FDblDialog : public finalcut::FDialog { +public: + /** + * @brief Initializes a new instance of the FDblDialog class. + * @param widget + */ + explicit FDblDialog(FWidget* widget = nullptr) : finalcut::FDialog{widget} + { + /* stub */ + } + +protected: + /** + * @brief + */ + void drawBorder() override + { + if (!hasBorder()) + return; + + setColor(); + + FRect box{{1, 2}, getSize()}; + box.scaleBy(0, -1); + + FRect rect = box; + if (rect.x1_ref() > rect.x2_ref()) + std::swap(rect.x1_ref(), rect.x2_ref()); + + if (rect.y1_ref() > rect.y2_ref()) + std::swap(rect.y1_ref(), rect.y2_ref()); + + rect.x1_ref() = std::max(rect.x1_ref(), 1); + rect.y1_ref() = std::max(rect.y1_ref(), 1); + rect.x2_ref() = std::min(rect.x2_ref(), rect.x1_ref() + int(getWidth()) - 1); + rect.y2_ref() = std::min(rect.y2_ref(), rect.y1_ref() + int(getHeight()) - 1); + + if (box.getWidth() < 3) + return; + + // Use box-drawing characters to draw a border + constexpr std::array box_char + {{ + static_cast(0x2554), // ╔ + static_cast(0x2550), // ═ + static_cast(0x2557), // ╗ + static_cast(0x2551), // ║ + static_cast(0x2551), // ║ + static_cast(0x255A), // ╚ + static_cast(0x2550), // ═ + static_cast(0x255D) // ╝ + }}; + + drawGenericBox(this, box, box_char); + } +}; + +#endif // __F_DBL_DIALOG_H__ diff --git a/src/monitor/TransmitWndBase.h b/src/monitor/TransmitWndBase.h index d0be1b0a..8796f9e9 100644 --- a/src/monitor/TransmitWndBase.h +++ b/src/monitor/TransmitWndBase.h @@ -20,6 +20,8 @@ #include "remote/RESTClient.h" #include "MonitorMain.h" +#include "FDblDialog.h" + #include using namespace finalcut; @@ -31,14 +33,14 @@ using namespace finalcut; * @brief This class implements the base class for transmit windows. * @ingroup monitor */ -class HOST_SW_API TransmitWndBase : public finalcut::FDialog { +class HOST_SW_API TransmitWndBase : public FDblDialog { public: /** * @brief Initializes a new instance of the TransmitWndBase class. * @param channel Channel data. * @param widget */ - explicit TransmitWndBase(lookups::VoiceChData channel, FWidget* widget = nullptr) : FDialog{widget}, + explicit TransmitWndBase(lookups::VoiceChData channel, FWidget* widget = nullptr) : FDblDialog{widget}, m_selectedCh(channel) { /* stub */ diff --git a/src/sysview/AffListWnd.h b/src/sysview/AffListWnd.h index 63af7aac..6ec5137b 100644 --- a/src/sysview/AffListWnd.h +++ b/src/sysview/AffListWnd.h @@ -19,6 +19,7 @@ #include "fne/network/RESTDefines.h" #include "remote/RESTClient.h" +#include "FDblDialog.h" #include "SysViewMainWnd.h" #include @@ -39,13 +40,13 @@ using namespace finalcut; * @brief This class implements the affiliations list window. * @ingroup fneSysView */ -class HOST_SW_API AffListWnd final : public finalcut::FDialog { +class HOST_SW_API AffListWnd final : public FDblDialog { public: /** * @brief Initializes a new instance of the AffListWnd class. * @param widget */ - explicit AffListWnd(FWidget* widget = nullptr) : FDialog{widget} + explicit AffListWnd(FWidget* widget = nullptr) : FDblDialog{widget} { m_timerId = addTimer(10000); // starts the timer every 10 seconds } diff --git a/src/sysview/FDblDialog.h b/src/sysview/FDblDialog.h new file mode 100644 index 00000000..98e754b6 --- /dev/null +++ b/src/sysview/FDblDialog.h @@ -0,0 +1,87 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Digital Voice Modem - FNE System View + * GPLv2 Open Source. Use is subject to license terms. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright (C) 2024 Bryan Biedenkapp, N2PLL + * + */ +/** + * @file FDblDialog.h + * @ingroup fneSysView + */ +#if !defined(__F_DBL_DIALOG_H__) +#define __F_DBL_DIALOG_H__ + +#include "common/Defines.h" + +#include +using namespace finalcut; + +// --------------------------------------------------------------------------- +// Class Declaration +// --------------------------------------------------------------------------- + +/** + * @brief This class implements the double-border dialog. + * @ingroup fneSysView + */ +class HOST_SW_API FDblDialog : public finalcut::FDialog { +public: + /** + * @brief Initializes a new instance of the FDblDialog class. + * @param widget + */ + explicit FDblDialog(FWidget* widget = nullptr) : finalcut::FDialog{widget} + { + /* stub */ + } + +protected: + /** + * @brief + */ + void drawBorder() override + { + if (!hasBorder()) + return; + + setColor(); + + FRect box{{1, 2}, getSize()}; + box.scaleBy(0, -1); + + FRect rect = box; + if (rect.x1_ref() > rect.x2_ref()) + std::swap(rect.x1_ref(), rect.x2_ref()); + + if (rect.y1_ref() > rect.y2_ref()) + std::swap(rect.y1_ref(), rect.y2_ref()); + + rect.x1_ref() = std::max(rect.x1_ref(), 1); + rect.y1_ref() = std::max(rect.y1_ref(), 1); + rect.x2_ref() = std::min(rect.x2_ref(), rect.x1_ref() + int(getWidth()) - 1); + rect.y2_ref() = std::min(rect.y2_ref(), rect.y1_ref() + int(getHeight()) - 1); + + if (box.getWidth() < 3) + return; + + // Use box-drawing characters to draw a border + constexpr std::array box_char + {{ + static_cast(0x2554), // ╔ + static_cast(0x2550), // ═ + static_cast(0x2557), // ╗ + static_cast(0x2551), // ║ + static_cast(0x2551), // ║ + static_cast(0x255A), // ╚ + static_cast(0x2550), // ═ + static_cast(0x255D) // ╝ + }}; + + drawGenericBox(this, box, box_char); + } +}; + +#endif // __F_DBL_DIALOG_H__ diff --git a/src/sysview/NodeStatusWnd.h b/src/sysview/NodeStatusWnd.h index 37c8fd18..250d9a44 100644 --- a/src/sysview/NodeStatusWnd.h +++ b/src/sysview/NodeStatusWnd.h @@ -19,6 +19,8 @@ #include "common/Thread.h" #include "SysViewMain.h" +#include "FDblDialog.h" + #include using namespace finalcut; @@ -769,13 +771,13 @@ private: * @brief This class implements the node status window. * @ingroup fneSysView */ -class HOST_SW_API NodeStatusWnd final : public finalcut::FDialog { +class HOST_SW_API NodeStatusWnd final : public FDblDialog { public: /** * @brief Initializes a new instance of the NodeStatusWnd class. * @param widget */ - explicit NodeStatusWnd(FWidget* widget = nullptr) : FDialog{widget}, + explicit NodeStatusWnd(FWidget* widget = nullptr) : FDblDialog{widget}, m_killed(false), m_threadStopped(false) { diff --git a/src/sysview/PeerListWnd.h b/src/sysview/PeerListWnd.h index 0ad263fd..2f0f51aa 100644 --- a/src/sysview/PeerListWnd.h +++ b/src/sysview/PeerListWnd.h @@ -19,6 +19,7 @@ #include "fne/network/RESTDefines.h" #include "remote/RESTClient.h" +#include "FDblDialog.h" #include "SysViewMainWnd.h" #include @@ -39,13 +40,13 @@ using namespace finalcut; * @brief This class implements the peer list window. * @ingroup fneSysView */ -class HOST_SW_API PeerListWnd final : public finalcut::FDialog { +class HOST_SW_API PeerListWnd final : public FDblDialog { public: /** * @brief Initializes a new instance of the PeerListWnd class. * @param widget */ - explicit PeerListWnd(FWidget* widget = nullptr) : FDialog{widget} + explicit PeerListWnd(FWidget* widget = nullptr) : FDblDialog{widget} { m_timerId = addTimer(25000); // starts the timer every 25 seconds } diff --git a/src/sysview/TransmitWndBase.h b/src/sysview/TransmitWndBase.h index 48644a2f..9411841f 100644 --- a/src/sysview/TransmitWndBase.h +++ b/src/sysview/TransmitWndBase.h @@ -28,6 +28,8 @@ #include "host/modem/Modem.h" #include "SysViewMain.h" +#include "FDblDialog.h" + #include using namespace finalcut; @@ -83,13 +85,13 @@ public: * @brief This class implements the base class for transmit windows. * @ingroup fneSysView */ -class HOST_SW_API TransmitWndBase : public finalcut::FDialog { +class HOST_SW_API TransmitWndBase : public FDblDialog { public: /** * @brief Initializes a new instance of the TransmitWndBase class. * @param widget */ - explicit TransmitWndBase(FWidget* widget = nullptr) : FDialog{widget} + explicit TransmitWndBase(FWidget* widget = nullptr) : FDblDialog{widget} { /* stub */ } diff --git a/src/tged/CloseWndBase.h b/src/tged/CloseWndBase.h index 908443e8..40c3d4c4 100644 --- a/src/tged/CloseWndBase.h +++ b/src/tged/CloseWndBase.h @@ -16,6 +16,8 @@ #include "common/Thread.h" +#include "FDblDialog.h" + #include using namespace finalcut; @@ -27,13 +29,13 @@ using namespace finalcut; * @brief This class implements the base class for windows with close buttons. * @ingroup tged */ -class HOST_SW_API CloseWndBase : public finalcut::FDialog { +class HOST_SW_API CloseWndBase : public FDblDialog { public: /** * @brief Initializes a new instance of the CloseWndBase class. * @param widget */ - explicit CloseWndBase(FWidget* widget = nullptr) : FDialog{widget} + explicit CloseWndBase(FWidget* widget = nullptr) : FDblDialog{widget} { /* stub */ } diff --git a/src/tged/FDblDialog.h b/src/tged/FDblDialog.h new file mode 100644 index 00000000..ffdbdf45 --- /dev/null +++ b/src/tged/FDblDialog.h @@ -0,0 +1,87 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Digital Voice Modem - Talkgroup Editor + * GPLv2 Open Source. Use is subject to license terms. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright (C) 2024 Bryan Biedenkapp, N2PLL + * + */ +/** + * @file FDblDialog.h + * @ingroup tged + */ +#if !defined(__F_DBL_DIALOG_H__) +#define __F_DBL_DIALOG_H__ + +#include "common/Defines.h" + +#include +using namespace finalcut; + +// --------------------------------------------------------------------------- +// Class Declaration +// --------------------------------------------------------------------------- + +/** + * @brief This class implements the double-border dialog. + * @ingroup tged + */ +class HOST_SW_API FDblDialog : public finalcut::FDialog { +public: + /** + * @brief Initializes a new instance of the FDblDialog class. + * @param widget + */ + explicit FDblDialog(FWidget* widget = nullptr) : finalcut::FDialog{widget} + { + /* stub */ + } + +protected: + /** + * @brief + */ + void drawBorder() override + { + if (!hasBorder()) + return; + + setColor(); + + FRect box{{1, 2}, getSize()}; + box.scaleBy(0, -1); + + FRect rect = box; + if (rect.x1_ref() > rect.x2_ref()) + std::swap(rect.x1_ref(), rect.x2_ref()); + + if (rect.y1_ref() > rect.y2_ref()) + std::swap(rect.y1_ref(), rect.y2_ref()); + + rect.x1_ref() = std::max(rect.x1_ref(), 1); + rect.y1_ref() = std::max(rect.y1_ref(), 1); + rect.x2_ref() = std::min(rect.x2_ref(), rect.x1_ref() + int(getWidth()) - 1); + rect.y2_ref() = std::min(rect.y2_ref(), rect.y1_ref() + int(getHeight()) - 1); + + if (box.getWidth() < 3) + return; + + // Use box-drawing characters to draw a border + constexpr std::array box_char + {{ + static_cast(0x2554), // ╔ + static_cast(0x2550), // ═ + static_cast(0x2557), // ╗ + static_cast(0x2551), // ║ + static_cast(0x2551), // ║ + static_cast(0x255A), // ╚ + static_cast(0x2550), // ═ + static_cast(0x255D) // ╝ + }}; + + drawGenericBox(this, box, box_char); + } +}; + +#endif // __F_DBL_DIALOG_H__ diff --git a/src/tged/TGListWnd.h b/src/tged/TGListWnd.h index 5dd2391c..94315f78 100644 --- a/src/tged/TGListWnd.h +++ b/src/tged/TGListWnd.h @@ -16,6 +16,7 @@ #include "common/Log.h" +#include "FDblDialog.h" #include "TGEdMainWnd.h" #include "TGEditWnd.h" @@ -37,13 +38,13 @@ using namespace finalcut; * @brief This class implements the talkgroup list window. * @ingroup tged */ -class HOST_SW_API TGListWnd final : public finalcut::FDialog { +class HOST_SW_API TGListWnd final : public FDblDialog { public: /** * @brief Initializes a new instance of the TGListWnd class. * @param widget */ - explicit TGListWnd(FWidget* widget = nullptr) : FDialog{widget} + explicit TGListWnd(FWidget* widget = nullptr) : FDblDialog{widget} { /* stub */ } @@ -284,6 +285,50 @@ private: loadListView(); } + /** + * @brief + */ + void drawBorder() override + { + if (!hasBorder()) + return; + + setColor(); + + FRect box{{1, 2}, getSize()}; + box.scaleBy(0, -1); + + FRect rect = box; + if (rect.x1_ref() > rect.x2_ref()) + std::swap(rect.x1_ref(), rect.x2_ref()); + + if (rect.y1_ref() > rect.y2_ref()) + std::swap(rect.y1_ref(), rect.y2_ref()); + + rect.x1_ref() = std::max(rect.x1_ref(), 1); + rect.y1_ref() = std::max(rect.y1_ref(), 1); + rect.x2_ref() = std::min(rect.x2_ref(), rect.x1_ref() + int(getWidth()) - 1); + rect.y2_ref() = std::min(rect.y2_ref(), rect.y1_ref() + int(getHeight()) - 1); + + if (box.getWidth() < 3) + return; + + // Use box-drawing characters to draw a border + constexpr std::array box_char + {{ + static_cast(0x2554), + static_cast(0x2550), + static_cast(0x2557), + static_cast(0x2551), + static_cast(0x2551), + static_cast(0x255A), + static_cast(0x2550), + static_cast(0x255D) + }}; + + drawGenericBox(this, box, box_char); + } + /* ** Event Handlers */ From 665ed3e018b38f38667eb11cbd38cd653675e8b5 Mon Sep 17 00:00:00 2001 From: Bryan Biedenkapp Date: Fri, 1 Nov 2024 14:48:59 -0400 Subject: [PATCH 14/23] add definition for a CC-VC (to prevent confusion); --- src/sysview/NodeStatusWnd.h | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/src/sysview/NodeStatusWnd.h b/src/sysview/NodeStatusWnd.h index 250d9a44..653855c0 100644 --- a/src/sysview/NodeStatusWnd.h +++ b/src/sysview/NodeStatusWnd.h @@ -171,7 +171,15 @@ public: m_tbText = std::string("ENH. VOICE/CONV"); } else { - m_tbText = std::string("VOICE/CONV"); + if (peerStatus["vControl"].is()) { + bool vControl = peerStatus["vControl"].getDefault(false); + if (vControl) + m_tbText = std::string("CC-VC"); + else + m_tbText = std::string("VOICE/CONV"); + } else { + m_tbText = std::string("VOICE/CONV"); + } } // are we transmitting? @@ -531,6 +539,9 @@ public: vcObj["state"].set(state); + bool _true = true; + vcObj["vControl"].set(_true); + bool _false = false; vcObj["dmrTSCCEnable"].set(_false); vcObj["dmrCC"].set(_false); @@ -613,6 +624,9 @@ public: vcObj["state"].set(state); + bool _true = true; + vcObj["vControl"].set(_true); + bool _false = false; vcObj["dmrTSCCEnable"].set(_false); vcObj["dmrCC"].set(_false); From 9f17cfc8ef9c2d9ad12085409cadd7c14a25ef2b Mon Sep 17 00:00:00 2001 From: Bryan Biedenkapp Date: Fri, 1 Nov 2024 14:57:26 -0400 Subject: [PATCH 15/23] log the currently loaded file in the system log; --- src/tged/TGEdMain.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/src/tged/TGEdMain.cpp b/src/tged/TGEdMain.cpp index b7156013..589f16d1 100644 --- a/src/tged/TGEdMain.cpp +++ b/src/tged/TGEdMain.cpp @@ -210,6 +210,7 @@ int main(int argc, char** argv) g_tidLookups = new TalkgroupRulesLookup(g_iniFile, 0U, false); g_tidLookups->read(); + LogMessage(LOG_HOST, "Loaded talkgroup rules file: %s", g_iniFile.c_str()); // show and start the application wnd.show(); From 93a554302ebc4a363712f001f1187b8c49dcccd5 Mon Sep 17 00:00:00 2001 From: Bryan Biedenkapp Date: Fri, 1 Nov 2024 15:06:29 -0400 Subject: [PATCH 16/23] reprint reloaded file in log; --- src/tged/TGEdMainWnd.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/tged/TGEdMainWnd.h b/src/tged/TGEdMainWnd.h index 2bb78b98..bf61ffc1 100644 --- a/src/tged/TGEdMainWnd.h +++ b/src/tged/TGEdMainWnd.h @@ -74,7 +74,7 @@ public: m_quitItem.addAccelerator(FKey::Meta_x); // Meta/Alt + X m_quitItem.addCallback("clicked", getFApplication(), &FApplication::cb_exitApp, this); m_keyF3.addCallback("activate", getFApplication(), &FApplication::cb_exitApp, this); - m_keyF5.addCallback("activate", this, [&]() { g_tidLookups->reload(); m_wnd->loadListView(); }); + m_keyF5.addCallback("activate", this, [&]() { g_tidLookups->reload(); m_wnd->loadListView(); LogMessage(LOG_HOST, "Loaded talkgroup rules file: %s", g_iniFile.c_str()); }); m_backupOnSave.setChecked(); From 497b6a7b658082df4d4438e072389e4d347bb97c Mon Sep 17 00:00:00 2001 From: Bryan Biedenkapp Date: Sat, 2 Nov 2024 19:50:17 -0400 Subject: [PATCH 17/23] allow FNE PUT /dmr/rid and /p25/rid to target *all* connected peers; correct naming of packet data dumping configuration parameter; continue some work on P25 PDU data and VTUN; adjust P25 PDU ACK_RSP; --- configs/fne-config.example.yml | 2 +- src/fne/HostFNE.cpp | 56 +++- src/fne/HostFNE.h | 6 + src/fne/network/FNENetwork.cpp | 6 +- src/fne/network/FNENetwork.h | 2 +- src/fne/network/RESTAPI.cpp | 26 +- src/fne/network/callhandler/TagDMRData.cpp | 36 ++- src/fne/network/callhandler/TagP25Data.cpp | 35 ++- .../callhandler/packetdata/DMRPacketData.cpp | 4 +- .../callhandler/packetdata/P25PacketData.cpp | 240 ++++++++++++------ .../callhandler/packetdata/P25PacketData.h | 26 +- src/host/p25/packet/Data.cpp | 35 +-- 12 files changed, 329 insertions(+), 145 deletions(-) diff --git a/configs/fne-config.example.yml b/configs/fne-config.example.yml index bc46a707..8264e5fc 100644 --- a/configs/fne-config.example.yml +++ b/configs/fne-config.example.yml @@ -69,7 +69,7 @@ master: # Flag indicating whether packet data will be passed. disablePacketData: false # Flag indicating whether verbose dumping of data packets is enabled. - dumpDataPacket: false + dumpPacketData: false # Delay from when a call on a parrot TG ends to when the playback starts (in milliseconds). parrotDelay: 2000 diff --git a/src/fne/HostFNE.cpp b/src/fne/HostFNE.cpp index 8d370729..16d55d07 100644 --- a/src/fne/HostFNE.cpp +++ b/src/fne/HostFNE.cpp @@ -206,6 +206,8 @@ int HostFNE::run() #if !defined(_WIN32) if (!Thread::runAsThread(this, threadVirtualNetworking)) return EXIT_FAILURE; + if (!Thread::runAsThread(this, threadVirtualNetworkingClock)) + return EXIT_FAILURE; #endif // !defined(_WIN32) /* ** Main execution loop @@ -836,7 +838,7 @@ void* HostFNE::threadVirtualNetworking(void* arg) if (th != nullptr) { ::pthread_detach(th->thread); - std::string threadName("fne:vtun-loop"); + std::string threadName("fne:vtun-net-rx"); HostFNE* fne = static_cast(th->obj); if (fne == nullptr) { g_killed = true; @@ -863,7 +865,6 @@ void* HostFNE::threadVirtualNetworking(void* arg) stopWatch.start(); while (!g_killed) { - uint32_t ms = stopWatch.elapsed(); stopWatch.start(); uint8_t packet[DEFAULT_MTU_SIZE]; @@ -882,6 +883,55 @@ void* HostFNE::threadVirtualNetworking(void* arg) } } + Thread::sleep(2U); + } + } + + LogDebug(LOG_HOST, "[STOP] %s", threadName.c_str()); + delete th; + } + + return nullptr; +} + +/* Entry point to virtual networking clocking thread. */ + +void* HostFNE::threadVirtualNetworkingClock(void* arg) +{ + thread_t* th = (thread_t*)arg; + if (th != nullptr) { + ::pthread_detach(th->thread); + + std::string threadName("fne:vtun-clock"); + HostFNE* fne = static_cast(th->obj); + if (fne == nullptr) { + g_killed = true; + LogDebug(LOG_HOST, "[FAIL] %s", threadName.c_str()); + } + + if (g_killed) { + delete th; + return nullptr; + } + + if (!fne->m_vtunEnabled) { + delete th; + return nullptr; + } + + LogDebug(LOG_HOST, "[ OK ] %s", threadName.c_str()); +#ifdef _GNU_SOURCE + ::pthread_setname_np(th->thread, threadName.c_str()); +#endif // _GNU_SOURCE + + if (fne->m_tun != nullptr) { + StopWatch stopWatch; + stopWatch.start(); + + while (!g_killed) { + uint32_t ms = stopWatch.elapsed(); + stopWatch.start(); + // clock traffic handler switch (fne->m_packetDataMode) { case PacketDataMode::DMR: @@ -893,7 +943,7 @@ void* HostFNE::threadVirtualNetworking(void* arg) break; } - Thread::sleep(5U); + Thread::sleep(2U); } } diff --git a/src/fne/HostFNE.h b/src/fne/HostFNE.h index e86ea372..77e983de 100644 --- a/src/fne/HostFNE.h +++ b/src/fne/HostFNE.h @@ -163,6 +163,12 @@ private: * @returns void* (Ignore) */ static void* threadVirtualNetworking(void* arg); + /** + * @brief Entry point to virtual networking clocking thread. + * @param arg Instance of the thread_t structure. + * @returns void* (Ignore) + */ + static void* threadVirtualNetworkingClock(void* arg); #endif // !defined(_WIN32) /** * @brief Processes any peer network traffic. diff --git a/src/fne/network/FNENetwork.cpp b/src/fne/network/FNENetwork.cpp index 81f0c44a..84104871 100644 --- a/src/fne/network/FNENetwork.cpp +++ b/src/fne/network/FNENetwork.cpp @@ -94,7 +94,7 @@ FNENetwork::FNENetwork(HostFNE* host, const std::string& address, uint16_t port, m_influxBucket("dvm"), m_influxLogRawData(false), m_disablePacketData(false), - m_dumpDataPacket(false), + m_dumpPacketData(false), m_reportPeerPing(reportPeerPing), m_verbose(verbose) { @@ -153,7 +153,7 @@ void FNENetwork::setOptions(yaml::Node& conf, bool printOptions) m_filterTerminators = conf["filterTerminators"].as(true); m_disablePacketData = conf["disablePacketData"].as(false); - m_dumpDataPacket = conf["dumpDataPacket"].as(false); + m_dumpPacketData = conf["dumpPacketData"].as(false); /* ** Drop Unit to Unit Peers @@ -176,7 +176,7 @@ void FNENetwork::setOptions(yaml::Node& conf, bool printOptions) LogWarning(LOG_NET, "NOTICE: All P25 ADJ_STS_BCAST messages will be blocked and dropped!"); } LogInfo(" Disable Packet Data: %s", m_disablePacketData ? "yes" : "no"); - LogInfo(" Dump Packet Data: %s", m_dumpDataPacket ? "yes" : "no"); + LogInfo(" Dump Packet Data: %s", m_dumpPacketData ? "yes" : "no"); LogInfo(" Disable P25 ADJ_STS_BCAST to external peers: %s", m_disallowExtAdjStsBcast ? "yes" : "no"); LogInfo(" Allow conventional sites to override affiliation and receive all traffic: %s", m_allowConvSiteAffOverride ? "yes" : "no"); LogInfo(" Restrict grant response by affiliation: %s", m_restrictGrantToAffOnly ? "yes" : "no"); diff --git a/src/fne/network/FNENetwork.h b/src/fne/network/FNENetwork.h index 779c8558..a6ea52e6 100644 --- a/src/fne/network/FNENetwork.h +++ b/src/fne/network/FNENetwork.h @@ -475,7 +475,7 @@ namespace network influxdb::ServerInfo m_influxServer; bool m_disablePacketData; - bool m_dumpDataPacket; + bool m_dumpPacketData; bool m_reportPeerPing; bool m_verbose; diff --git a/src/fne/network/RESTAPI.cpp b/src/fne/network/RESTAPI.cpp index 62491805..46e39825 100644 --- a/src/fne/network/RESTAPI.cpp +++ b/src/fne/network/RESTAPI.cpp @@ -1419,12 +1419,6 @@ void RESTAPI::restAPI_PutDMRRID(const HTTPPayload& request, HTTPPayload& reply, return; } - // validate peer ID is a integer within the JSON blob - if (!req["peerId"].is()) { - errorPayload(reply, "peer ID was not valid"); - return; - } - // validate destination ID is a integer within the JSON blob if (!req["dstId"].is()) { errorPayload(reply, "destination ID was not valid"); @@ -1437,15 +1431,10 @@ void RESTAPI::restAPI_PutDMRRID(const HTTPPayload& request, HTTPPayload& reply, return; } - uint32_t peerId = req["peerId"].get(); + uint32_t peerId = req["peerId"].getDefault(0U); uint32_t dstId = req["dstId"].get(); uint8_t slot = req["slot"].get(); - if (peerId == 0U) { - errorPayload(reply, "peer ID was not valid"); - return; - } - if (dstId == 0U) { errorPayload(reply, "destination ID was not valid"); return; @@ -1500,26 +1489,15 @@ void RESTAPI::restAPI_PutP25RID(const HTTPPayload& request, HTTPPayload& reply, return; } - // validate peer ID is a integer within the JSON blob - if (!req["peerId"].is()) { - errorPayload(reply, "peer ID was not valid"); - return; - } - // validate destination ID is a integer within the JSON blob if (!req["dstId"].is()) { errorPayload(reply, "destination ID was not valid"); return; } - uint32_t peerId = req["peerId"].get(); + uint32_t peerId = req["peerId"].getDefault(0U); uint32_t dstId = req["dstId"].get(); - if (peerId == 0U) { - errorPayload(reply, "peer ID was not valid"); - return; - } - if (dstId == 0U) { errorPayload(reply, "destination ID was not valid"); return; diff --git a/src/fne/network/callhandler/TagDMRData.cpp b/src/fne/network/callhandler/TagDMRData.cpp index 315a1170..137da596 100644 --- a/src/fne/network/callhandler/TagDMRData.cpp +++ b/src/fne/network/callhandler/TagDMRData.cpp @@ -952,5 +952,39 @@ void TagDMRData::write_CSBK(uint32_t peerId, uint8_t slot, lc::CSBK* csbk) return; } - m_network->writePeer(peerId, { NET_FUNC::PROTOCOL, NET_SUBFUNC::PROTOCOL_SUBFUNC_DMR }, message.get(), messageLength, RTP_END_OF_CALL_SEQ, streamId, false, true); + if (peerId > 0U) { + m_network->writePeer(peerId, { NET_FUNC::PROTOCOL, NET_SUBFUNC::PROTOCOL_SUBFUNC_DMR }, message.get(), messageLength, RTP_END_OF_CALL_SEQ, streamId, false, true); + } else { + // repeat traffic to the connected peers + if (m_network->m_peers.size() > 0U) { + uint32_t i = 0U; + for (auto peer : m_network->m_peers) { + // every 5 peers flush the queue + if (i % 5U == 0U) { + m_network->m_frameQueue->flushQueue(); + } + + m_network->writePeer(peer.first, { NET_FUNC::PROTOCOL, NET_SUBFUNC::PROTOCOL_SUBFUNC_DMR }, message.get(), messageLength, RTP_END_OF_CALL_SEQ, streamId, true); + if (m_network->m_debug) { + LogDebug(LOG_NET, "DMR, peer = %u, slotNo = %u, len = %u, stream = %u", + peer.first, slot, messageLength, streamId); + } + i++; + } + m_network->m_frameQueue->flushQueue(); + } + + // repeat traffic to external peers + if (m_network->m_host->m_peerNetworks.size() > 0U) { + for (auto peer : m_network->m_host->m_peerNetworks) { + uint32_t dstPeerId = peer.second->getPeerId(); + + peer.second->writeMaster({ NET_FUNC::PROTOCOL, NET_SUBFUNC::PROTOCOL_SUBFUNC_DMR }, message.get(), messageLength, RTP_END_OF_CALL_SEQ, streamId); + if (m_network->m_debug) { + LogDebug(LOG_NET, "DMR, peer = %u, slotNo = %u, len = %u, stream = %u", + dstPeerId, slot, messageLength, streamId); + } + } + } + } } diff --git a/src/fne/network/callhandler/TagP25Data.cpp b/src/fne/network/callhandler/TagP25Data.cpp index 4db93cbd..6e22416e 100644 --- a/src/fne/network/callhandler/TagP25Data.cpp +++ b/src/fne/network/callhandler/TagP25Data.cpp @@ -1283,5 +1283,38 @@ void TagP25Data::write_TSDU(uint32_t peerId, lc::TSBK* tsbk) } uint32_t streamId = m_network->createStreamId(); - m_network->writePeer(peerId, { NET_FUNC::PROTOCOL, NET_SUBFUNC::PROTOCOL_SUBFUNC_P25 }, message.get(), messageLength, RTP_END_OF_CALL_SEQ, streamId, false, true); + if (peerId > 0U) { + m_network->writePeer(peerId, { NET_FUNC::PROTOCOL, NET_SUBFUNC::PROTOCOL_SUBFUNC_P25 }, message.get(), messageLength, RTP_END_OF_CALL_SEQ, streamId, false, true); + } else { + // repeat traffic to the connected peers + if (m_network->m_peers.size() > 0U) { + uint32_t i = 0U; + for (auto peer : m_network->m_peers) { + // every 5 peers flush the queue + if (i % 5U == 0U) { + m_network->m_frameQueue->flushQueue(); + } + + m_network->writePeer(peer.first, { NET_FUNC::PROTOCOL, NET_SUBFUNC::PROTOCOL_SUBFUNC_P25 }, message.get(), messageLength, RTP_END_OF_CALL_SEQ, streamId, true); + if (m_network->m_debug) { + LogDebug(LOG_NET, "P25, peer = %u, len = %u, streamId = %u", + peer.first, messageLength, streamId); + } + i++; + } + m_network->m_frameQueue->flushQueue(); + } + + // repeat traffic to external peers + if (m_network->m_host->m_peerNetworks.size() > 0U) { + for (auto peer : m_network->m_host->m_peerNetworks) { + uint32_t dstPeerId = peer.second->getPeerId(); + peer.second->writeMaster({ NET_FUNC::PROTOCOL, NET_SUBFUNC::PROTOCOL_SUBFUNC_P25 }, message.get(), messageLength, RTP_END_OF_CALL_SEQ, streamId); + if (m_network->m_debug) { + LogDebug(LOG_NET, "P25, peer = %u, len = %u, streamId = %u", + dstPeerId, messageLength, streamId); + } + } + } + } } diff --git a/src/fne/network/callhandler/packetdata/DMRPacketData.cpp b/src/fne/network/callhandler/packetdata/DMRPacketData.cpp index e94c62be..528eca05 100644 --- a/src/fne/network/callhandler/packetdata/DMRPacketData.cpp +++ b/src/fne/network/callhandler/packetdata/DMRPacketData.cpp @@ -253,8 +253,8 @@ void DMRPacketData::dispatch(uint32_t peerId, dmr::data::NetData& dmrData, const LogWarning(LOG_NET, P25_PDU_STR ", failed CRC-32 check, blocks %u, len %u", status->header.getBlocksToFollow(), status->pduDataOffset); } - if (m_network->m_dumpDataPacket) { - Utils::dump(1U, "PDU Packet", status->pduUserData, status->pduDataOffset); + if (m_network->m_dumpPacketData) { + Utils::dump(1U, "ISP PDU Packet", status->pduUserData, status->pduDataOffset); } } } diff --git a/src/fne/network/callhandler/packetdata/P25PacketData.cpp b/src/fne/network/callhandler/packetdata/P25PacketData.cpp index 4f7e75b6..a0671936 100644 --- a/src/fne/network/callhandler/packetdata/P25PacketData.cpp +++ b/src/fne/network/callhandler/packetdata/P25PacketData.cpp @@ -40,6 +40,12 @@ using namespace p25::sndcp; const uint8_t DATA_CALL_COLL_TIMEOUT = 60U; +// --------------------------------------------------------------------------- +// Static Class Members +// --------------------------------------------------------------------------- + +std::timed_mutex P25PacketData::m_vtunMutex; + // --------------------------------------------------------------------------- // Public Class Members // --------------------------------------------------------------------------- @@ -52,8 +58,8 @@ P25PacketData::P25PacketData(FNENetwork* network, TagP25Data* tag, bool debug) : m_dataFrames(), m_status(), m_arpTable(), - m_readyForPkt(), - m_suNotReadyTimeout(), + m_readyForNextPkt(), + m_suSendSeq(), m_debug(debug) { assert(network != nullptr); @@ -129,6 +135,7 @@ bool P25PacketData::processFrame(const uint8_t* data, uint32_t len, uint32_t pee status->llId = status->header.getLLId(); m_status[peerId] = status; + m_readyForNextPkt[status->llId] = true; // is this a response header? if (status->header.getFormat() == PDUFormatType::RSP) { @@ -141,7 +148,6 @@ bool P25PacketData::processFrame(const uint8_t* data, uint32_t len, uint32_t pee if (status->header.getSAP() != PDUSAP::EXT_ADDR && status->header.getFormat() != PDUFormatType::UNCONFIRMED) { - m_readyForPkt[status->llId] = true; m_suSendSeq[status->llId] = 0U; } @@ -218,7 +224,6 @@ bool P25PacketData::processFrame(const uint8_t* data, uint32_t len, uint32_t pee status->extendedAddress = true; status->llId = status->header.getSrcLLId(); - m_readyForPkt[status->llId] = true; m_suSendSeq[status->llId] = 0U; offset += P25_PDU_FEC_LENGTH_BYTES; @@ -277,7 +282,7 @@ bool P25PacketData::processFrame(const uint8_t* data, uint32_t len, uint32_t pee else LogWarning(LOG_NET, P25_PDU_STR ", unfixable PDU data (1/2 rate or CRC), block %u", i); - if (m_network->m_dumpDataPacket) { + if (m_network->m_dumpPacketData) { Utils::dump(1U, "Unfixable PDU Data", buffer, P25_PDU_FEC_LENGTH_BYTES); } } @@ -325,6 +330,8 @@ bool P25PacketData::processFrame(const uint8_t* data, uint32_t len, uint32_t pee void P25PacketData::processPacketFrame(const uint8_t* data, uint32_t len, bool alreadyQueued) { + uint64_t now = std::chrono::duration_cast(std::chrono::system_clock::now().time_since_epoch()).count(); + #if !defined(_WIN32) struct ip* ipHeader = (struct ip*)data; @@ -337,30 +344,34 @@ void P25PacketData::processPacketFrame(const uint8_t* data, uint32_t len, bool a uint8_t proto = ipHeader->ip_p; uint16_t pktLen = Utils::reverseEndian(ipHeader->ip_len); // bryanb: this could be problematic on different endianness - LogMessage(LOG_NET, "P25, VTUN -> PDU IP Data, srcIp = %s, dstIp = %s, pktLen = %u, proto = %02X", srcIp, dstIp, pktLen, proto); #if DEBUG_P25_PDU_DATA Utils::dump(1U, "P25PacketData::processPacketFrame() packet", data, pktLen); #endif - VTUNDataFrame dataFrame; - dataFrame.buffer = new uint8_t[len]; - ::memcpy(dataFrame.buffer, data, len); - dataFrame.bufferLen = len; - dataFrame.pktLen = pktLen; + VTUNDataFrame* dataFrame = new VTUNDataFrame(); + dataFrame->buffer = new uint8_t[len]; + ::memcpy(dataFrame->buffer, data, len); + dataFrame->bufferLen = len; + dataFrame->pktLen = pktLen; + dataFrame->proto = proto; uint32_t dstLlId = getLLIdAddress(Utils::reverseEndian(ipHeader->ip_dst.s_addr)); - dataFrame.srcHWAddr = WUID_FNE; - dataFrame.srcProtoAddr = Utils::reverseEndian(ipHeader->ip_src.s_addr); - dataFrame.tgtHWAddr = dstLlId; - dataFrame.tgtProtoAddr = Utils::reverseEndian(ipHeader->ip_dst.s_addr); + dataFrame->srcHWAddr = WUID_FNE; + dataFrame->srcProtoAddr = Utils::reverseEndian(ipHeader->ip_src.s_addr); + dataFrame->tgtHWAddr = dstLlId; + dataFrame->tgtProtoAddr = Utils::reverseEndian(ipHeader->ip_dst.s_addr); + + dataFrame->timestamp = now; if (dstLlId == 0U) { LogMessage(LOG_NET, "P25, no ARP entry for, dstIp = %s", dstIp); write_PDU_ARP(Utils::reverseEndian(ipHeader->ip_dst.s_addr)); } + m_vtunMutex.try_lock_for(std::chrono::milliseconds(60)); m_dataFrames.push_back(dataFrame); + m_vtunMutex.unlock(); #endif // !defined(_WIN32) } @@ -368,61 +379,92 @@ void P25PacketData::processPacketFrame(const uint8_t* data, uint32_t len, bool a void P25PacketData::clock(uint32_t ms) { - // transmit queued data frames - if (m_dataFrames.size() > 0) { - auto& dataFrame = m_dataFrames[0]; + uint64_t now = std::chrono::duration_cast(std::chrono::system_clock::now().time_since_epoch()).count(); - if (dataFrame.tgtHWAddr == 0U) { - uint32_t dstLlId = getLLIdAddress(dataFrame.tgtProtoAddr); - if (dstLlId == 0U) - return; + if (m_dataFrames.size() == 0U) { + return; + } - dataFrame.tgtHWAddr = dstLlId; - } + // transmit queued data frames + bool processed = false; + m_vtunMutex.try_lock_for(std::chrono::milliseconds(60)); + auto& dataFrame = m_dataFrames[0]; + m_vtunMutex.unlock(); + + if (dataFrame != nullptr) { + if (now > dataFrame->timestamp + 500U) { + processed = true; + + // do we have a valid target address? + if (dataFrame->tgtHWAddr == 0U) { + uint32_t dstLlId = getLLIdAddress(dataFrame->tgtProtoAddr); + if (dstLlId == 0U) { + processed = false; + goto pkt_clock_abort; + } - // don't allow another packet to go out if we haven't acked the previous - if (!m_readyForPkt[dataFrame.tgtHWAddr]) { - m_suNotReadyTimeout[dataFrame.tgtHWAddr].clock(ms); - if (m_suNotReadyTimeout[dataFrame.tgtHWAddr].isRunning() && m_suNotReadyTimeout[dataFrame.tgtHWAddr].hasExpired()) { - m_suNotReadyTimeout[dataFrame.tgtHWAddr].stop(); - m_readyForPkt[dataFrame.tgtHWAddr] = true; + dataFrame->tgtHWAddr = dstLlId; } - return; - } + // is the SU ready for the next packet? + auto ready = std::find_if(m_readyForNextPkt.begin(), m_readyForNextPkt.end(), [=](ReadyForNextPktPair x) { return x.first == dataFrame->tgtHWAddr; }); + if (ready != m_readyForNextPkt.end()) { + if (!ready->second) { + processed = false; + goto pkt_clock_abort; + } + } else { + processed = false; + goto pkt_clock_abort; + } + + m_readyForNextPkt[dataFrame->tgtHWAddr] = false; + + std::string srcIp = __IP_FROM_UINT(dataFrame->srcProtoAddr); + std::string tgtIp = __IP_FROM_UINT(dataFrame->tgtProtoAddr); + + LogMessage(LOG_NET, "P25, VTUN -> PDU IP Data, srcIp = %s (%u), dstIp = %s (%u), pktLen = %u, proto = %02X", + srcIp.c_str(), dataFrame->srcHWAddr, tgtIp.c_str(), dataFrame->tgtHWAddr, dataFrame->pktLen, dataFrame->proto); + + // assemble a P25 PDU frame header for transport... + data::DataHeader rspHeader = data::DataHeader(); + rspHeader.setFormat(PDUFormatType::CONFIRMED); + rspHeader.setMFId(MFG_STANDARD); + rspHeader.setAckNeeded(true); + rspHeader.setOutbound(true); + rspHeader.setSAP(PDUSAP::EXT_ADDR); + rspHeader.setLLId(dataFrame->tgtHWAddr); + rspHeader.setBlocksToFollow(1U); - m_readyForPkt[dataFrame.tgtHWAddr] = false; - m_suNotReadyTimeout[dataFrame.tgtHWAddr] = Timer(1000U, 5U, 0U); - m_suNotReadyTimeout[dataFrame.tgtHWAddr].start(); - - // assemble a P25 PDU frame header for transport... - data::DataHeader rspHeader = data::DataHeader(); - rspHeader.setFormat(PDUFormatType::CONFIRMED); - rspHeader.setMFId(MFG_STANDARD); - rspHeader.setAckNeeded(true); - rspHeader.setOutbound(true); - rspHeader.setSAP(PDUSAP::EXT_ADDR); - rspHeader.setLLId(dataFrame.tgtHWAddr); - rspHeader.setBlocksToFollow(1U); - - rspHeader.setEXSAP(PDUSAP::PACKET_DATA); - rspHeader.setSrcLLId(WUID_FNE); - - rspHeader.calculateLength(dataFrame.pktLen); - uint32_t pduLength = rspHeader.getPDULength(); - - UInt8Array __pduUserData = std::make_unique(pduLength); - uint8_t* pduUserData = __pduUserData.get(); - ::memset(pduUserData, 0x00U, pduLength); - ::memcpy(pduUserData + 4U, dataFrame.buffer, dataFrame.pktLen); + rspHeader.setEXSAP(PDUSAP::PACKET_DATA); + rspHeader.setSrcLLId(WUID_FNE); + + rspHeader.calculateLength(dataFrame->pktLen); + uint32_t pduLength = rspHeader.getPDULength(); + + UInt8Array __pduUserData = std::make_unique(pduLength); + uint8_t* pduUserData = __pduUserData.get(); + ::memset(pduUserData, 0x00U, pduLength); + ::memcpy(pduUserData + 4U, dataFrame->buffer, dataFrame->pktLen); #if DEBUG_P25_PDU_DATA - Utils::dump(1U, "P25PacketData::clock() pduUserData", pduUserData, pduLength); + Utils::dump(1U, "P25PacketData::clock() pduUserData", pduUserData, pduLength); #endif - dispatchUserFrameToFNE(rspHeader, true, pduUserData); + dispatchUserFrameToFNE(rspHeader, true, pduUserData); + } + } - delete[] dataFrame.buffer; - m_dataFrames.pop_front(); +pkt_clock_abort: + m_vtunMutex.try_lock_for(std::chrono::milliseconds(60)); + m_dataFrames.pop_front(); + if (processed) { + if (dataFrame->buffer != nullptr) + delete[] dataFrame->buffer; + delete dataFrame; + } else { + // requeue packet + m_dataFrames.push_back(dataFrame); } + m_vtunMutex.unlock(); } // --------------------------------------------------------------------------- @@ -449,19 +491,21 @@ void P25PacketData::dispatch(uint32_t peerId) } } - if (m_network->m_dumpDataPacket && status->dataBlockCnt > 0U) { - Utils::dump(1U, "PDU Packet", status->pduUserData, status->pduUserDataLength); + if (m_network->m_dumpPacketData && status->dataBlockCnt > 0U) { + Utils::dump(1U, "ISP PDU Packet", status->pduUserData, status->pduUserDataLength); } if (status->header.getFormat() == PDUFormatType::RSP) { LogMessage(LOG_NET, P25_PDU_STR ", ISP, response, fmt = $%02X, rspClass = $%02X, rspType = $%02X, rspStatus = $%02X, llId = %u, srcLlId = %u", status->header.getFormat(), status->header.getResponseClass(), status->header.getResponseType(), status->header.getResponseStatus(), status->header.getLLId(), status->header.getSrcLLId()); -/* + if (status->header.getResponseClass() == PDUAckClass::ACK && status->header.getResponseType() == PDUAckType::ACK) { - m_readyForPkt[status->header.getSrcLLId()] = true; + m_readyForNextPkt[status->header.getSrcLLId()] = true; } -*/ + + write_PDU_Ack_Response(status->header.getResponseClass(), status->header.getResponseType(), status->header.getResponseStatus(), + status->header.getLLId(), status->header.getSrcLLId()); return; } @@ -507,9 +551,17 @@ void P25PacketData::dispatch(uint32_t peerId) LogWarning(LOG_NET, P25_PDU_STR ", ARP reply, %u is trying to masquerade as us...", srcHWAddr); } else { m_arpTable[srcHWAddr] = srcProtoAddr; - } - m_readyForPkt[srcHWAddr] = true; + // is the SU ready for the next packet? + auto ready = std::find_if(m_readyForNextPkt.begin(), m_readyForNextPkt.end(), [=](ReadyForNextPktPair x) { return x.first == srcHWAddr; }); + if (ready != m_readyForNextPkt.end()) { + if (!ready->second) { + m_readyForNextPkt[srcHWAddr] = true; + } + } else { + m_readyForNextPkt[srcHWAddr] = true; + } + } } #else break; @@ -540,7 +592,45 @@ void P25PacketData::dispatch(uint32_t peerId) uint8_t proto = ipHeader->ip_p; uint16_t pktLen = Utils::reverseEndian(ipHeader->ip_len); // bryanb: this could be problematic on different endianness - LogMessage(LOG_NET, "P25, PDU -> VTUN, IP Data, srcIp = %s, dstIp = %s, pktLen = %u, proto = %02X", srcIp, dstIp, pktLen, proto); + // reflect broadcast messages back to the CAI network + bool handled = false; + if (status->header.getLLId() == WUID_ALL) { + LogMessage(LOG_NET, "P25, PDU -> VTUN, IP Data, repeated to CAI, broadcast packet, dstIp = %s (%u)", + dstIp, status->header.getLLId()); + + dispatchUserFrameToFNE(status->header, status->extendedAddress, status->pduUserData); + handled = true; + + // is the source SU one we have proper ARP entries for? + auto arpEntry = std::find_if(m_arpTable.begin(), m_arpTable.end(), [=](ArpTablePair x) { return x.first == status->header.getSrcLLId(); }); + if (arpEntry == m_arpTable.end()) { + uint32_t srcProtoAddr = Utils::reverseEndian(ipHeader->ip_src.s_addr); + LogMessage(LOG_NET, P25_PDU_STR ", adding ARP entry, %s is at %u", __IP_FROM_UINT(srcProtoAddr).c_str(), status->header.getSrcLLId()); + m_arpTable[status->header.getSrcLLId()] = Utils::reverseEndian(ipHeader->ip_src.s_addr); + } + } + + // is the target SU one we have proper ARP entries for? + auto arpEntry = std::find_if(m_arpTable.begin(), m_arpTable.end(), [=](ArpTablePair x) { return x.first == status->header.getLLId(); }); + if (arpEntry != m_arpTable.end()) { + LogMessage(LOG_NET, "P25, PDU -> VTUN, IP Data, repeated to CAI, destination IP has a CAI ARP table entry, dstIp = %s (%u)", + dstIp, status->header.getLLId()); + + dispatchUserFrameToFNE(status->header, status->extendedAddress, status->pduUserData); + handled = true; + + // is the source SU one we have proper ARP entries for? + auto arpEntry = std::find_if(m_arpTable.begin(), m_arpTable.end(), [=](ArpTablePair x) { return x.first == status->header.getSrcLLId(); }); + if (arpEntry == m_arpTable.end()) { + uint32_t srcProtoAddr = Utils::reverseEndian(ipHeader->ip_src.s_addr); + LogMessage(LOG_NET, P25_PDU_STR ", adding ARP entry, %s is at %u", __IP_FROM_UINT(srcProtoAddr).c_str(), status->header.getSrcLLId()); + m_arpTable[status->header.getSrcLLId()] = Utils::reverseEndian(ipHeader->ip_src.s_addr); + } + } + + // transmit packet to IP network + LogMessage(LOG_NET, "P25, PDU -> VTUN, IP Data, srcIp = %s (%u), dstIp = %s (%u), pktLen = %u, proto = %02X", + srcIp, status->header.getSrcLLId(), dstIp, status->header.getLLId(), pktLen, proto); UInt8Array __ipFrame = std::make_unique(pktLen); uint8_t* ipFrame = __ipFrame.get(); @@ -553,8 +643,10 @@ void P25PacketData::dispatch(uint32_t peerId) LogError(LOG_NET, P25_PDU_STR ", failed to write IP frame to virtual tunnel, len %u", pktLen); } - write_PDU_Ack_Response(PDUAckClass::ACK, PDUAckType::ACK, status->header.getNs(), (status->extendedAddress) ? status->header.getSrcLLId() : status->header.getLLId()); - m_readyForPkt[status->header.getSrcLLId()] = true; + // if the packet is unhandled and sent off to VTUN; ack the packet so the sender knows we received it + if (!handled) { + write_PDU_Ack_Response(PDUAckClass::ACK, PDUAckType::ACK, status->header.getNs(), status->header.getSrcLLId(), status->header.getLLId()); + } #endif // !defined(_WIN32) } break; @@ -849,11 +941,9 @@ void P25PacketData::write_PDU_Ack_Response(uint8_t ackClass, uint8_t ackType, ui rspHeader.setLLId(llId); if (srcLlId > 0U) { rspHeader.setSrcLLId(srcLlId); - rspHeader.setFullMessage(false); - } - else { - rspHeader.setFullMessage(true); } + rspHeader.setFullMessage(true); + rspHeader.setBlocksToFollow(0U); dispatchUserFrameToFNE(rspHeader, srcLlId > 0U, nullptr); @@ -921,7 +1011,7 @@ void P25PacketData::write_PDU_User(uint32_t peerId, network::PeerNetwork* peerNe edac::CRC::addCRC32(pduUserData, packetLength); } - if (m_network->m_dumpDataPacket) { + if (m_network->m_dumpPacketData) { Utils::dump("OSP PDU User Data", pduUserData, packetLength); } diff --git a/src/fne/network/callhandler/packetdata/P25PacketData.h b/src/fne/network/callhandler/packetdata/P25PacketData.h index 6a388e35..b5187c7e 100644 --- a/src/fne/network/callhandler/packetdata/P25PacketData.h +++ b/src/fne/network/callhandler/packetdata/P25PacketData.h @@ -90,17 +90,20 @@ namespace network */ class VTUNDataFrame { public: - uint32_t srcHWAddr; - uint32_t srcProtoAddr; - uint32_t tgtHWAddr; - uint32_t tgtProtoAddr; + uint32_t srcHWAddr; //! Source Hardware Address + uint32_t srcProtoAddr; //! Source Protocol Address + uint32_t tgtHWAddr; //! Target Hardware Address + uint32_t tgtProtoAddr; //! Target Protocol Address - uint8_t* buffer; - uint32_t bufferLen; + uint8_t* buffer; //! Raw data buffer + uint32_t bufferLen; //! Length of raw data buffer - uint16_t pktLen; + uint16_t pktLen; //! Packet Length + uint8_t proto; //! Packet Protocol + + uint64_t timestamp; //! Timestamp in milliseconds }; - std::deque m_dataFrames; + std::deque m_dataFrames; /** * @brief Represents the receive status of a call. @@ -161,13 +164,16 @@ namespace network typedef std::pair StatusMapPair; std::unordered_map m_status; + typedef std::pair ArpTablePair; std::unordered_map m_arpTable; - std::unordered_map m_readyForPkt; - std::unordered_map m_suNotReadyTimeout; + typedef std::pair ReadyForNextPktPair; + std::unordered_map m_readyForNextPkt; std::unordered_map m_suSendSeq; bool m_debug; + static std::timed_mutex m_vtunMutex; + /** * @brief Helper to dispatch PDU user data. * @param peerId Peer ID. diff --git a/src/host/p25/packet/Data.cpp b/src/host/p25/packet/Data.cpp index bea3c1f1..de8c6e00 100644 --- a/src/host/p25/packet/Data.cpp +++ b/src/host/p25/packet/Data.cpp @@ -381,7 +381,7 @@ bool Data::process(uint8_t* data, uint32_t len) LogMessage(LOG_RF, P25_PDU_STR ", ISP, response, OSP ACK RETRY, llId = %u, exceeded retries, undeliverable", m_rfDataHeader.getLLId()); - writeRF_PDU_Ack_Response(PDUAckClass::NACK, PDUAckType::NACK_UNDELIVERABLE, m_rfDataHeader.getNs(), m_rfDataHeader.getLLId()); + writeRF_PDU_Ack_Response(PDUAckClass::NACK, PDUAckType::NACK_UNDELIVERABLE, m_rfDataHeader.getNs(), m_rfDataHeader.getLLId(), m_rfDataHeader.getSrcLLId()); } } } @@ -390,11 +390,8 @@ bool Data::process(uint8_t* data, uint32_t len) // only repeat the PDU locally if the packet isn't for the FNE if (m_repeatPDU && m_rfDataHeader.getLLId() != WUID_FNE) { - if (m_verbose) { - LogMessage(LOG_RF, P25_PDU_STR ", repeating ACK PDU, llId = %u, srcLlId = %u", m_rfDataHeader.getLLId(), m_rfDataHeader.getSrcLLId()); - } - - writeRF_PDU_Buffered(); // re-generate buffered PDU and send it on + writeRF_PDU_Ack_Response(m_rfDataHeader.getResponseClass(), m_rfDataHeader.getResponseType(), m_rfDataHeader.getResponseStatus(), + m_rfDataHeader.getLLId(), m_rfDataHeader.getSrcLLId()); } } else { @@ -497,9 +494,6 @@ bool Data::process(uint8_t* data, uint32_t len) bool Data::processNetwork(uint8_t* data, uint32_t len, uint32_t blockLength) { - if (m_p25->m_rfState != RS_RF_LISTENING && m_p25->m_netState == RS_NET_IDLE) - return false; - if (m_p25->m_netState != RS_NET_DATA) { m_netDataHeader.reset(); m_netDataOffset = 0U; @@ -601,13 +595,8 @@ bool Data::processNetwork(uint8_t* data, uint32_t len, uint32_t blockLength) } } - if (m_repeatPDU) { - if (m_verbose) { - LogMessage(LOG_NET, P25_PDU_STR ", repeating ACK PDU, llId = %u, srcLlId = %u", m_netDataHeader.getLLId(), m_netDataHeader.getSrcLLId()); - } - - writeNet_PDU_Buffered(); // re-generate buffered PDU and send it on - } + writeRF_PDU_Ack_Response(m_netDataHeader.getResponseClass(), m_netDataHeader.getResponseType(), m_netDataHeader.getResponseStatus(), + m_netDataHeader.getLLId(), m_netDataHeader.getSrcLLId()); m_netDataHeader.reset(); m_netExtendedAddress = false; @@ -1439,7 +1428,7 @@ void Data::writeRF_PDU(const uint8_t* pdu, uint32_t bitLength, bool noNulls, boo // Add status bits P25Utils::addStatusBits(data + 2U, newBitLength, false); - P25Utils::addIdleStatusBits(data + 2U, newBitLength); + P25Utils::addTrunkSlotStatusBits(data + 2U, newBitLength); // Set first busy bits to 1,1 P25Utils::setStatusBits(data + 2U, P25_SS0_START, true, true); @@ -1697,13 +1686,11 @@ void Data::writeRF_PDU_Ack_Response(uint8_t ackClass, uint8_t ackType, uint8_t a rspHeader.setResponseType(ackType); rspHeader.setResponseStatus(ackStatus); rspHeader.setLLId(llId); - if (m_rfDataHeader.getSAP() == PDUSAP::EXT_ADDR) { + if (srcLlId > 0U) { rspHeader.setSrcLLId(srcLlId); - rspHeader.setFullMessage(false); - } - else { - rspHeader.setFullMessage(true); } + rspHeader.setFullMessage(true); + rspHeader.setBlocksToFollow(0U); // Generate the PDU header and 1/2 rate Trellis @@ -1711,8 +1698,8 @@ void Data::writeRF_PDU_Ack_Response(uint8_t ackClass, uint8_t ackType, uint8_t a Utils::setBitRange(block, data, offset, P25_PDU_FEC_LENGTH_BITS); if (m_verbose) { - LogMessage(LOG_RF, P25_PDU_STR ", OSP, response, ackClass = $%02X, ackType = $%02X, llId = %u, srcLLId = %u", - rspHeader.getResponseClass(), rspHeader.getResponseType(), rspHeader.getLLId(), rspHeader.getSrcLLId()); + LogMessage(LOG_RF, P25_PDU_STR ", OSP, response, rspClass = $%02X, rspType = $%02X, rspStatus = $%02X, llId = %u, srcLLId = %u", + rspHeader.getResponseClass(), rspHeader.getResponseType(), rspHeader.getResponseStatus(), rspHeader.getLLId(), rspHeader.getSrcLLId()); } writeRF_PDU(data, bitLength, noNulls); From 116953b62ea14a6e94a3f0bc8bd1b618f276cf4d Mon Sep 17 00:00:00 2001 From: Bryan Biedenkapp Date: Mon, 4 Nov 2024 16:21:00 -0500 Subject: [PATCH 18/23] fix thread names; --- src/fne/HostFNE.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/fne/HostFNE.cpp b/src/fne/HostFNE.cpp index 16d55d07..266238eb 100644 --- a/src/fne/HostFNE.cpp +++ b/src/fne/HostFNE.cpp @@ -604,7 +604,7 @@ void* HostFNE::threadMasterNetwork(void* arg) ::pthread_detach(th->thread); #endif // defined(_WIN32) - std::string threadName("fne:network-loop"); + std::string threadName("fne:net"); HostFNE* fne = static_cast(th->obj); if (fne == nullptr) { g_killed = true; @@ -647,7 +647,7 @@ void* HostFNE::threadDiagNetwork(void* arg) ::pthread_detach(th->thread); #endif // defined(_WIN32) - std::string threadName("fne:diag-network-loop"); + std::string threadName("fne:diag-net"); HostFNE* fne = static_cast(th->obj); if (fne == nullptr) { g_killed = true; @@ -838,7 +838,7 @@ void* HostFNE::threadVirtualNetworking(void* arg) if (th != nullptr) { ::pthread_detach(th->thread); - std::string threadName("fne:vtun-net-rx"); + std::string threadName("fne:vt-net-rx"); HostFNE* fne = static_cast(th->obj); if (fne == nullptr) { g_killed = true; @@ -902,7 +902,7 @@ void* HostFNE::threadVirtualNetworkingClock(void* arg) if (th != nullptr) { ::pthread_detach(th->thread); - std::string threadName("fne:vtun-clock"); + std::string threadName("fne:vt-clock"); HostFNE* fne = static_cast(th->obj); if (fne == nullptr) { g_killed = true; From a8b451b5ee2c750191163c3716e3669efee035f0 Mon Sep 17 00:00:00 2001 From: Bryan Biedenkapp Date: Tue, 5 Nov 2024 16:20:35 -0500 Subject: [PATCH 19/23] add count of data for affiliations and peer list; --- src/sysview/AffListWnd.h | 7 +++++++ src/sysview/PeerListWnd.h | 7 +++++++ 2 files changed, 14 insertions(+) diff --git a/src/sysview/AffListWnd.h b/src/sysview/AffListWnd.h index 6ec5137b..93a4a580 100644 --- a/src/sysview/AffListWnd.h +++ b/src/sysview/AffListWnd.h @@ -110,6 +110,7 @@ public: m_listView.clear(); json::array fneAffils = rsp["affiliations"].get(); + uint32_t cnt = 0U; for (auto entry : fneAffils) { json::object peerAffils = entry.get(); uint32_t peerId = peerAffils["peerId"].getDefault(0U); @@ -140,8 +141,14 @@ public: const finalcut::FStringList line(columns.cbegin(), columns.cend()); m_listView.insert(line); + + cnt++; } } + + std::ostringstream wndTitle; + wndTitle << "Affiliations View" << " [" << cnt << "] (10s)"; + FDialog::setText(wndTitle.str()); } catch (std::exception& e) { ::LogWarning(LOG_HOST, "[AFFVIEW] %s:%u, failed to properly handle affiliation request, %s", fneRESTAddress.c_str(), fneRESTPort, e.what()); diff --git a/src/sysview/PeerListWnd.h b/src/sysview/PeerListWnd.h index 2f0f51aa..cf626e01 100644 --- a/src/sysview/PeerListWnd.h +++ b/src/sysview/PeerListWnd.h @@ -110,6 +110,7 @@ public: m_listView.clear(); json::array fnePeers = rsp["peers"].get(); + uint32_t cnt = 0U; for (auto entry : fnePeers) { json::object peerObj = entry.get(); uint32_t peerId = peerObj["peerId"].getDefault(0U); @@ -214,7 +215,13 @@ public: const finalcut::FStringList line(columns.cbegin(), columns.cend()); m_listView.insert(line); + + cnt++; } + + std::ostringstream wndTitle; + wndTitle << "Peers View" << " [" << cnt << "] (10s)"; + FDialog::setText(wndTitle.str()); } catch (std::exception& e) { ::LogWarning(LOG_HOST, "[AFFVIEW] %s:%u, failed to properly handle peer query request, %s", fneRESTAddress.c_str(), fneRESTPort, e.what()); From 05fa1c21146e91c9f9dc130afff221a0e3c30ff9 Mon Sep 17 00:00:00 2001 From: Bryan Biedenkapp Date: Tue, 5 Nov 2024 21:09:39 -0500 Subject: [PATCH 20/23] increase SSL timeout delay from 2 to 5 seconds; --- src/common/network/tcp/SecureTcpClient.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/common/network/tcp/SecureTcpClient.h b/src/common/network/tcp/SecureTcpClient.h index 27dfff71..8c7c5a14 100644 --- a/src/common/network/tcp/SecureTcpClient.h +++ b/src/common/network/tcp/SecureTcpClient.h @@ -80,7 +80,7 @@ namespace network int status = -1; struct timeval tv, tvRestore; - tv.tv_sec = 2; + tv.tv_sec = 5; tv.tv_usec = 0; tvRestore = tv; From 38492cf709be0befe800c9fd9dbac83c4b34a002 Mon Sep 17 00:00:00 2001 From: Bryan Biedenkapp Date: Thu, 7 Nov 2024 08:41:01 -0500 Subject: [PATCH 21/23] resolve peer ID to identity name from available name map; --- src/sysview/network/PeerNetwork.cpp | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/src/sysview/network/PeerNetwork.cpp b/src/sysview/network/PeerNetwork.cpp index ddc7fdf1..80c0152d 100644 --- a/src/sysview/network/PeerNetwork.cpp +++ b/src/sysview/network/PeerNetwork.cpp @@ -66,7 +66,13 @@ void PeerNetwork::userPacketHandler(uint32_t peerId, FrameQueue::OpcodePair opco bool currState = g_disableTimeDisplay; g_disableTimeDisplay = true; - ::Log(9999U, nullptr, "%.9u %s", peerId, payload.c_str()); + + std::string identity = std::string(); + auto it = std::find_if(g_peerIdentityNameMap.begin(), g_peerIdentityNameMap.end(), [&](PeerIdentityMapPair x) { return x.first == peerId; }); + if (it != g_peerIdentityNameMap.end()) + identity = g_peerIdentityNameMap[peerId]; + + ::Log(9999U, nullptr, "%.9u (%8s) %s", peerId, identity.c_str(), payload.c_str()); g_disableTimeDisplay = currState; } break; From 50bdb43db2dc106407bb53e2b8de392db8be6145 Mon Sep 17 00:00:00 2001 From: Bryan Biedenkapp Date: Thu, 7 Nov 2024 10:13:55 -0500 Subject: [PATCH 22/23] add Peer-Link support to SysView, so long as the FNE is appropriately configured, this will allow SysView to operate without TGID or RID configuration files; --- src/fne/network/PeerNetwork.cpp | 6 +- src/sysview/HostWS.cpp | 165 ++++++++++++++++ src/sysview/network/PeerNetwork.cpp | 287 +++++++++++++++++++++++++++- src/sysview/network/PeerNetwork.h | 16 ++ 4 files changed, 470 insertions(+), 4 deletions(-) diff --git a/src/fne/network/PeerNetwork.cpp b/src/fne/network/PeerNetwork.cpp index e8af9f02..b66446ff 100644 --- a/src/fne/network/PeerNetwork.cpp +++ b/src/fne/network/PeerNetwork.cpp @@ -231,7 +231,7 @@ void PeerNetwork::userPacketHandler(uint32_t peerId, FrameQueue::OpcodePair opco } } -tid_lookup_cleanup: + tid_lookup_cleanup: m_tgidSize = 0U; m_tgidCompressedSize = 0U; if (m_tgidBuffer != nullptr) @@ -363,7 +363,7 @@ tid_lookup_cleanup: } } -rid_lookup_cleanup: + rid_lookup_cleanup: m_ridSize = 0U; m_ridCompressedSize = 0U; if (m_ridBuffer != nullptr) @@ -495,7 +495,7 @@ rid_lookup_cleanup: } } -pid_lookup_cleanup: + pid_lookup_cleanup: m_pidSize = 0U; m_pidCompressedSize = 0U; if (m_pidBuffer != nullptr) diff --git a/src/sysview/HostWS.cpp b/src/sysview/HostWS.cpp index 8295dca3..fe60e38e 100644 --- a/src/sysview/HostWS.cpp +++ b/src/sysview/HostWS.cpp @@ -9,15 +9,19 @@ */ #if !defined(NO_WEBSOCKETS) #include "Defines.h" +#include "common/lookups/TalkgroupRulesLookup.h" #include "common/Log.h" #include "common/StopWatch.h" #include "common/Thread.h" #include "common/Utils.h" #include "fne/network/RESTDefines.h" #include "remote/RESTClient.h" +#include "network/PeerNetwork.h" #include "HostWS.h" #include "SysViewMain.h" +using namespace lookups; + #include #include @@ -27,6 +31,110 @@ #define IDLE_WARMUP_MS 5U + +// --------------------------------------------------------------------------- +// Global Functions +// --------------------------------------------------------------------------- + +/** + * @brief Helper to convert a TalkgroupRuleGroupVoice to JSON. + * @param groupVoice Instance of TalkgroupRuleGroupVoice to convert to JSON. + * @returns json::object JSON object. + */ +json::object tgToJson(const TalkgroupRuleGroupVoice& groupVoice) +{ + json::object tg = json::object(); + + std::string tgName = groupVoice.name(); + tg["name"].set(tgName); + std::string tgAlias = groupVoice.nameAlias(); + tg["alias"].set(tgAlias); + bool invalid = groupVoice.isInvalid(); + tg["invalid"].set(invalid); + + // source stanza + { + json::object source = json::object(); + uint32_t tgId = groupVoice.source().tgId(); + source["tgid"].set(tgId); + uint8_t tgSlot = groupVoice.source().tgSlot(); + source["slot"].set(tgSlot); + tg["source"].set(source); + } + + // config stanza + { + json::object config = json::object(); + bool active = groupVoice.config().active(); + config["active"].set(active); + bool affiliated = groupVoice.config().affiliated(); + config["affiliated"].set(affiliated); + bool parrot = groupVoice.config().parrot(); + config["parrot"].set(parrot); + + json::array inclusions = json::array(); + std::vector inclusion = groupVoice.config().inclusion(); + if (inclusion.size() > 0) { + for (auto inclEntry : inclusion) { + uint32_t peerId = inclEntry; + inclusions.push_back(json::value((double)peerId)); + } + } + config["inclusion"].set(inclusions); + + json::array exclusions = json::array(); + std::vector exclusion = groupVoice.config().exclusion(); + if (exclusion.size() > 0) { + for (auto exclEntry : exclusion) { + uint32_t peerId = exclEntry; + exclusions.push_back(json::value((double)peerId)); + } + } + config["exclusion"].set(exclusions); + + json::array rewrites = json::array(); + std::vector rewrite = groupVoice.config().rewrite(); + if (rewrite.size() > 0) { + for (auto rewrEntry : rewrite) { + json::object rewrite = json::object(); + uint32_t peerId = rewrEntry.peerId(); + rewrite["peerid"].set(peerId); + uint32_t tgId = rewrEntry.tgId(); + rewrite["tgid"].set(tgId); + uint8_t tgSlot = rewrEntry.tgSlot(); + rewrite["slot"].set(tgSlot); + + rewrites.push_back(json::value(rewrite)); + } + } + config["rewrite"].set(rewrites); + + json::array always = json::array(); + std::vector alwaysSend = groupVoice.config().alwaysSend(); + if (alwaysSend.size() > 0) { + for (auto alwaysEntry : alwaysSend) { + uint32_t peerId = alwaysEntry; + always.push_back(json::value((double)peerId)); + } + } + config["always"].set(always); + + json::array preferreds = json::array(); + std::vector preferred = groupVoice.config().preferred(); + if (preferred.size() > 0) { + for (auto prefEntry : preferred) { + uint32_t peerId = prefEntry; + preferreds.push_back(json::value((double)peerId)); + } + } + config["preferred"].set(preferreds); + + tg["config"].set(config); + } + + return tg; +} + // --------------------------------------------------------------------------- // Public Class Members // --------------------------------------------------------------------------- @@ -161,6 +269,11 @@ int HostWS::run() Timer peerStatusUpdate(1000U, 0U, 175U); peerStatusUpdate.start(); + Timer tgDataUpdate(1000U, 30U); + tgDataUpdate.start(); + Timer ridDataUpdate(1000U, 30U); + ridDataUpdate.start(); + setNetDataEventCallback([=](json::object obj) { netDataEvent(obj); }); // main execution loop @@ -257,6 +370,58 @@ int HostWS::run() } } } + + // send full talkgroup list data + tgDataUpdate.clock(ms); + if (tgDataUpdate.isRunning() && tgDataUpdate.hasExpired()) { + tgDataUpdate.start(); + + json::object wsObj = json::object(); + std::string type = "tg_data"; + + json::array tgs = json::array(); + if (g_tidLookup != nullptr) { + if (g_tidLookup->groupVoice().size() > 0) { + for (auto entry : g_tidLookup->groupVoice()) { + json::object tg = tgToJson(entry); + tgs.push_back(json::value(tg)); + } + } + } + + wsObj["payload"].set(tgs); + send(wsObj); + } + + // send full radio ID list data + ridDataUpdate.clock(ms); + if (ridDataUpdate.isRunning() && ridDataUpdate.hasExpired()) { + ridDataUpdate.start(); + + json::object wsObj = json::object(); + std::string type = "rid_data"; + + json::array rids = json::array(); + if (g_ridLookup != nullptr) { + if (g_ridLookup->table().size() > 0) { + for (auto entry : g_ridLookup->table()) { + json::object ridObj = json::object(); + + uint32_t rid = entry.first; + ridObj["id"].set(rid); + bool enabled = entry.second.radioEnabled(); + ridObj["enabled"].set(enabled); + std::string alias = entry.second.radioAlias(); + ridObj["alias"].set(alias); + + rids.push_back(json::value(ridObj)); + } + } + } + + wsObj["payload"].set(rids); + send(wsObj); + } } else { // clear ostream logOutput.str(""); diff --git a/src/sysview/network/PeerNetwork.cpp b/src/sysview/network/PeerNetwork.cpp index 80c0152d..f3128011 100644 --- a/src/sysview/network/PeerNetwork.cpp +++ b/src/sysview/network/PeerNetwork.cpp @@ -11,6 +11,7 @@ #include "common/network/json/json.h" #include "common/p25/dfsi/DFSIDefines.h" #include "common/p25/dfsi/LC.h" +#include "common/zlib/zlib.h" #include "common/Utils.h" #include "network/PeerNetwork.h" #include "SysViewMain.h" @@ -18,6 +19,8 @@ using namespace network; #include +#include +#include // --------------------------------------------------------------------------- // Static Class Members @@ -34,7 +37,14 @@ std::mutex PeerNetwork::m_peerStatusMutex; PeerNetwork::PeerNetwork(const std::string& address, uint16_t port, uint16_t localPort, uint32_t peerId, const std::string& password, bool duplex, bool debug, bool dmr, bool p25, bool nxdn, bool slot1, bool slot2, bool allowActivityTransfer, bool allowDiagnosticTransfer, bool updateLookup, bool saveLookup) : Network(address, port, localPort, peerId, password, duplex, debug, dmr, p25, nxdn, slot1, slot2, allowActivityTransfer, allowDiagnosticTransfer, updateLookup, saveLookup), - peerStatus() + peerStatus(), + m_peerLink(false), + m_tgidCompressedSize(0U), + m_tgidSize(0U), + m_tgidBuffer(nullptr), + m_ridCompressedSize(0U), + m_ridSize(0U), + m_ridBuffer(nullptr) { assert(!address.empty()); assert(port > 0U); @@ -112,6 +122,279 @@ void PeerNetwork::userPacketHandler(uint32_t peerId, FrameQueue::OpcodePair opco } break; + case NET_FUNC::PEER_LINK: + { + switch (opcode.second) { + case NET_SUBFUNC::PL_TALKGROUP_LIST: + { + uint8_t curBlock = data[8U]; + uint8_t blockCnt = data[9U]; + + // if this is the first block store sizes and initialize temp buffer + if (curBlock == 0U) { + m_tgidSize = __GET_UINT32(data, 0U); + m_tgidCompressedSize = __GET_UINT32(data, 4U); + + if (m_tgidBuffer != nullptr) + delete[] m_tgidBuffer; + if (m_tgidSize < PEER_LINK_BLOCK_SIZE) + m_tgidBuffer = new uint8_t[PEER_LINK_BLOCK_SIZE + 1U]; + else + m_tgidBuffer = new uint8_t[m_tgidSize + 1U]; + } + + if (m_tgidBuffer != nullptr) { + if (curBlock < blockCnt) { + uint32_t offs = curBlock * PEER_LINK_BLOCK_SIZE; + ::memcpy(m_tgidBuffer + offs, data + 10U, PEER_LINK_BLOCK_SIZE); + // Utils::dump(1U, "Block Payload", data, 10U + PEER_LINK_BLOCK_SIZE); + } else { + uint32_t offs = curBlock * PEER_LINK_BLOCK_SIZE; + ::memcpy(m_tgidBuffer + offs, data + 10U, PEER_LINK_BLOCK_SIZE); + + // Utils::dump(1U, "Block Payload", data, 10U + PEER_LINK_BLOCK_SIZE); + // Utils::dump(1U, "Compressed Payload", m_tgidBuffer, m_tgidCompressedSize); + + // handle last block + // compression structures + z_stream strm; + strm.zalloc = Z_NULL; + strm.zfree = Z_NULL; + strm.opaque = Z_NULL; + + // set input data + strm.avail_in = m_tgidCompressedSize; + strm.next_in = m_tgidBuffer; + + // initialize decompression + int ret = inflateInit(&strm); + if (ret != Z_OK) { + LogError(LOG_NET, "PEER %u error initializing ZLIB", peerId); + + m_tgidSize = 0U; + m_tgidCompressedSize = 0U; + if (m_tgidBuffer != nullptr) + delete[] m_tgidBuffer; + m_tgidBuffer = nullptr; + break; + } + + // decompress data + std::vector decompressedData; + uint8_t outbuffer[1024]; + do { + strm.avail_out = sizeof(outbuffer); + strm.next_out = outbuffer; + + ret = inflate(&strm, Z_NO_FLUSH); + if (ret == Z_STREAM_ERROR) { + LogError(LOG_NET, "PEER %u error decompressing TGID list", peerId); + inflateEnd(&strm); + goto tid_lookup_cleanup; // yes - I hate myself; but this is quick + } + + decompressedData.insert(decompressedData.end(), outbuffer, outbuffer + sizeof(outbuffer) - strm.avail_out); + } while (ret != Z_STREAM_END); + + // cleanup + inflateEnd(&strm); + + // scope is intentional + { + uint32_t decompressedLen = strm.total_out; + uint8_t* decompressed = decompressedData.data(); + + // Utils::dump(1U, "Raw TGID Data", decompressed, decompressedLen); + + // check that we got the appropriate data + if (decompressedLen == m_tgidSize) { + // store to file + std::unique_ptr __str = std::make_unique(decompressedLen + 1U); + char* str = __str.get(); + ::memcpy(str, decompressed, decompressedLen); + str[decompressedLen] = 0; // null termination + + // randomize filename + std::ostringstream s; + std::random_device rd; + std::mt19937 mt(rd()); + std::uniform_int_distribution dist(0x00U, 0xFFFFFFFFU); + s << "/tmp/talkgroup_rules.yml." << dist(mt); + + std::string filename = s.str(); + std::ofstream file(filename, std::ofstream::out); + if (file.fail()) { + LogError(LOG_NET, "Cannot open the talkgroup ID lookup file - %s", filename.c_str()); + goto tid_lookup_cleanup; // yes - I hate myself; but this is quick + } + + file << str; + file.close(); + + m_tidLookup->stop(true); + m_tidLookup->filename(filename); + m_tidLookup->reload(); + + // flag this peer as Peer-Link enabled + m_peerLink = true; + + // cleanup temporary file + ::remove(filename.c_str()); + } + else { + LogError(LOG_NET, "PEER %u error decompressed TGID list, was not of expected size! %u != %u", peerId, decompressedLen, m_tgidSize); + } + } + + tid_lookup_cleanup: + m_tgidSize = 0U; + m_tgidCompressedSize = 0U; + if (m_tgidBuffer != nullptr) + delete[] m_tgidBuffer; + m_tgidBuffer = nullptr; + } + } + } + break; + + case NET_SUBFUNC::PL_RID_LIST: + { + uint8_t curBlock = data[8U]; + uint8_t blockCnt = data[9U]; + + // if this is the first block store sizes and initialize temp buffer + if (curBlock == 0U) { + m_ridSize = __GET_UINT32(data, 0U); + m_ridCompressedSize = __GET_UINT32(data, 4U); + + if (m_ridBuffer != nullptr) + delete[] m_ridBuffer; + if (m_ridSize < PEER_LINK_BLOCK_SIZE) + m_ridBuffer = new uint8_t[PEER_LINK_BLOCK_SIZE + 1U]; + else + m_ridBuffer = new uint8_t[m_ridSize + 1U]; + } + + if (m_ridBuffer != nullptr) { + if (curBlock < blockCnt) { + uint32_t offs = curBlock * PEER_LINK_BLOCK_SIZE; + ::memcpy(m_ridBuffer + offs, data + 10U, PEER_LINK_BLOCK_SIZE); + // Utils::dump(1U, "Block Payload", data, 10U + PEER_LINK_BLOCK_SIZE); + } else { + uint32_t offs = curBlock * PEER_LINK_BLOCK_SIZE; + ::memcpy(m_ridBuffer + offs, data + 10U, PEER_LINK_BLOCK_SIZE); + + // Utils::dump(1U, "Block Payload", data, 10U + PEER_LINK_BLOCK_SIZE); + // Utils::dump(1U, "Compressed Payload", m_ridBuffer, m_ridCompressedSize); + + // handle last block + // compression structures + z_stream strm; + strm.zalloc = Z_NULL; + strm.zfree = Z_NULL; + strm.opaque = Z_NULL; + + // set input data + strm.avail_in = m_ridCompressedSize; + strm.next_in = m_ridBuffer; + + // initialize decompression + int ret = inflateInit(&strm); + if (ret != Z_OK) { + LogError(LOG_NET, "PEER %u error initializing ZLIB", peerId); + + m_ridSize = 0U; + m_ridCompressedSize = 0U; + if (m_ridBuffer != nullptr) + delete[] m_ridBuffer; + m_ridBuffer = nullptr; + break; + } + + // decompress data + std::vector decompressedData; + uint8_t outbuffer[1024]; + do { + strm.avail_out = sizeof(outbuffer); + strm.next_out = outbuffer; + + ret = inflate(&strm, Z_NO_FLUSH); + if (ret == Z_STREAM_ERROR) { + LogError(LOG_NET, "PEER %u error decompressing RID list", peerId); + inflateEnd(&strm); + goto rid_lookup_cleanup; // yes - I hate myself; but this is quick + } + + decompressedData.insert(decompressedData.end(), outbuffer, outbuffer + sizeof(outbuffer) - strm.avail_out); + } while (ret != Z_STREAM_END); + + // cleanup + inflateEnd(&strm); + + // scope is intentional + { + uint32_t decompressedLen = strm.total_out; + uint8_t* decompressed = decompressedData.data(); + + // Utils::dump(1U, "Raw RID Data", decompressed, decompressedLen); + + // check that we got the appropriate data + if (decompressedLen == m_ridSize) { + // store to file + std::unique_ptr __str = std::make_unique(decompressedLen + 1U); + char* str = __str.get(); + ::memcpy(str, decompressed, decompressedLen); + str[decompressedLen] = 0; // null termination + + // randomize filename + std::ostringstream s; + std::random_device rd; + std::mt19937 mt(rd()); + std::uniform_int_distribution dist(0x00U, 0xFFFFFFFFU); + s << "/tmp/rid_acl.dat." << dist(mt); + + std::string filename = s.str(); + std::ofstream file(filename, std::ofstream::out); + if (file.fail()) { + LogError(LOG_NET, "Cannot open the radio ID lookup file - %s", filename.c_str()); + goto rid_lookup_cleanup; // yes - I hate myself; but this is quick + } + + file << str; + file.close(); + + m_ridLookup->stop(true); + m_ridLookup->filename(filename); + m_ridLookup->reload(); + + // flag this peer as Peer-Link enabled + m_peerLink = true; + + // cleanup temporary file + ::remove(filename.c_str()); + } + else { + LogError(LOG_NET, "PEER %u error decompressed RID list, was not of expected size! %u != %u", peerId, decompressedLen, m_ridSize); + } + } + + rid_lookup_cleanup: + m_ridSize = 0U; + m_ridCompressedSize = 0U; + if (m_ridBuffer != nullptr) + delete[] m_ridBuffer; + m_ridBuffer = nullptr; + } + } + } + break; + + default: + break; + } + } + break; + default: Utils::dump("unknown opcode from the master", data, length); break; @@ -160,6 +443,8 @@ bool PeerNetwork::writeConfig() rcon["port"].set(m_restApiPort); // REST API Port config["rcon"].set(rcon); + bool external = true; + config["externalPeer"].set(external); // External Peer Marker bool convPeer = true; config["conventionalPeer"].set(convPeer); // Conventional Peer Marker bool sysView = true; diff --git a/src/sysview/network/PeerNetwork.h b/src/sysview/network/PeerNetwork.h index 1321fcce..c02efdce 100644 --- a/src/sysview/network/PeerNetwork.h +++ b/src/sysview/network/PeerNetwork.h @@ -57,6 +57,11 @@ namespace network PeerNetwork(const std::string& address, uint16_t port, uint16_t localPort, uint32_t peerId, const std::string& password, bool duplex, bool debug, bool dmr, bool p25, bool nxdn, bool slot1, bool slot2, bool allowActivityTransfer, bool allowDiagnosticTransfer, bool updateLookup, bool saveLookup); + /** + * @brief Flag indicating whether or not SysView has received Peer-Link data transfers. + */ + bool hasPeerLink() const { return m_peerLink; } + /** * @brief Helper to lock the peer status mutex. */ @@ -91,6 +96,17 @@ namespace network private: static std::mutex m_peerStatusMutex; + bool m_peerLink; + + uint32_t m_tgidCompressedSize; + uint32_t m_tgidSize; + + uint8_t* m_tgidBuffer; + + uint32_t m_ridCompressedSize; + uint32_t m_ridSize; + + uint8_t* m_ridBuffer; }; } // namespace network From dc739c9b720590c14bed45118afee1292dfa556d Mon Sep 17 00:00:00 2001 From: Bryan Biedenkapp Date: Thu, 7 Nov 2024 10:17:40 -0500 Subject: [PATCH 23/23] report RID 0 in SysView as EXTERNAL/PATCH; --- src/sysview/SysViewMain.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/sysview/SysViewMain.cpp b/src/sysview/SysViewMain.cpp index 206713e9..30b57362 100644 --- a/src/sysview/SysViewMain.cpp +++ b/src/sysview/SysViewMain.cpp @@ -153,6 +153,9 @@ std::string resolveRID(uint32_t id) return std::string("SYS/FNE"); case P25DEF::WUID_ALL: return std::string("ALL CALL"); + + case 0: + return std::string("EXTERNAL/PATCH"); } auto entry = g_ridLookup->find(id);